在浏览mscorlib
的{{3}}代码时,我遇到了以下代码:
for (int i = 0; i < m_restockSize; i++)
{
// Make a new buffer.
object newBuffer = m_factory();
// Create space between the objects. We do this because otherwise it forms
// a single plug (group of objects) and the GC pins the entire plug making
// them NOT move to Gen1 and Gen2. By putting space between them
// we ensure that object get a chance to move independently (even if some are pinned).
var dummyObject = new object();
m_NotGen2.Add(newBuffer);
}
让我想知道对插件的引用意味着什么?在尝试将对象固定在内存中时,GC是否会固定为对象指定的特定地址?这plug
行为实际上在做什么,为什么需要在对象之间“空出”?
答案 0 :(得分:14)
好的,所以经过多次尝试获得知情人员的正式回复后,我决定自己做一点实验。
我尝试做的是重新制作场景,其中我有几个固定对象和它们之间的一些未固定对象(我使用byte[]
)来尝试创建未固定对象的效果#&# 39;移动GC堆中的更高代。
代码运行在我的英特尔酷睿i5笔记本电脑上,在调试和发布中运行Visual Studio 2015的32位控制台应用程序中运行。我使用WinDBG实时调试了代码。
代码很简单:
private static void Main(string[] args)
{
byte[] byteArr1 = new byte[4096];
GCHandle obj1Handle = GCHandle.Alloc(byteArr1 , GCHandleType.Pinned);
object byteArr2 = new byte[4096];
GCHandle obj2Handle = GCHandle.Alloc(byteArr2, GCHandleType.Pinned);
object byteArr3 = new byte[4096];
object byteArr4 = new byte[4096];
object byteArr5 = new byte[4096];
GCHandle obj4Handle = GCHandle.Alloc(byteArr5, GCHandleType.Pinned);
GC.Collect(2, GCCollectionMode.Forced);
}
我开始使用!eeheap -gc
来查看GC堆地址空间:
generation 0 starts at 0x02541018
generation 1 starts at 0x0254100c
generation 2 starts at 0x02541000
ephemeral segment allocation context: none
segment begin allocated size
02540000 02541000 02545ff4 0x4ff4(20468)
现在,我逐步运行代码并观察对象是否已分配:
0:000> !dumpheap -type System.Byte[]
Address MT Size
025424e8 72101860 4108
025434f4 72101860 4108
02544500 72101860 4108
0254550c 72101860 4108
02546518 72101860 4108
查看地址我可以看到他们当前在0 {0}从0x02541018
开始。我还看到使用!gchandles
:
Handle Type Object Size Data Type
002913e4 Pinned 025434f4 4108 System.Byte[]
002913e8 Pinned 025424e8 4108 System.Byte[]
现在,我逐步完成代码,直到我到达运行GC.Collect
的行:
0:000> p
eax=002913e1 ebx=0020ee54 ecx=00000002 edx=00000001 esi=025424d8 edi=0020eda0
eip=0062055e esp=0020ed6c ebp=0020edb8 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
0062055e e80d851272 call mscorlib_ni+0xa28a70 (GC.Collect) (72748a70)
现在,预测会发生什么,我使用!eeheap -gc
再次检查GC生成地址,我看到以下内容:
Number of GC Heaps: 1
generation 0 starts at 0x02547524
generation 1 starts at 0x0254100c
generation 2 starts at 0x02541000
第0代的起始地址已从 0x02541018 移至 0x02547524 。
现在,我检查固定的byte[]
对象的地址:
0:000> !dumpheap -type System.Byte[]
Address MT Size
025424e8 72101860 4108
025434f4 72101860 4108
02544500 72101860 4108
0254550c 72101860 4108
02546518 72101860 4108
我发现他们所有都停留在同一个地址。 但是,第0代现在从 0x02547524 开始,这意味着它们都被提升为第1代。
然后,我记得在 Pro .NET Performance 一书中阅读了有关该行为的内容,它说明了以下内容:
固定一个对象可防止它被垃圾移动 集电极。在代际模型中,它阻止了固定的推广 世代之间的对象。这在此尤为重要 年龄较小的一代,如0代,因为大小 第0代非常小。固定对象导致碎片 在第0代内有可能造成比它更多的伤害 可能会在我们介绍几代之前从检查中看出来 进入图片。 幸运的是,CLR有能力进行推广 使用以下技巧固定对象:如果生成0变为 CLR可以用固定的物体严重破碎 第0代的整个空间被认为是更高代和 从将要成为的新内存区域分配新对象 第0代。这是通过改变短暂的片段来实现的。
这实际上解释了我在WinDBG中看到的行为。
所以,总而言之,直到任何人有任何其他解释,我认为评论是不正确的,并没有真正捕捉到GC内部真正发生的事情。如果有人要详细说明,我很乐意补充。