1

我在 .NET 4.5 中编写了以下 C#:

public struct DisposableStruct : IDisposable {
    private readonly int _i, _j;

    public DisposableStruct(int i, int j) {
        _i = i;
        _j = j;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    void IDisposable.Dispose() {
        Console.WriteLine(_i + _j);
    }
}

internal class Program {

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static void Dispose<T>(ref T obj) where T : IDisposab
        obj.Dispose();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static void Dispose<T>(T obj) where T : IDisposable {
        obj.Dispose();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void Test() {
        var ds = new DisposableStruct(1, 2);
        Dispose(ref ds);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void TestNoRef() {
        var ds = new DisposableStruct(1, 2);
        Dispose(ds);
    }

    private static unsafe void Main() {
        Test();
        TestNoRef();

        Console.WriteLine(Process.GetCurrentProcess().Id);
        Console.ReadLine();
    }
}

暂停后我用打印的进程ID转储内存,但发现了一些奇怪的东西。这是本机代码Test

0:000> !u 000007fd85c30120 
Normal JIT generated code
SimpleConsole.Program.Test()
Begin 000007fd85c30120, size 40
*** WARNING: Unable to verify checksum for SimpleConsole.exe

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 40:
>>> 000007fd`85c30120 4883ec38        sub     rsp,38h
000007fd`85c30124 48c744242800000000 mov   qword ptr [rsp+28h],0
000007fd`85c3012d 48c744242000000000 mov   qword ptr [rsp+20h],0
000007fd`85c30136 488d442420      lea     rax,[rsp+20h]
000007fd`85c3013b 488d4c2428      lea     rcx,[rsp+28h]
000007fd`85c30140 c70001000000    mov     dword ptr [rax],1
000007fd`85c30146 c7400402000000  mov     dword ptr [rax+4],2
000007fd`85c3014d 488b442420      mov     rax,qword ptr [rsp+20h]
000007fd`85c30152 488901          mov     qword ptr [rcx],rax
000007fd`85c30155 e83ebfeeff      call    000007fd`85b1c098 (SimpleConsole.DisposableStruct.System.IDisposable.Dispose(), mdToken: 0000000006000017)
000007fd`85c3015a 90              nop
000007fd`85c3015b 4883c438        add     rsp,38h
000007fd`85c3015f c3              ret

这是方法的本机代码TestNoRef

0:000> !u 000007fd85c301b0 
Normal JIT generated code
SimpleConsole.Program.TestNoRef()
Begin 000007fd85c301b0, size 42

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 46:
>>> 000007fd`85c301b0 4883ec38        sub     rsp,38h
000007fd`85c301b4 48c744242800000000 mov   qword ptr [rsp+28h],0
000007fd`85c301bd 48c744242000000000 mov   qword ptr [rsp+20h],0
000007fd`85c301c6 488d442428      lea     rax,[rsp+28h]
000007fd`85c301cb c70001000000    mov     dword ptr [rax],1
000007fd`85c301d1 c7400402000000  mov     dword ptr [rax+4],2

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 47:
000007fd`85c301d8 488b442428      mov     rax,qword ptr [rsp+28h]
000007fd`85c301dd 4889442420      mov     qword ptr [rsp+20h],rax
000007fd`85c301e2 488d4c2420      lea     rcx,[rsp+20h]
000007fd`85c301e7 e894ffffff      call    000007fd`85c30180 (SimpleConsole.DisposableStruct.System.IDisposable.Dispose(), mdToken: 0000000006000017)
000007fd`85c301ec 90              nop
000007fd`85c301ed 4883c438        add     rsp,38h
000007fd`85c301f1 c3              ret

我们可以很容易地发现相同的入口地址Dispose是不同的:

  • 测试:000007fd 85b1c098
  • 测试编号:000007fd 85c30180

但只有地址TestNoRef是正确的:

0:000> !u 000007fd`85c30180
Normal JIT generated code
SimpleConsole.DisposableStruct.System.IDisposable.Dispose()
Begin 000007fd85c30180, size 19

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 22:
>>> 000007fd`85c30180 4883ec28        sub     rsp,28h
000007fd`85c30184 488bc1          mov     rax,rcx
000007fd`85c30187 8b08            mov     ecx,dword ptr [rax]
000007fd`85c30189 8b4004          mov     eax,dword ptr [rax+4]
000007fd`85c3018c 03c8            add     ecx,eax
000007fd`85c3018e e85d8dda5e      call    mscorlib_ni+0xce8ef0 (000007fd`e49d8ef0) (System.Console.WriteLine(Int32), mdToken: 000000000600098d)
000007fd`85c30193 90              nop
000007fd`85c30194 4883c428        add     rsp,28h
000007fd`85c30198 c3              ret

但是里面的那个是什么Test

0:000> !u 000007fd`85b1c098
Unmanaged code
000007fd`85b1c098 e89360765f      call    clr+0x2130 (000007fd`e5282130)
000007fd`85b1c09d 5e              pop     rsi
000007fd`85b1c09e 05009048b1      add     eax,0B1489000h
000007fd`85b1c0a3 85fd            test    ebp,edi
000007fd`85b1c0a5 07              ???
000007fd`85b1c0a6 0000            add     byte ptr [rax],al
000007fd`85b1c0a8 0000            add     byte ptr [rax],al
000007fd`85b1c0aa 0000            add     byte ptr [rax],al
000007fd`85b1c0ac 0000            add     byte ptr [rax],al
000007fd`85b1c0ae 0000            add     byte ptr [rax],al

程序的结果显然是正确的,但是本机代码对Test方法的表现如何?

4

1 回答 1

1

这个不同000007fd 85b1c098的地址实际上是 Jit 存根代码的地址,因为方法SimpleConsole.DisposableStruct.System.IDisposable.Dispose()之前没有经过 Jit 编译Test。存根代码在第一次调用时调用 Jit 编译器,并在编译后修补自身以调用已编译的方法。

这里的一些信息:http ://codeidol.com/csharp/net-framework/Inside-the-CLR/Just-In-Time-(JIT)-Compilation/

于 2013-03-29T20:24:12.973 回答