私が自分のコンピューターで試した結果は次のとおりです。
- データをクラスとして使用
_list = null
し、DoWorkの最後に使用しない場合->メモリが増加します
- データを構造体として使用
_list = null
し、DoWorkの最後に使用しない場合->メモリが増加します
- クラスとしてのデータと
_list = null
DoWorkの最後の場合->メモリは150MBで安定します
- データを構造体として使用
_list = null
し、DoWorkの最後に->メモリが増加します
コメントされている場合、_list = null
この結果を見ても驚くことではありません。_listへの参照がまだあるためです。DoWork
二度と呼び出されなくても、GCはそれを知ることができません。
3番目のケースでは、ガベージコレクターは期待どおりの動作をします。
4番目のケースでは、BlockingCollectionは、のData
引数として渡したときにを格納しますcollection.Add(new Data(l));
が、その後はどうなりますか?
- 新しい構造体はequalsto
data
で作成されます(つまり、型はクラス(参照型)であるため、構造体ではのアドレスに等しい)。data._list
l
List
data._list
Data
l
- 次に、引数としてそれを渡し、1で作成された
collection.Add(new Data(l));
もののコピーをdata
作成します。次に、のアドレスl
がコピーされます。
- ブロッキングコレクションは、
Data
要素を配列に格納します。
- を
DoWork
実行すると、現在の構造体でのみ_list = null
問題のある参照が削除List
され、に保存されているすべてのコピーされたバージョンでは削除されませんBlockingCollection
。
- 次に、をクリアしない限り、問題が発生します
BlockingCollection
。
問題を見つける方法は?
メモリリークの問題を見つけるには、SOS(http://msdn.microsoft.com/en-us/library/bb190764.aspx)を使用することをお勧めします。
ここでは、私がどのように問題を見つけたかを示します。これはヒープだけでなくスタックも意味する問題であるため、ヒープ分析(ここでのように)を使用することは、問題の原因を見つけるための最良の方法ではありません。
1ブレークポイントを設定します_list = null
(この行は機能するはずなので!!!)
2プログラムを実行します
3ブレークポイントに達したら、SOSデバッグツールをロードします(イミディエイトウィンドウに「.loadsos」と書き込みます)
4問題は、private List> _list
ノートが正しく廃棄されていることに起因しているようです。そのため、このタイプのインスタンスを見つけようとします。イミディエイト!DumpHeap -stat -type List
ウィンドウに入力します。結果:
total 0 objects
Statistics:
MT Count TotalSize Class Name
0570ffdc 1 24 System.Collections.Generic.List1[[System.Threading.CancellationTokenRegistration, mscorlib]]
04f63e50 1 24 System.Collections.Generic.List1[[System.Security.Policy.StrongName, mscorlib]]
00202800 2 48 System.Collections.Generic.List1[[System.Collections.Generic.Dictionary2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]]
Total 4 objects
問題のあるタイプは最後のものList<Dictionary<...>>
です。2つのインスタンスがあり、MethodTable(タイプの一種の参照)は00202800
です。
5参照を取得するには、と入力し!DumpHeap -mt 00202800
ます。結果:
Address MT Size
02618a9c 00202800 24
0733880c 00202800 24
total 0 objects
Statistics:
MT Count TotalSize Class Name
00202800 2 48 System.Collections.Generic.List1[[System.Collections.Generic.Dictionary2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]]
Total 2 objects
2つのインスタンスが、アドレスとともに表示されます02618a9c
。0733880c
6それらがどのように参照されているかを確認するには:(!GCRoot 02618a9c
最初のインスタンスの場合)または!GCRoot 0733880c
(2番目のインスタンスの場合)と入力します。結果(すべての結果をコピーしたわけではありませんが、重要な部分を保持しています):
ESP:3bef9c:Root: 0261874c(ConsoleApplication1.Test1)->
0261875c(System.Collections.Concurrent.BlockingCollection1[[ConsoleApplication1.Data, ConsoleApplication1]])->
02618784(System.Collections.Concurrent.ConcurrentQueue1[[ConsoleApplication1.Data, ConsoleApplication1]])->
02618798(System.Collections.Concurrent.ConcurrentQueue1+Segment[[ConsoleApplication1.Data, ConsoleApplication1]])->
026187bc(ConsoleApplication1.Data[])->
02618a9c(System.Collections.Generic.List1[[System.Collections.Generic.Dictionary2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]])
最初のインスタンスの場合、および:
Scan Thread 5216 OSTHread 1460
ESP:3bf0b0:Root: 0733880c(System.Collections.Generic.List1[[System.Collections.Generic.Dictionary2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]])
Scan Thread 4960 OSTHread 1360
Scan Thread 6044 OSTHread 179c
2番目のオブジェクト(分析されたオブジェクトのルートが深くない場合は、スタックに参照があることを意味すると思います)。
私たちは最終的に私たちのタイプを見るので、見ることは何が起こるかを理解するための良い方法で026187bc(ConsoleApplication1.Data[])
あるはずです。Data
7オブジェクトのコンテンツを表示するには、を使用します。!DumpObj 026187bc
この場合、配列であるため、を使用します!DumpArray -details 026187bc
。結果(部分的):
Name: ConsoleApplication1.Data[]
MethodTable: 00214f30
EEClass: 00214ea8
Size: 140(0x8c) bytes
Array: Rank 1, Number of elements 32, Type VALUETYPE
Element Methodtable: 00214670
[0] 026187c4
Name: ConsoleApplication1.Data
MethodTable: 00214670
EEClass: 00211ac4
Size: 12(0xc) bytes
File: D:\Development Projects\Centive Solutions\SVN\trunk\CentiveSolutions.Renderers\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
Fields:
MT Field Offset Type VT Attr Value Name
00202800 4000001 0 ...lib]], mscorlib]] 0 instance 02618a9c _list
[1] 026187c8
Name: ConsoleApplication1.Data
MethodTable: 00214670
EEClass: 00211ac4
Size: 12(0xc) bytes
File: D:\Development Projects\Centive Solutions\SVN\trunk\CentiveSolutions.Renderers\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
Fields:
MT Field Offset Type VT Attr Value Name
00202800 4000001 0 ...lib]], mscorlib]] 0 instance 6d50950800000000 _list
[2] 026187cc
Name: ConsoleApplication1.Data
MethodTable: 00214670
EEClass: 00211ac4
Size: 12(0xc) bytes
File: D:\Development Projects\Centive Solutions\SVN\trunk\CentiveSolutions.Renderers\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
Fields:
MT Field Offset Type VT Attr Value Name
00202800 4000001 0 ...lib]], mscorlib]] 0 instance 6d50950800000000 _list
ここに、配列の最初の3つの要素の属性の値があります_list
:02618a9c
、、。「nullポインタ」だと思います。6d50950800000000
6d50950800000000
6d50950800000000
ここにあなたの質問に対する答えがあります:_list
ガベージコレクターにファイナライズさせたいアドレスを直接含む配列(ブロッキングコレクション(6を参照)によって参照されます)があります。
8行が実行されたときに変更されていないことを確認するために、行_line = null
を実行します。
ノート
すでに述べたように、DumpHeapの使用は、値型を意味する現在のタスクにはあまり適していません。なんで?値型はヒープではなくスタックにあるためです。これを確認するのは非常に簡単です!DumpHeap -stat -type ConsoleApplication1.Data
。ブレークポイントを試してください。結果:
total 0 objects
Statistics:
MT Count TotalSize Class Name
00214c00 1 20 System.Collections.Concurrent.ConcurrentQueue`1[[ConsoleApplication1.Data, ConsoleApplication1]]
00214e24 1 36 System.Collections.Concurrent.ConcurrentQueue`1+Segment[[ConsoleApplication1.Data, ConsoleApplication1]]
00214920 1 40 System.Collections.Concurrent.BlockingCollection`1[[ConsoleApplication1.Data, ConsoleApplication1]]
00214f30 1 140 ConsoleApplication1.Data[]
Total 4 objects
の配列はありますが、ありData
ませんData
。DumpHeapはヒープのみを分析するためです。次に!DumpArray -details 026187bc
、ポインタは同じ値でここにあります。!GCRoot
そして、行を実行する前と後の前(と)で見つけた2つのインスタンスのルートを比較すると、行だけが削除されます。実際、リストへの参照は、値型の1つのコピーからのみ削除されていますData
。