Ok, also nach mehreren Versuchen, offizielle Antworten von Leuten mit "Insider-Wissen" zu erhalten, habe ich mich entschieden, ein wenig selbst zu experimentieren.
Ich habe versucht, das Szenario zu reproduzieren, wo ich ein paar fixierte Objekte und einige nicht gepinnte Objekte zwischen ihnen habe (ich verwendete eine byte[]
), um den Effekt zu erstellen, wo die nicht gepinnten Objekte nicht verschieben eine höhere Generation innerhalb des GC-Heaps.
Der Code lief auf meinem Intel Core i5-Laptop, in einer 32-Bit-Konsolenanwendung mit Visual Studio 2015, sowohl in Debug als auch in Release. Ich debuggte den Code live mit WinDBG.
Der Code ist ziemlich einfach:
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);
}
Ich begann mit einem Blick auf die GC-Heap-Adressraum unter !eeheap -gc
mit:
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)
Nun, ich Schritt durch den Code ausgeführt wird und Uhr wie die Objekte zugewiesen werden:
0:000> !dumpheap -type System.Byte[]
Address MT Size
025424e8 72101860 4108
025434f4 72101860 4108
02544500 72101860 4108
0254550c 72101860 4108
02546518 72101860 4108
Blick auf die Adresse s Ich kann sehen, dass sie alle bei Generation 0 sind, wie sie bei 0x02541018
beginnt. Ich sehe auch, dass die Objekte !gchandles
gepinnten verwenden:
Handle Type Object Size Data Type
002913e4 Pinned 025434f4 4108 System.Byte[]
002913e8 Pinned 025424e8 4108 System.Byte[]
Nun, ich Schritt durch den Code, bis ich auf der Linie erhalten, die läuft 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)
Und jetzt antizipieren, was passiert, ich überprüfen Sie die GC-Generierungsadresse wieder !eeheap -gc
mit, und ich sehe die folgenden:
Number of GC Heaps: 1
generation 0 starts at 0x02547524
generation 1 starts at 0x0254100c
generation 2 starts at 0x02541000
die Startadresse für Generation 0 Biene hat n bewegt von 0x02541018 zu 0x02547524. Jetzt , überprüfe ich die Adresse der festgelegten und keine byte[]
Objekte gemerkt:
0:000> !dumpheap -type System.Byte[]
Address MT Size
025424e8 72101860 4108
025434f4 72101860 4108
02544500 72101860 4108
0254550c 72101860 4108
02546518 72101860 4108
Und ich sehe, sie haben alle an der gleichen Adresse geblieben. Aber, die Tatsache, dass Generation jetzt 0 beginnt bei 0x02547524 bedeutet, sie haben alle 1.
Dann Generation gefördert worden, ich erinnere mich Pro in dem Buch etwas über dieses Verhalten zu lesen.Netto-Performance, es führt die folgenden Schritte aus:
Pinning ein Objekt verhindert, dass er durch den Garbage Kollektors bewegt werden. Im Generationenmodell verhindert es die Förderung von gepinnten Objekten zwischen Generationen. Dies ist besonders wichtig in den jüngeren Generationen, wie Generation 0, weil die Größe Generation 0 sehr klein ist. Fixierte Objekte, die die Fragmentierung innerhalb der Generation 0 verursachen, haben das Potenzial, mehr Schaden zu verursachen, als es von der Untersuchung gepinned erscheinen könnte, bevor wir Generationen in das Bild einführten. Glücklicherweise hat die CLR die Möglichkeit, pinned Objekte mit dem folgenden Trick zu fördern: Wenn Generation 0 stark mit gepinnten Objekten fragmentiert wird, kann die CLR den gesamten Bereich der Generation 0 als höhere Generation und als neue Generation deklarieren Objekte aus einer neuen Speicherregion, die Generation 0 wird. Dies wird durch Ändern des ephemeren Segments erreicht.
Und das erklärt tatsächlich das Verhalten, das ich in WinDBG sehe.
Also, um zu schließen und bis jemand andere Erklärung hat, ich denke, der Kommentar ist nicht korrekt und nicht wirklich erfassen, was wirklich in der GC passiert. Wenn jemand etwas zu erarbeiten hat, würde ich gerne hinzufügen.
Es gibt ein bisschen mehr Informationen im Blogpost http://blogs.msdn.com/b/maoni/archive/2005/10/03/so-what-s-new-in-the-clr-2- 0-gc.aspx, scrollen Sie nach unten zu "Fragmentierungskontrolle". Ich bin mir nicht ganz sicher, ob es die Frage beantwortet ?! –
@MattWarren Er spricht über * Demotion *, wo Objekte zwischen beabstandeten gepinnten Objekten nicht befördert werden. Aber in diesem Beispiel hat der Autor bewusst einen Abstand zwischen den gepinnten Objekten * in der Reihenfolge * zugewiesen, um sicherzustellen, dass sie unabhängig voneinander befördert werden. Leider spricht es nicht über das Plug-Verhalten: \ –
Sieht aus wie ein Weg, um Polsterung im Speicher zu erstellen. Wenn man 'dummyObject' ziemlich schnell angibt, sollte nach 'newBuffer' ein' freier 'Platz frei sein (vorausgesetzt, die Zuweisung ist benachbart). Vielleicht ist der minimale Pinning-Platz zweimal "IntPtr.Size"? – leppie