当我使用List 时,我在Jetbrains dotMemory中看到的这个“pinning handle object ”是什么?
我试图想出最简单的代码来重现我所看到的。 完整的程序如下,但我将在此处进行描述。 假设我有一个名为ListData
,它只有一些属性。 然后假设我有一个MyList
类,它有一个成员List m_list
。 假设m_list
在m_list
构造函数中初始化。
在main方法中,我只需创建其中一个MyList
对象,向其中添加一些ListData
,然后让它超出范围。 添加ListData
后,我在dotMemory中拍摄快照,然后在MyList对象超出范围后再拍摄另一个快照。
在dotMemory中,我可以看到MyList对象已按预期回收。 我还看到我创建的两个ListData
对象也按预期回收。
我不明白为什么有幸存的ListData[]
? 这是一个屏幕截图:
我在ListData[]
的最新快照上打开幸存的对象,然后我查看Key Retention Paths,这就是我所看到的。
我是.NET内存管理的新手,我创建了这个示例应用程序来帮助我探索它。 我下载了JetBrains dotMemory版本4.3的试用版。 我正在使用Visual Studio 2013 Professional。 我必须学习内存管理,这样才能解决我们在工作中遇到的内存问题。
这是完整的程序,可用于重现这一点。 它只是一个快速而肮脏的应用程序,但如果你对它进行分析,它会得到我要问的东西。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { class ListData { public ListData(string name, int n) { Name = name; Num = n; } public string Name { get; private set; } public int Num { get; private set; } } class MyList { public MyList() { m = new List(); } public void AddString(ListData d) { m.Add(d); } private List m; } class Program { static void Main(string[] args) { { MyList l = new MyList(); bool bRunning = true; while (bRunning) { Console.WriteLine("a or q"); string input = Console.ReadLine(); switch (input) { case "a": { Console.WriteLine("Name: "); string strName = Console.ReadLine(); Console.WriteLine("Num: "); string strNum = Console.ReadLine(); l.AddString(new ListData(strName, Convert.ToInt32(strNum))); break; } case "q": { bRunning = false; break; } } } } Console.WriteLine("good bye"); Console.ReadLine(); } } }
脚步:
- 在发布中构建上面的代码。
- 在dotMemory中,选择配置独立应用程序。
- 浏览到发布exe。
- 选择立即开始收集分配数据的选项。
- 单击运行。
- 立即拍摄快照并在“之前”命名。 这是在添加任何ListData之前。
- 在应用程序中,键入a并添加两个ListData。
- 在dotMemory中,拍摄另一个快照,并将其命名为“添加2”,因为我们添加了两个ListData。
- 在应用程序中,键入q以退出(MyList将超出范围)。 在再次输入Enter以退出应用程序之前,请在dotMemory中拍摄另一个快照。 将其命名为“超出范围”。
- 在应用程序中,输入Enter以关闭应用程序。
- 在dotMemory中,比较“添加的2”和“超出范围”快照。 按命名空间分组。 您将看到我所指的ListData []。
请注意,MyList和两个ListData对象确实收集了垃圾,但ListData []却没有。 为什么会有一个ListData []? 如何让垃圾收集?
为什么会有一个ListData []? 如何让垃圾收集?
如果你看一下dotMemory里面的“Creation Stack Trace”,你会看到:
这表明空ListData[0]
实例是通过List
的静态构造函数创建的。 如果你看一下这个来源 ,你会看到:
static readonly T[] _emptyArray = new T[0];
List
初始化一个默认的空数组,以优化每次创建新List
时避免这样的分配。 这是默认构造函数:
public List() { _items = _emptyArray; }
只有一个使用List
,它会调整数组的大小。
static
成员是从“高频堆”引用的,它们为应用程序中的每个AppDomain
创建一次。 您看到的固定object[]
实际上是存储所有static
实例的位置。
由于实例是static
,因此它将在应用程序的生命周期内保留在内存中。