C# .net framework 4中存在高内存问题,但framework 4.5中没有

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() {

我有一段消耗大量内存的代码(.net 4):

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