Get rid of it quickly

You sometimes see pieces of code which set local variables to null with the aim of releasing instances early. In debug builds, local variable lifetimes are extended to the end of the method, but for release builds I think you’d expect the jit compiler to calculate the liveness information (in most situations) to allow resources to be freed early.

Given a class Test1, I want to have a quick look at the liveness information that is calculated for the method:

void DoTest()
{
    Type1 target = new Type1();
    Type1 target2 = new Type1();
    Type1 target3 = new Type1();
    Type1 target4 = new Type1();
    Type1 target5 = new Type1();
    target.Hello();
    target2.Hello();
    target3.Hello();
    target4.Hello();
    target5.Hello();
    Console.WriteLine("Finished");
}

Using the sos gcinfo command, we get the following output.

0:000> !gcinfo 003C00A0
entry point 003c00a0
Normal JIT generated code
GC info 000e196c
Method info block:
    method      size   = 00B6
    prolog      size   =  9
    epilog      size   =  8
    epilog     count   =  1
    epilog      end    = yes 
    callee-saved regs  = EDI ESI EBX EBP
    ebp frame          = yes 
    fully interruptible= no 
    double align       = no 
    arguments size     =  0 DWORDs
    stack frame size   =  2 DWORDs
    untracked count    =  0
    var ptr tab count  =  2
    epilog        at   00AE
    argTabOffset = 6 
81 36 E5 C6 2A |
02 06          |

Pointer table:
10 16 5C       | 0016..0072  [EBP-10H] a  pointer
14 51 2C       | 0067..0093  [EBP-14H] a  pointer
B2 40          | 0032        call [ ESI ] argMask=00
2A             | 003C        call [ ESI ] argMask=00
3A             | 0046        call [ EDI ESI ] argMask=00
3A             | 0050        call [ EDI ESI ] argMask=00
7A             | 005A        call [ EDI ESI EBX ] argMask=00
7A             | 0064        call [ EDI ESI EBX ] argMask=00
7B             | 006F        call [ EDI ESI EBX ] argMask=00
79             | 0078        call [ EDI ESI EBX ] argMask=00
58             | 0080        call [ EDI EBX ] argMask=00
48             | 0088        call [ EBX ] argMask=00
FF             |

Note that in the above C# code, I needed to keep going until five local variables to make sure that there is a need to spill some locals on to the stack. This is the disassembled code for the method which we can cross-reference with the pointer tracking information from gcinfo.

0:000> u 003C00A0
003c00a0 55              push    ebp
003c00a1 8bec            mov     ebp,esp
003c00a3 57              push    edi
003c00a4 56              push    esi
003c00a5 53              push    ebx
003c00a6 83ec08          sub     esp,8
003c00a9 b9b0380e00      mov     ecx,0E38B0h
003c00ae e86d1fd1ff      call    000d2020                         

We have now allocated the object in the local variable target. This is returned in eax, which we place into the [ebp-10h] spill. The pointer tracking, above, notes that the spill contains a tracked value after this instruction.

003c00b3 8945f0          mov     dword ptr [ebp-10h],eax
003c00b6 8bc8            mov     ecx,eax
003c00b8 ff15f0380e00    call    dword ptr ds:[0E38F0h]
003c00be b9b0380e00      mov     ecx,0E38B0h
003c00c3 e8581fd1ff      call    000d2020
003c00c8 8bf0            mov     esi,eax

The local variable target2 is held in esi. This is live for the next 8 call instructions according to the pointer map from the gcinfo. Information such as
   call [ EDI ESI ] argMask=00
is showing that EDI and ESI contain live pointers at the point of the calls.

003c00ca 8bce            mov     ecx,esi
003c00cc ff15f0380e00    call    dword ptr ds:[0E38F0h]
003c00d2 b9b0380e00      mov     ecx,0E38B0h
003c00d7 e8441fd1ff      call    000d2020
003c00dc 8bf8            mov     edi,eax
003c00de 8bcf            mov     ecx,edi
003c00e0 ff15f0380e00    call    dword ptr ds:[0E38F0h]
003c00e6 b9b0380e00      mov     ecx,0E38B0h
003c00eb e8301fd1ff      call    000d2020
003c00f0 8bd8            mov     ebx,eax
003c00f2 8bcb            mov     ecx,ebx
003c00f4 ff15f0380e00    call    dword ptr ds:[0E38F0h]
003c00fa b9b0380e00      mov     ecx,0E38B0h
003c00ff e81c1fd1ff      call    000d2020

On the next instruction we spill the local target5. This will remain a valid pointer source until offset 0093.

003c0104 8945ec          mov     dword ptr [ebp-14h],eax
003c0107 8bc8            mov     ecx,eax
003c0109 ff15f0380e00    call    dword ptr ds:[0E38F0h]
003c010f 8b4df0          mov     ecx,dword ptr [ebp-10h]
003c0112 ff15a4380e00    call    dword ptr ds:[0E38A4h]

The pointer tracking for [ebp-10h], the spill holding target, is no longer live according to the pointer tracking information. [ebp-10h] was live from 0016..0072. The local variable target is now dead.

003c0118 8bce            mov     ecx,esi
003c011a ff15a4380e00    call    dword ptr ds:[0E38A4h]

Esi is no longer a valid pointer and hence is not included in the live register information for subsequent calls.

003c0120 8bcf            mov     ecx,edi
003c0122 ff15a4380e00    call    dword ptr ds:[0E38A4h]
003c0128 8bcb            mov     ecx,ebx
003c012a ff15a4380e00    call    dword ptr ds:[0E38A4h]
003c0130 8b4dec          mov     ecx,dword ptr [ebp-14h]
003c0133 ff15a4380e00    call    dword ptr ds:[0E38A4h]

[ebp-14h], the spill for target5, is now not longer a source of pointers. The target5 local is now dead.

003c0139 e8926f525b      call    mscorlib_ni+0x2570d0 (5b8e70d0)
003c013e 8bc8            mov     ecx,eax
003c0140 8b1530200303    mov     edx,dword ptr ds:[3032030h]
003c0146 8b01            mov     eax,dword ptr [ecx]
003c0148 8b403c          mov     eax,dword ptr [eax+3Ch]
003c014b ff5010          call    dword ptr [eax+10h]
003c014e 8d65f4          lea     esp,[ebp-0Ch]
003c0151 5b              pop     ebx
003c0152 5e              pop     esi
003c0153 5f              pop     edi
003c0154 5d              pop     ebp
003c0155 c3              ret

From the liveness information, we can see that locations holding the various locals are only tracked for some of the method scope. Hence there is no need to set such locals to null as the liveness information means that the values they hold are not going to be traced by the garbage collector anyway.

Advertisements
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s