2014-11-14 10 views
35

Pinning Während durch den Code von PinnableObjectCache von mscorlib durchsuchen, habe ich den folgenden Code gestoßen:GC Verhalten, wenn ein Objekt

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); 
} 

Es hat mich fragen, was der Hinweis auf ein Stecker Mittel? Versucht der GC beim Versuch, ein Objekt im Speicher anzuheften, die spezifische Adresse, die für das Objekt angegeben wurde, an Pin? Was ist das plug Verhalten tatsächlich und warum ist es notwendig, "space out" zwischen den Objekten?

+0

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 ?! –

+1

@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: \ –

+0

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

Antwort

13

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.

+0

Sorry, wenn das eine dämliche Frage ist, aber was ist 'obj1'? Sollte es "ByteArr1" sein? – dumbledad

+0

@dumbledad Du hast vollkommen recht. Ich hatte ein paar Iterationen mit dem Code und ich habe vergessen, die Variablen in der Antwort umzubenennen. –

+0

Sie sollten etwas nach dem Aufruf von GC hinzufügen, um das Objekt am Leben zu erhalten, da ich denke, dass es nur der Debugger ist, der sie am Leben erhält. –

Verwandte Themen