Ответ 1
Итак, после нескольких попыток получить официальные ответы от людей с "внутренними знаниями", я решил немного поэкспериментировать.
То, что я попытался сделать, - это повторное создание сценария, в котором у меня есть несколько закрепленных объектов и некоторые незакрепленные объекты между ними (я использовал byte[]
), чтобы попытаться создать эффект, при котором незакрепленные объекты не перемещаются более высокое поколение внутри кучи GC.
Код работал на моем ноутбуке Intel Core i5, в 32-битном консольном приложении под управлением Visual Studio 2015 как в Debug, так и в Release. Я отлаживал код в реальном времени с помощью 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);
}
Я начал с рассмотрения адресного пространства кучи GC с помощью !eeheap -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, начиная с 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)
И теперь, предвидя, что произойдет, я снова проверю адрес генерации GC, используя !eeheap -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 имеют потенциал причинения большего вреда, чем он может возникнуть из-за того, что мы рассмотрели прикрепление, прежде чем мы представили поколения в картину. Fortunatly, CLR обладает способностью продвигать закрепленные объекты, используя следующий трюк: если поколение 0 становится сильно фрагментированные с закрепленными объектами, CLR может объявить все пространство поколения 0 считается более высоким поколением и выделить новые объекты из новой области памяти, которые станут генерация 0. Это достигается путем изменения эфемерного сегмента.
И это на самом деле объясняет поведение, которое я вижу внутри WinDBG.
Итак, чтобы заключить и до тех пор, пока у кого-нибудь не будет другого объяснения, я думаю, что комментарий неверен и на самом деле не отражает то, что действительно происходит внутри GC. Если у кого-то есть что-то, что нужно разработать, я был бы рад добавить.