2013-03-19 52 views
4

我用C#编写一个非常简单的类:如何理解JIT编译的代码在C#中的“使用”与异常处理

class DisposableClass : IDisposable { 
    public void Dispose() { } 
} 


static void UsingClass() {     // line 31 
    using (var dc = new DisposableClass()) { // line 32 
     DoSomething(dc);      // line 33 
    }           // line 34 
}            // line 35 

我使用WinDbg转储的本地代码JIT后它:

0:000> !u 000007fe87d30120 
Normal JIT generated code 
SimpleConsole.Program.UsingClass() 
Begin 000007fe87d30120, size 80 

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 32: 
>>> 000007fe`87d30120 55    push rbp 
000007fe`87d30121 4883ec30  sub  rsp,30h 
000007fe`87d30125 488d6c2420  lea  rbp,[rsp+20h] 
000007fe`87d3012a 48896500  mov  qword ptr [rbp],rsp 
000007fe`87d3012e 48c7450800000000 mov  qword ptr [rbp+8],0 
000007fe`87d30136 488d0d6b47eeff lea  rcx,[000007fe`87c148a8] 
000007fe`87d3013d e8fe24665f  call clr+0x2640 (000007fe`e7392640) (JitHelp: CORINFO_HELP_NEWSFAST) // new DisposableClass() 
000007fe`87d30142 48894508  mov  qword ptr [rbp+8],rax 

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 33: 
000007fe`87d30146 488b4d08  mov  rcx,qword ptr [rbp+8] 
000007fe`87d3014a e8d1beeeff  call 000007fe`87c1c020 (SimpleConsole.Program.DoSomething(System.Object), mdToken: 0000000006000012) 
000007fe`87d3014f 90    nop 
000007fe`87d30150 90    nop 

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 35: 
000007fe`87d30151 488b4d08  mov  rcx,qword ptr [rbp+8] 
000007fe`87d30155 4c8d1dc4feeeff lea  r11,[000007fe`87c20020] 
000007fe`87d3015c ff15befeeeff call qword ptr [000007fe`87c20020] // Call Dispose() 
000007fe`87d30162 90    nop 
000007fe`87d30163 488d6510  lea  rsp,[rbp+10h] 
000007fe`87d30167 5d    pop  rbp 
000007fe`87d30168 c3    ret 

// I could understand the code above (without exception thrown). 

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 32: 
000007fe`87d30169 55    push rbp 
000007fe`87d3016a 4883ec30  sub  rsp,30h 
000007fe`87d3016e 488b6920  mov  rbp,qword ptr [rcx+20h] 
000007fe`87d30172 48896c2420  mov  qword ptr [rsp+20h],rbp 
000007fe`87d30177 488d6d20  lea  rbp,[rbp+20h] 

c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 35: 
000007fe`87d3017b 48837d0800  cmp  qword ptr [rbp+8],0 
000007fe`87d30180 7417   je  000007fe`87d30199 
000007fe`87d30182 488d1597feeeff lea  rdx,[000007fe`87c20020] 
000007fe`87d30189 488b4508  mov  rax,qword ptr [rbp+8] 
000007fe`87d3018d 803800   cmp  byte ptr [rax],0 
000007fe`87d30190 488b4d08  mov  rcx,qword ptr [rbp+8] 
000007fe`87d30194 4c8bda   mov  r11,rdx 
000007fe`87d30197 ff12   call qword ptr [rdx] 
000007fe`87d30199 90    nop 
000007fe`87d3019a 4883c430  add  rsp,30h 
000007fe`87d3019e 5d    pop  rbp 
000007fe`87d3019f c3    ret 

我能理解无一例外的代码(以上注释),但如何当一个异常被抛出代码工作?代码如何进入评论下面的代码?

更新:

有些人认为我们应该从IL开始,所以我粘贴下面的代码:

.method private hidebysig static 
    void UsingClass() cil managed noinlining 
{ 
    // Method begins at RVA 0x23bc 
    // Code size 25 (0x19) 
    .maxstack 1 
    .locals init (
     [0] class SimpleConsole.DisposableClass dc 
    ) 

    IL_0000: newobj instance void SimpleConsole.DisposableClass::.ctor() 
    IL_0005: stloc.0 
    .try 
    { 
     IL_0006: ldloc.0 
     IL_0007: call void SimpleConsole.Program::DoSomething(object) 
     IL_000c: leave.s IL_0018 
    } // end .try 
    finally 
    { 
     IL_000e: ldloc.0 
     IL_000f: brfalse.s IL_0017 

     IL_0011: ldloc.0 
     IL_0012: callvirt instance void [mscorlib]System.IDisposable::Dispose() 

     IL_0017: endfinally 
    } // end handler 

    IL_0018: ret 
} // end of method Program::UsingClass 

但我不认为它可以帮助因为IL几乎在C#中保持这里的一切就像完整的try...finally声明一样。我想了解的是本地代码在这里如何处理异常。

+0

你是问一个'Try ... Finally'块如何在汇编级工作? – keyboardP 2013-03-19 14:14:11

+0

@GregC我只是想知道更多关于JIT代码。 – 2013-03-19 14:19:02

+0

@keyboardP我认为这会有所帮助。 – 2013-03-19 14:23:36

回答

6

抖动比从反汇编窗口中看到的要多得多。首先,它会生成一个描述本地变量生命周期和存储的表格。垃圾收集器非常重要,它需要该表来查找对象引用。

并且它为异常生成展开表。其中有一个非常理想的属性,它使尝试声明免费。编写具有异常处理的代码的代价是零,不需要代码就可以输入try块。所以你不会在拆卸中看到任何东西。从调试器中找到该表没有简单的方法。他们相当不错的描述is here

+0

'try'语句很便宜,但不是很自由 - 带有异常处理表的方法不适用于JIT内联。 – 2013-03-19 14:54:29

+0

+1为一个很好的参考 – GregC 2013-03-19 15:06:28

+0

感谢您的参考。 – 2013-03-20 09:07:42

5

实际上,您正在研究在没有任何异常抛出时.NET中异常处理的开销很小的原因。在发生异常时,框架使用正常代码流路径之外的各种算法(具体取决于抛出何种类型的异常),该算法将调用或显式设置指向该方法的异常处理块的指令指针。