C# .net framework 4中存在高内存问题,但framework 4.5中没有
我有一段消耗大量内存的代码(.net 4):C# .net framework 4中存在高内存问题,但framework 4.5中没有,c#,garbage-collection,C#,Garbage Collection,我有一段消耗大量内存的代码(.net 4): struct Data { private readonly List<Dictionary<string,string>> _list; public Data(List<Dictionary<string,string>> List) { _list = List; } public void DoWork() {
struct Data
{
private readonly List<Dictionary<string,string>> _list;
public Data(List<Dictionary<string,string>> List)
{
_list = List;
}
public void DoWork()
{
int num = 0;
foreach (Dictionary<string, string> d in _list)
{
foreach (KeyValuePair<string, string> kvp in d)
num += Convert.ToInt32(kvp.Value);
}
Console.Write(num);
//_list = null;
}
}
class Test1
{
BlockingCollection<Data> collection = new BlockingCollection<Data>(10);
Thread th;
public Test1()
{
th = new Thread(Work);
th.Start();
}
public void Read()
{
List<Dictionary<string, string>> l = new List<Dictionary<string, string>>();
Random r = new Random();
for (int i=0; i<100000; i++)
{
Dictionary<string, string> d = new Dictionary<string,string>();
d["1"] = r.Next().ToString();
d["2"] = r.Next().ToString();
d["3"] = r.Next().ToString();
d["4"] = r.Next().ToString();
l.Add(d);
}
collection.Add(new Data(l));
}
private void Work()
{
while (true)
{
collection.Take().DoWork();
}
}
}
class Program
{
Test1 t = new Test1();
static void Main(string[] args)
{
Program p = new Program();
for (int i = 0; i < 1000; i++)
{
p.t.Read();
}
}
}
struct数据
{
私有只读列表_List;
公共数据(列表)
{
_列表=列表;
}
公共工作
{
int num=0;
foreach(字典d在_列表中)
{
foreach(d中的KeyValuePair kvp)
num+=Convert.ToInt32(kvp.Value);
}
控制台写入(num);
//_列表=空;
}
}
类Test1
{
BlockingCollection=新BlockingCollection(10);
螺纹th;
公共测试1()
{
th=新螺纹(工件);
th.Start();
}
公共无效读取()
{
列表l=新列表();
随机r=新随机();
对于(int i=0;i我在我的计算机上进行了尝试,结果如下:
将数据作为类,在工作结束时不使用\u list=null
->内存增加
将数据作为struct,并且在工作结束时不使用\u list=null
->内存增加
将数据作为类,并在工作结束时使用\u list=null
,内存稳定在150MB
将数据作为struct,并在工作结束时使用\u list=null
->内存增加
在注释了\u list=null
的情况下,看到这个结果并不奇怪。因为仍然有一个对列表的引用。即使不再调用DoWork
,GC也无法知道它
在第三种情况下,垃圾收集器具有我们期望的行为
对于第四种情况,当您将数据作为collection.Add(newdata(l));
中的参数传递时,BlockingCollection存储数据,但接下来该怎么办
新结构数据
是用数据创建的。_list
等于l
(即类型列表
是一个类(参考类型),数据。_list
在结构数据
中等于l
的地址)
然后将其作为参数传递到collection.Add(newdata(l));
中,然后创建在1中创建的数据的副本。然后复制l
的地址
阻塞集合将数据
元素存储在一个数组中
当执行DoWork
时,它仅在当前结构中删除对有问题的列表的引用,而不是在存储在BlockingCollection
中的所有复制版本中
然后,除非清除BlockingCollection
,否则就会出现问题
如何发现问题?
要查找内存泄漏问题,我建议您使用SOS()
在这里,我将介绍我是如何发现这个问题的。因为这个问题不仅意味着堆,而且意味着堆栈,所以使用堆分析(如这里所示)并不是找到问题根源的最佳方法
1在\u list=null
上放置一个断点(因为这行应该可以工作!!!)
2执行程序
3当达到断点时,加载SOS调试工具(在即时窗口中写入“.load SOS”)
4问题似乎来自于正确处理注释的private List>\u List
。因此,我们将尝试在即时窗口中查找类型的实例。type!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
有问题的类型是最后一个列表
。有2个实例,MethodTable(该类型的一种引用)是0022800
5要获取引用,请键入!DumpHeap-mt 0022800
。结果:
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
显示了两个实例及其地址:02618a9c
和0733880c
6查找它们是如何引用的:键入!GCRoot 02618a9c
(第一个实例)或!GCRoot 0733880c
(第二个实例)。结果(我没有复制所有结果,但保留了重要部分):
首先,以及:
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
对于第二个(当分析的对象没有更深的根时,我认为这意味着它在堆栈中有引用)
查看026187bc(ConsoleApplication1.Data[])
应该是了解发生了什么的好方法,因为我们终于看到了数据类型
7要显示对象的内容,请使用!DumpObj 026187bc
,或者在这种情况下,由于它是一个数组,请使用!DumpArray-details 026187bc
。结果(部分):
这里有数组前三个元素的\u list
属性值:02618a9c
,6D5095080000000
,6D5095080000000
。
我怀疑6D5095080000000
是“空指针”
这里我们可以回答您的问题:有一个数组(由阻塞集合引用(参见6.)),它直接包含我们希望垃圾收集器完成的\u列表的地址
8为确保在执行行\u 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
有一个数据数组
,但没有数据
。因为DumpHeap只分析堆。然后!DumpArray-details 026187bc
,指针仍然在这里,具有相同的值。如果您比较我们以前找到的两个实例的根(使用!GCRoot
)在执行行之前和之后,将只删除行。实际上,对列表的引用仅从1中删除
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
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