序文
.net运行库通过垃圾回收器自动处理回收托管资源,非托管的资源需要手动编码处理。理解内存管理的工作原理,有助于提高应用程序的速度和性能。废话少说,切入正题。主要阐述的概念见下图:

コンセプト
記憶: とも呼ばれます仮想メモリ、または仮想アドレス空間、Windowsはvirtualを使用しますアドレッシング システム は、使用可能なメモリ アドレスをバックグラウンドでハードウェア メモリの実際のアドレスに自動的にマップします。その結果、32 ビット プロセッサ上の各プロセスは、実行可能コード (. exe ファイル)、コードによってロードされたすべての DLL、およびプログラムの実行時に使用されるすべての変数の内容。
メモリスタック
プロセスの仮想メモリには、変数の有効期間をネストする必要がある領域があります。
メモリ ヒープ
プロセスの仮想メモリ内で、メソッドが終了した後もデータが引き続き使用できる領域。
マネージド リソース
ガベージ コレクターがバックグラウンドで自動的に処理できるリソース
アンマネージ リソース
デストラクター、Finalize、IDisposable、Using などのメカニズムまたはメソッドを通じて手動でコーディングおよび処理する必要があるリソース
メモリスタック
値型データはメモリスタックに格納され、参照型のインスタンスアドレス値もメモリスタックに配置されます(メモリスタックの動作原理の説明を参照)。 stack は、次のコード部分を通じて理解できます:
{ //block1开始 int a; //solve something {//block2开始 int b; // solve something else }//block2结束}//block1结束
上記のコードの 2 つの点に注意してください:
1) C# の変数のスコープは、最初に宣言された変数はスコープ外になり、後で宣言された変数はスコープ外になるという原則に従います。スコープが最初です。つまり、b が最初に解放され、a が後に解放されます。解放順序は常に同じです。逆の順序でメモリが割り当てられます。
2) b は別のブロックスコープ (block2) にあり、a が配置されているブロック名は block1 で、その中に block2 がネストされています。
下の図を見てください:

スタックメモリ管理では、スタックポインタが常に維持され、常にステーションエリアで次に利用可能なという名前のアドレスを指します。 sp は、図に示すように、1000 番のアドレスを指しているとします。
変数 a が最初にスタックにプッシュされると、int 型は 4 バイト、つまり 997 ~ 1000 を占有します。スタックにプッシュされた後、sp は 996 を指します。メモリスタックの成長方向は高アドレスから低アドレス方向であることがわかります。
次に、b がスタックにプッシュされ、993 ~ 996 を占め、sp は 992 を指します。ブロック block2 を超えると、変数 b は直ちにメモリ スタック上のストレージを解放し、sp は 4 バイト増加して 996 を指します。
ブロック block1 を超えて外側に進むと、変数 a はすぐに解放され、この時点で sp は 4 バイト増加し、元の初期アドレス 1000 を指します。後でスタックにプッシュされると、これらのアドレスは戻ります。再び占有され、その後解放されるというサイクルが繰り返されます。
メモリ ヒープ
スタックは非常に高いパフォーマンスを持っていますが、メモリ スタック上にある変数の有効期間をネストする必要があるため、すべての変数に対して柔軟性がまだあまり高くありません。多くの場合、メソッドが終了した後もデータを長期間使用できるようにする必要があるため、この要件は厳しすぎます。
new 演算子を使用して要求されたヒープ記憶領域が使用されている限り、すべての参照型など、データ宣言期間の遅延は満たされます。 .net の マネージド ヒープ を使用して、メモリ ヒープ上のデータを管理します。 .net のマネージド ヒープは C++ で使用されるヒープとは異なり、ガベージ コレクターの制御下で動作しますが、C++ ヒープは低レベルです。
参照型データはマネージドヒープに保存されるのですが、どのように保存されるのでしょうか?次のコードを見てください
void Shout() { Monkey xingxing; //猴子类 xingxing = new Monkey(); }このコードでは、Monkey と AIMonkey という 2 つのクラスを想定しており、AIMonkey クラスは Monkey オブジェクトを拡張しています。
ここでは、Monkey をオブジェクト、xingxing をそのインスタンスと呼びます。
まず、Monkey 参照 xingxing を宣言し、この参照にストレージ領域をスタック上に割り当てます。これは単なる参照であり、実際の Monkey オブジェクトではないことに注意してください。これは覚えておくことが重要です! ! !
次に、コードの 2 行目を見てください:
xingxing = new Monkey();その内容: まず、Monkey オブジェクトを保存するためにヒープにメモリを割り当てます。 ! !これは実際のオブジェクトであり、4 バイトを占めるアドレスではありません。 ! ! Monkey オブジェクトが 64 バイトを占めるとします。この 64 バイトには、Monkey インスタンスのフィールドと、.NET で Monkey クラスのインスタンスを識別および管理するために使用されるいくつかの情報が含まれているとします。メモリ ヒープ上のアドレスが 1937 ~ 2000 であると仮定すると、これらの 64 バイトは実際にはメモリ ヒープ上に割り当てられます。 new 演算子は、997 ~ 1000 と想定され、xingxing に割り当てられたメモリ アドレスを返します。概略図は次のとおりです。

记住一点:
与内存栈不同的是,堆上的内存是向上分配的,由低地址到高地址。
从上面的例子中,可以看出建立引用实例的过程要比建立值变量的过程更复杂,系统开销更大。那么既然开销这么大,它到底优势何在呢?引用数据类型强大到底在哪里???
请看下面代码:
{//block1 Monkey xingxing; //猴子类 xingxing = new Monkey(); {//block2 Monkey jingjing = xingxing; //jingjing也引用了Monkey对象 //do something } //jinjing超出作用域,它从栈中删除 //现在只有xingxing还在引用Monkey}//xingxing超出作用域,它从栈中删除//现在没有在引用Monkey的了
把一个引用实例的值xingxing赋值予另一个相同类型的实例jingjing,这样的结果便是有两个引用内存中的同一个对象Monkey了。当一个实例超出作用域时,它会从栈中删除,但引用对象的数据还是保留在堆中,一直到程序终止,或垃圾回收器回收它位置,而只有该数据不再有任何实例引用它时,它才会被删除!
随便举一个实际应用引用的简单例子:
//从界面抓取数据放到list中List<Person> persons = getPersonsFromUI(); //retrieve these persons from DBList<person> personsFromDB = retrievePersonsFromDB(); //do something to personsFromDBgetSomethingToPersonsFromDB();
请问对personsFromDB的改变,能在界面上及时相应出来吗?
不能!
请看下面修改代码:
//从界面抓取数据放到list中List<Person> persons = getPersonsFromUI(); //retrieve these persons from DBList<Person> personsFromDB = retrievePersonsFromDB(); int cnt = persons.Count;for(int i=0;i<cnt;i++) { persons[i]= personsFromDB [i] ; } //do something to personsFromDBgetSomethingToPersonsFromDB();
修改后,数据能立即响应在界面上。因为persons与UI绑定,所有修改在persons上,自然可以立即响应。
这就是引用数据类型的强大之处,在C#.NET中广泛使用了这个特性。这表明,我们可以对数据的生存期进行非常强大的控制,因为只要保持对数据的引用,该数据就肯定位于堆上!!!
这也表明了基于栈的实例与基于堆的对象的生存期不匹配!
垃圾回收器 GC
内存堆上会有碎片形成,.NET垃圾回收器会压缩内存堆,移动对象和修改对象的所有引用的地址,这是托管的堆与非托管的堆的区别之一。
.NET的托管堆只需要读取堆指针的值即可,但是非托管的旧堆需要遍历地址链表,找出一个地方来放置新数据,所以在.NET下实例化对象要快得多。
堆的第一部分称为第0代,这部分驻留了最新的对象。在第0代垃圾回收过程中遗留下来的旧对象放在第1代对应的部分上,依次递归下去。。。
承上启下
以上部分便是对托管资源的内存管理部分,这些都是在后台由.NET自动执行的。下面看下非托管资源的内存管理,比如这些资源可能是UI句柄,network连接,文件句柄,Image对象等。.NET主要通过三种机制来做这件事。分别为析构函数、IDisposable接口,和两者的结合处理方法,以此实现最好的处理结果。下面分别看一下。
析构函数
C#编译器在编译析构函数时,它会隐式地把析构函数的代码编译为等价于Finalize()方法的代码,并确定执行父类的Finalize()方法。看下面的代码:
public class Person { ~Person() { //析构实现 } }
~Person()析构函数生成的IL的C#代码:
protected override void Finalize() { try { //析构实现 } finally { base.Finalize(); } }
放在finally块中确保父类的Finalize()一定调用。
C#析构函数要比C++析构函数的使用少很多,因为它的问题是不确定性。在销毁C++对象时,其析构函数会立即执行。但由于C#使用垃圾回收器,无法确定C#对象的析构函数何时执行。如果对象占用了 宝贵的资源,而需要尽快释放资源,此时就不能等待垃圾回收器来释放了。
第一次调用析构函数时,有析构函数的对象需要第二次调用析构函数,才会真正删除对象。如果频繁使用析构,对性能的影响非常大。
IDisposable接口
在C#中,推荐使用IDisposable接口替代析构函数,该模式为释放非托管资源提供了确定的机制,而不像析构那样何时执行不确定。
假定Person对象依赖于某些外部资源,且实现IDisposable接口,如果要释放它,可以这样:
class Person:IDisposable { public void Dispose() { //implementation } } Person xingxing = new Person();//dom somethingxingxing .Dispose();
上面代码如果在处理过程中出现异常,这段代码就没有释放xingxing,所以修改为:
Person xingxing = null;try{ xingxing = new Person(); //do something}finally{ if(xingxing !=null) { xingxing.Dispose(); } }
C#提供了一种语法糖,叫做using,来简化以上操作。
using(Person xingxing = new Person()) { // do something}
using在此处的语义不同于普通的引用类库作用。using用在此处的功能,仅仅是简化了代码,这种语法糖可以少用!!!
总之,实现IDisposable的对象,在释放非托管资源时,必须手动调用Dispose()方法。因此一旦忘记,就会造成资源泄漏。如下所示:
Image backImage = this.BackgroundImage; if (backImage != null) { backImage.Dispose(); SessionToImage.DeleteImage(_imageFilePath, _imageFileName); this.BackgroundImage = null; }
在上面那个例子中,backImage已经确定不再用了,并且backImage又是通过Image.FromFile(fullPathWay)从物理磁盘上读取的,是非托管的资源,所以需要Dispose()一下,这样读取Image的这个进程就被关闭了。如果忘记写backImage.Dispose();就会造成资源泄漏!
结合 析构函数和IDisposable这2种机制
一般情况下,最好的方法是实现两种机制,获得这两种机制的优点。因为正确调用Dispose()方法,同时把实现析构函数作为一种安全机制,以防没有调用Dispose()方法。请参考一种结合两种方法释放托管和非托管资源的机制:
public class Person:IDisposable { private bool isDisposed = false; //实现IDisposable接口 public void Dispose() { //为true表示清理托管和非托管资源 Dispose(true); //告诉垃圾回收器不要调用析构函数了 GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { //isDisposed: 是否对象已经被清理掉了 if(!isDisposed) { if(disposing) { //清理托管资源 } //清理非托管资源 } isDisposed = true; } ~Person() { //false:调用后只清理非托管资源 //托管资源会被垃圾回收器的一个单独线程Finalize() Dispose(false); } }
当这个对象的使用者,直接调用了Dispose()方法,比如
Person xingxing = new Person();//do somethingperson.Dispose();
此时调用IDisposable.Dispose()方法,指定应清理所有与该对象相关的资源,包括托管和非托管资源。
如果未调用Dispose()方法,则是由析构函数处理掉托管和非托管资源。
以上就是C#.NET:内存管理story的图文代码介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!

C#は、Microsoftによって開発された最新のオブジェクト指向プログラミング言語であり、.NETフレームワークの一部として開発されています。 1.C#は、カプセル化、継承、多型を含むオブジェクト指向プログラミング(OOP)をサポートしています。 2。C#の非同期プログラミングは非同期を通じて実装され、適用応答性を向上させるためにキーワードを待ちます。 3. LINQを使用してデータ収集を簡潔に処理します。 4.一般的なエラーには、null参照の例外と、範囲外の例外インデックスが含まれます。デバッグスキルには、デバッガーと例外処理の使用が含まれます。 5.パフォーマンスの最適化には、StringBuilderの使用と、不必要な梱包とボクシングの回避が含まれます。

C#.NETアプリケーションのテスト戦略には、ユニットテスト、統合テスト、エンドツーエンドテストが含まれます。 1.単位テストにより、コードの最小ユニットがMSTEST、ヌニット、またはXUNITフレームワークを使用して独立して動作することを保証します。 2。統合テストでは、一般的に使用されるシミュレートされたデータと外部サービスを組み合わせた複数のユニットの機能を検証します。 3.エンドツーエンドのテストでは、ユーザーの完全な操作プロセスをシミュレートし、通常、セレンは自動テストに使用されます。

C#シニア開発者とのインタビューでは、非同期プログラミング、LINQ、.NETフレームワークの内部作業原則などのコア知識をマスターする必要があります。 1.非同期プログラミングは、非同期を通じて操作を簡素化し、アプリケーションの応答性を向上させるのを待ちます。 2.LinqはSQLスタイルでデータを操作し、パフォーマンスに注意を払います。 3.ネットフレームワークのCLRはメモリを管理し、ガベージコレクションに注意して使用する必要があります。

C#.NETインタビューの質問と回答には、基本的な知識、コアの概念、高度な使用が含まれます。 1)基本知識:C#は、Microsoftが開発したオブジェクト指向言語であり、主に.NETフレームワークで使用されています。 2)コアの概念:委任とイベントは動的な結合方法を可能にし、LINQは強力なクエリ関数を提供します。 3)高度な使用:非同期プログラミングは応答性を向上させ、式ツリーは動的コード構造に使用されます。

C#.NETは、その強力なエコシステムと豊富なサポートのため、マイクロサービスを構築するために人気のある選択肢です。 1)asp.netcoreを使用してRestfulapiを作成して、順序の作成とクエリを処理します。 2)GRPCを使用して、マイクロサービス間の効率的な通信を実現し、注文サービスを定義および実装します。 3)Dockerコンテナ化されたマイクロサービスを介して展開と管理を簡素化します。

C#および.NETのセキュリティベストプラクティスには、入力検証、出力エンコード、例外処理、認証と承認が含まれます。 1)正規表現または組み込みのメソッドを使用して入力を検証して、悪意のあるデータがシステムに入るのを防ぎます。 2)XSS攻撃を防ぐための出力エンコード、httputility.htmlencodeメソッドを使用します。 3)例外処理により、情報の漏れが回避され、エラーが記録されますが、詳細情報はユーザーに返されません。 4)ASP.Netidentityおよび請求に基づく許可を使用して、不正アクセスから申請を保護します。

C言語におけるコロン( ':')の意味:条件付きステートメント:条件付き式とステートメントの分離ブロックループステートメント:初期化、条件付きおよび増分式のマクロ定義の分離:マクロ名とマクロ値の分離単一行コメント:コメントアレイの寸法としてのコロンから行までのコンテンツを表す:アレイの寸法を指定する

c言語は、後の運動後演算子であり、その動作メカニズムには次のものが含まれます。最初に変数の値を取得します。 aの値を1 x1。増加した後、aの値を返します。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

WebStorm Mac版
便利なJavaScript開発ツール

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

AtomエディタMac版ダウンロード
最も人気のあるオープンソースエディター
