原文: http://www.mikel.cn/article.asp?id=1698
.Netのガベージコレクションの仕組みをご存知ですか? GC がどのように機能するかを簡単に説明していただけますか?メモリを効果的に管理するにはどうすればよいでしょうか? using ステートメントの本文でインスタンス化されたオブジェクトの役割は何ですか?
このセクションは次のように構成されています。 1. ネット タイプとメモリ割り当て 2. GC ガベージ コレクターの動作方法 3. アンマネージ リソースとは 4. オブジェクト リソースを効果的に解放する方法。まとめ。このセクションの学習を始めましょう。
1..ネットタイプとメモリ割り当て
Net のすべての型は、System.Object 型から (直接的または間接的に) 派生します。
CTS の型は、メモリ ヒープに割り当てられる参照型 (参照型、マネージド型とも呼ばれる) と値型の 2 つのカテゴリに分類されます。値の型はスタック上に割り当てられます。写真に示すように
値型変数はスタック上で先入れ後出しの順序を持っているため、値型変数はスコープ外に追い出される前にリソースを解放します。参照型よりもシンプルで効率的です。スタックはメモリを上位アドレスから下位アドレスに割り当てます。
参照型はマネージド ヒープ (マネージド ヒープ) 上に割り当てられ、変数が宣言されてスタック上に保存されます。new を使用してオブジェクトを作成すると、オブジェクトのアドレスがこの変数に格納されます。逆に、マネージド ヒープは、図に示すように、下位アドレスから上位アドレスにメモリを割り当てます。
2. GC ガベージ コレクターの仕組み
上の図では、dataSet の有効期限が切れてもオブジェクトの破棄は表示されず、ヒープ上のオブジェクトは引き続き存在し、GC リサイクルを待っています。
ガベージ コレクターが生成を通じてオブジェクトのエージングをサポートすることをお勧めしますが、必須ではありません。世代とは、メモリ内の相対的な年齢を持つオブジェクトの単位です。物体
コードネームまたは年齢は、オブジェクトが属する世代を識別します。アプリケーションのライフサイクルでは、最近作成されたオブジェクトは新しい世代に属し、以前に作成されたオブジェクトよりも高い値を持ちます。
下位のサブコード番号。最新世代のオブジェクト コードは 0 です。
新しいオブジェクトを作成するときは、まず空きリンク リストを検索して最適なメモリ ブロックを見つけ、それを割り当て、メモリ ブロックのリンク リストを調整し、フラグメントをマージする必要があります。新しい操作は、ヒープの先頭ポインターに 1 を加えて、ほぼ O(1) 時間で完了できます。動作原理は次のとおりです。マネージド ヒープ上の残りのスペースが不十分な場合、またはジェネレーター 0 のスペースがいっぱいの場合、GC が実行され、メモリの再利用が開始されます。ガベージ コレクションの開始時に、GC によってヒープ メモリが圧縮および調整され、オブジェクトが上部に集中します。 GC はガベージをスキャンするときに一定量の CPU 時間を占有します。元の GC アルゴリズムは実際にはヒープ全体をスキャンするため、非効率的です。現在の GC は、ヒープ内のオブジェクトを 3 つの世代に分割します。ヒープへの最新のエントリは世代 0、続いて世代 1、世代 2 です。最初の GC は世代 0 のみをスキャンします。再利用されたスペースが現在の使用に十分な場合は、他の世代のオブジェクトをスキャンする必要はありません。したがって、GC は C++ よりも効率的にオブジェクトを作成し、ヒープ領域全体をスキャンする必要がありません。スキャン戦略とメモリ管理戦略によってもたらされるパフォーマンスの向上は、GC によって占有される CPU 時間を補うのに十分です。
3. アンマネージドリソースとは何ですか?
一般的なアンマネージド リソースは、ファイル、ウィンドウ、ネットワーク接続などのオペレーティング システム リソースをラップするオブジェクトですが、ガベージ コレクターはアンマネージド リソースをラップするオブジェクトの有効期間を追跡する方法を知っています。これらのリソースをクリーンアップします。幸いなことに、.net Framework が提供する Finalize() メソッドを使用すると、ガベージ コレクターがそのようなリソースをリサイクルする前に、アンマネージ リソースを適切にクリーンアップできます。ここでは、ブラシ、ストリーム オブジェクト、コンポーネント オブジェクト、その他のリソース (Object、OdbcDataReader、OleDBDataReader、Pen、Regex、Socket、StreamWriter、ApplicationContext、Brush、
コンポーネント、コンポーネントデザイナー、コンテナ、コンテキスト、カーソル、ファイルストリーム、
フォント、アイコン、画像、マトリックス、タイマー、ツールチップ)。 (MSDNを参照)
4. 管理されていないリソースを効果的に解放する方法。
GC はアンマネージド リソースを管理できないため、アンマネージド リソースを解放するにはどうすればよいでしょうか? .Net では、次の 2 つの方法が提供されます。
(1) デストラクター: ガベージ コレクターがアンマネージ オブジェクトのリソースをリサイクルするとき、オブジェクトのファイナライズ メソッド Finalize() を呼び出してリソースをクリーンアップします。ただし、GC 動作ルールの制限により、GC はオブジェクトの Finalize を呼び出します。リソースは一度は解放されず、オブジェクトは 2 回目の呼び出し後に削除されます。
(2) IDisposable インターフェイスを継承し、Dispose() メソッドを実装します。 IDisposable インターフェイスは、(言語レベルのサポートを備えた) パターンを定義し、アンマネージ リソースを解放するための特定のメカニズムを提供し、デストラクターのガベージ コレクションに固有の問題を回避します。 -関連の問題。
ガベージ コレクションのメカニズムをよりよく理解するために、コードの一部を特別に作成し、詳細なコメントを追加しました。単一クラス FrankClassWithDispose (インターフェイス IDisposable を継承)、FrankClassNoFinalize (ファイナライザーなし)、FrankClassWithDestructor (デストラクターを定義) を定義します。
具体的なコードは次のとおりです。
コード
1 システムを使用します。
2 System.Collections.Generic を使用します。
3 System.Text を使用します。
4 System.Data を使用します。
5 System.Data.Odbc を使用します。
6 System.Drawing を使用します。
7 // フランク・シュー・レイによってコード化 18/2/2009
8 // .NET のメモリ管理を勉強する
9 // ガベージ コレクター ガベージ コレクター。ホストされているリソースは、ポリシーに従って必要に応じて再利用できます。
10 // しかし、GC はアンマネージ リソースの管理方法を知りません。ネットワーク接続、データベース接続、ブラシ、コンポーネントなど。
11 //管理されていないリソースの解放の問題を解決する 2 つのメカニズム。デストラクター、IDispose インターフェイス
12 // COM参照数
13 // C++ 手動管理、新規削除
14 // VB 自動管理
15 名前空間メモリ管理
16 {
17 // インターフェース IDisposable を継承し、Dispose メソッドを実装し、FrankClassDispose のインスタンス リソースを解放します
18 パブリック クラス FrankClassWithDispose : IDisposable
19 {
20 プライベート OdbcConnection _odbcConnection = null;
21
22 // コンストラクター
23 パブリック FrankClassWithDispose()
24 {
25 if (_odbcConnection == null )
26 _odbcConnection = 新しい OdbcConnection();
27 Console.WriteLine( " FrankClassWithDispose が作成されました " );
28 }
29 //テスト方法
30 パブリック void DoSomething()
31 {
32
33 /**/ /// /何かを行うためにここにコードを書きます
34 戻ります。
35}
36 // このクラスで使用されるリソースの破棄と解放を実装します
37 パブリック void Dispose()
38 {
39 if (_odbcConnection != null )
40 _odbcConnection.Dispose();
41 Console.WriteLine( " FrankClassWithDispose が破棄されました " );
42 }
43}
44 // Finalize は実装されていないため、GC が FrankClassFinalize のインスタンス リソースをリサイクルするのを待ち、GC の実行中に直接リサイクルします。
45 パブリッククラス FrankClassNoFinalize
46 {
47 プライベート OdbcConnection _odbcConnection = null ;
48 //コンストラクター
49 パブリック FrankClassNoFinalize()
50{
51 if (_odbcConnection == null )
52 _odbcConnection = 新しい OdbcConnection();
53 Console.WriteLine( " FrankClassNoFinalize が作成されました " );
54 }
55 //テスト方法
56 パブリック void DoSomething()
57 {
58
59 // GC.Collect();
60 /**/ /// /ここにコードを書いて何かをする
61 戻ります。
62 }
63}
64 // デストラクターを実装し、Finalize メソッドにコンパイルして、オブジェクトのデストラクターを呼び出します
65 // GC の実行中、リソースは最初の呼び出しでは解放されず、2 番目の呼び出しでのみ解放されます。
66 //FrankClassDestructorのインスタンスリソース
67 // CLR は独立したスレッドを使用してオブジェクトの Finalize メソッドを実行すると、パフォーマンスが低下します。
68 パブリッククラス FrankClassWithDestructor
69 {
70 プライベート OdbcConnection _odbcConnection = null ;
71 //コンストラクター
72 パブリック FrankClassWithDestructor()
73 {
74 if (_odbcConnection == null )
75 _odbcConnection = 新しい OdbcConnection();
76 Console.WriteLine( " FrankClassWithDestructor が作成されました " );
77 }
78 //テスト方法
79 パブリック void DoSomething()
80 {
81 /**/ /// /コードをここに書いて何かをする
82
83 リターン;
84}
85 // デストラクター、アンマネージ リソースを解放します
86 ~ フランククラスウィズデストラクター()
87 {
88 if (_odbcConnection != null )
89 _odbcConnection.Dispose();
90 Console.WriteLine( " FrankClassWithDestructor が破棄されました " );
91 }
92 }
93}
94
アンマネージ オブジェクト OdbcConnection のインスタンスが使用されます。構築されたクライアントは簡単にテストされました。クライアントのコードは次のとおりです。
コード
1 システムを使用します。
2 System.Collections.Generic を使用します。
3 System.Text を使用します。
4 System.Data を使用します。
5 メモリ管理を使用します。
6 // フランク・シュー・レイによってコード化 18/2/2009
7 // .NET のメモリ管理を勉強する
8 // 回収されたアンマネージドオブジェクトをテストします。
9 // アンマネージ コードのテスト、比較
10 // マネージド コードの場合、GC はそれを単独でより戦略的にリサイクルしたり、IDisposable を実装して Dispose() メソッドを呼び出して積極的に解放したりできます。
11 名前空間 MemoryManagementClient
12 {
13のクラスプログラム
14 {
15 static void Main(string [] args)
16 {
17
18 /**/ //////////////////////////////////////////(1 ) /////////////////////////////////////////////
19 //Dispose() メソッドを呼び出してアクティブに解放します。リソース、柔軟性
20 FrankClassWithDispose _frankClassWithDispose = null ;
21 トライ
22 {
23 _frankClassWithDispose = 新しい FrankClassWithDispose();
24 _frankClassWithDispose.DoSomething();
25
26}
27ついに
28 {
29 if (_frankClassWithDispose != null )
30 _frankClassWithDispose.Dispose();
31 // Console.WriteLine("FrankClassWithDispose インスタンスが解放されました");
32}
33
34 /**/ //////////////////////////////////////////(2 ) / ////////////////////////////////////////////// /
35 // メソッドの実行が終了する前に、Using ステートメントを使用してアンマネージ オブジェクトを作成できます。
36 (FrankClassWithDispose _frankClassWithDispose2 = new FrankClassWithDispose()) を使用します
37 {
38 // _frankClassWithDispose2.DoSomething();
39 }
40
41 /**/ //////////////////////////////////////////(3 ) /////////////////////////////////////////////
42 //ガベージコレクターが実行されている場合、リソースは一度解放されます
43 FrankClassNoFinalize _frankClassNoFinalize = new FrankClassNoFinalize();
44 _frankClassNoFinalize.DoSomething();
45
46 /**/ //////////////////////////////////////////// (4) ////////////////////////////////////////////// / /
47 // ガベージ コレクターが実行されている場合、リソースの解放に 2 回かかります。
48 FrankClassWithDestructor _frankClassWithDestructor = new FrankClassWithDestructor();
49 _frankClassWithDestructor.DoSomething();
50 /**/ /////////////////////////////////////////// / (5) ///////////////////////////////////////////// /
51 // using ステートメントは IDispose インターフェイスを実装していないため、オブジェクトの作成に使用できません。
52 // (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor()) を使用します
53 // {
54 // _frankClassWithDestructor2.DoSomething();
55 // }
56
57 /**/ /////////////////////////////////////////// /// ///////////////////////////////////////////
58 // デバッグ用
59 Console.WriteLine( " 続行するには任意のキーを押してください " );
60 コンソール.ReadLine();
61
62
63}
64}
65 }
66
場合によっては、リソースを特定の時間に解放する必要がある場合があります。クラスは、リソース管理およびクリーンアップ タスク メソッド IDisposable.Dispose を実行するインターフェイス IDisposable を実装できます。
呼び出し元がオブジェクトをクリーンアップするために Dispose メソッドを呼び出す必要がある場合、クラスはコントラクトの一部として Dispose メソッドを実装する必要があります。ガベージ コレクターはデフォルトでは呼び出されません
Dispose メソッド。ただし、Dispose メソッドを実装すると、GC 内のメソッドを呼び出して、ガベージ コレクターの最終的な動作を制御できます。
Dispose() メソッドを呼び出すとリソースが積極的に解放され、メソッドの実行が終了する前に、Using ステートメントを使用してアンマネージ オブジェクトを作成できます。
Dispose() メソッドはリソースを解放します。コードの両端の効果は同じです。コンパイルされた IL を表示できます。
コード
1.試してみる
2 {
3 IL_0003: いや
4 IL_0004: newobj インスタンス void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
5 IL_0009: stloc 0。
6 IL_000a: ldloc 0。
7 IL_000b: callvirt インスタンス void [MemoryManagement]MemoryManagement.FrankClassWithDispose::DoSomething()
8 IL_0010: いいえ
9 IL_0011: いいえ
10 IL_0012: 休暇を取る IL_0028
11 } // 終了.try
ついに12
13 {
14 IL_0014: いや
15 IL_0015: ldloc 0。
16 IL_0016: ldnull
17 IL_0017: CEQ
18 IL_0019: stloc.s CS$ 4 $ 0000
19 IL_001b: ldloc.s CS$ 4 $ 0000
20 IL_001d: brtrue.s IL_0026
21 IL_001f: ldloc 0。
22 IL_0020: callvirt インスタンス void [MemoryManagement]MemoryManagement.FrankClassWithDispose::Dispose()
23 IL_0025: いいえ
24 IL_0026: いや
25 IL_0027: 最後に終了
26 } // 終了ハンドラー
27 IL_0028: いいえ
28 IL_0029: newobj インスタンス void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
29 IL_002e: stloc 1
30.試してみる
31 {
32 IL_002f: いや
33 IL_0030: いいえ
34 IL_0031: 休暇を取る IL_0045
35 } // .tryの終了
36 ついに
37 {
38 IL_0033: ldloc 1。
39 IL_0034: ldnull
40 IL_0035: CEQ
41 IL_0037: stloc.s CS$ 4 $ 0000
42 IL_0039: ldloc.s CS$ 4 $ 0000
43 IL_003b: brtrue.s IL_0044
44 IL_003d: ldloc 1。
45 IL_003e: callvirt インスタンス void [mscorlib]System.IDisposable::Dispose()
46 IL_0043: いや
47 IL_0044: 最後に終了
48 } // 終了ハンドラー
49
using ステートメントには、管理対象外のオブジェクト リソースを解放するのと同じ効果があります。これは、Using キーワードの用途や同様の質問など、面接でよく遭遇します。基本的な理想的な答えは、名前空間の参照と名前空間のエイリアス設定に加えて、この使用法により try Final ブロックのようなアンマネージ オブジェクト リソースのリサイクルが実現されることです。あくまで簡単な書き方です。
Dispose メソッドを使用してアンマネージ オブジェクトを解放する場合は、GC.SuppressFinalize を呼び出す必要があります。オブジェクトがファイナライズ キューにある場合、GC.SuppressFinalize により、GC が Finalize メソッドを呼び出すことができなくなります。 Finalize メソッドを呼び出すとパフォーマンスがある程度犠牲になるためです。 Dispose メソッドが委任されたリソースをすでにクリーンアップしている場合、GC がオブジェクトの Finalize メソッド (MSDN) を再度呼び出す必要はありません。参照用に MSDN コードを添付します。
コード
パブリック クラス BaseResource: IDisposable
{
// 外部のアンマネージド リソースを指す
プライベート IntPtr ハンドル。
// このクラスによって使用される他の管理リソース。
プライベートコンポーネントコンポーネント。
// .Dispose メソッドが呼び出されるかどうかを追跡し、フラグ ビットを付け、ガベージ コレクターの動作を制御します
private bool 破棄 = false ;
//コンストラクタ
パブリックBaseResource()
{
// 適切なコンストラクター コードをここに挿入します。
}
// インターフェース IDisposable を実装します。
// 仮想メソッド virtual として宣言できません。
// サブクラスはこのメソッドをオーバーライドできません。
public void Dispose()
{
破棄( true );
//ファイナライズキューを離れる
// オブジェクトのブロッキングファイナライザーコードを設定します
//
GC.SuppressFinalize( this );
}
// Dispose(bool disposing) は 2 つの異なる状況で実行されます。
// disposing が true に等しい場合、メソッドは呼び出されています。
// または、ユーザー コードによって間接的に呼び出されます。マネージ コードとアンマネージ コードの両方を解放できます。
// disposing が false に等しい場合、メソッドはファイナライザーによって内部的に呼び出されています。
// 他のオブジェクトを参照することはできません。解放できるのはアンマネージ リソースのみです。
protected virtual void Dispose( bool disposing)
{
// Dispose が呼び出されたかどうかを確認します。
if ( ! これ .処分)
{
// true の場合、すべてのマネージド リソースとアンマネージド リソースを解放します
もし(処分するなら)
{
// 管理されているリソースを解放します。
コンポーネント.Dispose();
}
// 破棄が false の場合、アンマネージ リソースを解放します。
// 次のコードのみが実行されます。
CloseHandle(ハンドル);
ハンドル = IntPtr.Zero;
// これはスレッドセーフではないことに注意してください。
// 管理対象リソースが解放された後、他のスレッドを開始してオブジェクトを破棄できます。
// ただし、破棄されたフラグが true に設定される前に
// スレッド セーフが必要な場合、クライアントはそれを実装する必要があります。
}
処分 = true;
}
//相互運用機能を使用してメソッドを呼び出す
// 管理されていないリソースをクリアします。
[System.Runtime.InteropServices.DllImport( " Kernel32 " )]
private extern static Boolean CloseHandle(IntPtr ハンドル);
//C# デストラクターを使用してファイナライザー コードを実装する
// これは、Dispose メソッドが呼び出されていない場合にのみ呼び出して実行できます。
// 基本クラスにファイナライズの機会を与える場合。
// サブクラスにはデストラクターを提供しないでください。
~BaseResource()
{
// クリーンアップ コードを再作成しないでください。
// 信頼性と保守性を考慮すると、Dispose(false) を呼び出すことが最善の方法です
破棄( false );
}
// Dispose メソッドを複数回呼び出すことができます。
// ただし、オブジェクトが解放されている場合は例外がスローされます。
// オブジェクトをいつ処理するかに関係なく、オブジェクトが解放されたかどうかを確認します。
// 破棄されたかどうかを確認します。
public void DoSomething()
{
if (この.処分済み)
{
新しい ObjectDisusedException() をスローします。
}
}
Dispose メソッドよりも Close メソッドを呼び出す方が自然な型の場合は、基本クラスに Close メソッドを追加できます。
Close メソッドはパラメーターを受け取らず、適切なクリーンアップ作業を実行する Dispose メソッドを呼び出します。
次の例は、Close メソッドを示しています。
// メソッドを仮想に設定しないでください。
// 継承されたクラスはこのメソッドをオーバーライドすることはできません
public void Close()
{
// パラメータを指定せずに Dispose パラメータを呼び出します。
破棄();
}
パブリック静的 void Main()
{
// ここにコードを挿入して作成します
// そして BaseResource オブジェクトを使用します。
}