Archive for the ‘JIT’ Category

Scope og oprydning

Friday, June 29th, 2007

Som bekendt sker oprydning i .NET ved, at garbage collectoren inspicerer alle objekter på heapen og finder ud af hvilke, der ikke længere er refereret. Så hvis koden ikke har aktive referencer til et objekt, kan garbage collectoren nedlægge objektet og snuppe den afsatte hukommelse. Det er muligvis også kendt, at garbage collection foretages mere aggressivt i Release mode end i Debug mode, men betragt nedenstående kode og vurder så, om der findes en instans af SomeType på heapen, når vi kommer til ReadLine()-kaldet.

static void Main(string[] args) {
   SomeType s = new SomeType();
   GC.Collect();
   Console.ReadLine();
}

Der er vel ingen tvivl om, at referencen s er gyldig, og dermed er der en reference til instansen, men faktum er, at i Release mode er der ingen instanser af SomeType på heapen, når vi kommer til ReadLine(). Forklaringen er, at JIT-compileren er smart nok til at gennemskue, at s ikke anvendes senere, og derfor tillader den, at objektet kan frigives. Eller det er i hvert fald forklaringen på side 69 i Francesco Balenas Visual C# 2005: The Base Class Library. Jeg blev derfor nysgerrig efter at finde ud af, hvor smart denne optimering er.

Vi kan altså konstatere, at det kan være lidt sværere at gennemskue objekters levetid i Release mode, så med det in mente kan vi se på nedenstående eksempel:

static void Main(string[] args) {
   SomeType p = new SomeType();
   SomeType s = new SomeType(p);
   GC.Collect();
   Console.ReadLine();
}

Hvor mange instanser af SomeType har vi på heapen, når vi kommer til ReadLine()? Nul eller to er oplagte svar, men WinDbg kan afsløre, at det korrekte svar er en instans!

0:000> !dumpheap -stat -type Type
total 18 objects
Statistics:
      MT    Count    TotalSize Class Name
011d71c8        1           12 System.RuntimeTypeHandle
009631bc        1           16 ConsoleApplication.Program+SomeType
01202fbc        2           64 System.Reflection.TypeFilter
01200774       14          280 System.RuntimeType
Total 18 objects
0:000> !dumpheap -mt 009631bc
 Address       MT     Size
0135234c 009631bc       16
total 1 objects
Statistics:
      MT    Count    TotalSize Class Name
009631bc        1           16 ConsoleApplication.Program+SomeType
Total 1 objects
0:000> !do 0135234c
Name: ConsoleApplication.Program+SomeType
MethodTable: 009631bc
EEClass: 009616a8
Size: 16(0x10) bytes
 (C:DevelopmentTestAppConsoleApplicationbinReleaseConsoleApplication.exe)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
0120d190  4000007        8         System.Int32  0 instance        1 vCount
009631bc  4000008        4 ....Program+SomeType  0 instance 00000000 vParent

På felternes værdier kan vi konkludere, at det er den instans, p peger på (vParent er null), der er i live. Ergo ser det ikke ud til, at påstanden om JIT-compilerens fortræffeligheder holder helt stik. Det kunne tyde på, at den interne reference, i den instans s peger på, holder ps instans i live til trods for, at denne instans er ligeså overflødig, som den s peger på.

Faktisk viser det sig, at hvis vi på nogen måde bruger en af disse referencer ved f.eks. at sætte et felt eller en property, kalde en metode eller oprette nye referencer til instanserne, holder JIT-compileren igen med den aggressive oprydning. Ergo lader det til, at det kun er i meget begrænset tilfælde at denne optimering finder sted. Man kan forestille sig, at JIT-compileren blot konstaterer, om en reference bruges efter tildelingen eller ej. Hvis den ikke gør, kan instansen nedlægges. I alle andre tilfælde tager de almindelige scope-regler sig af oprydningen.