【问题标题】:How to understand the JITed code for "using" with exception handling in C#如何理解 JITed 代码以在 C# 中“使用”异常处理
【发布时间】:2013-03-20 17:48:25
【问题描述】:

我用 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

我已经在 J​​IT 之后使用 WinDBG 转储了本机代码:

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 语句一样。这里我想了解的是原生代码是如何处理异常的。

【问题讨论】:

  • 您是在问Try...Finally 块在装配级别如何工作?
  • @GregC 我只是想了解更多关于 JITed 代码的信息。
  • @keyboardP 我认为这会有所帮助。

标签: c# .net exception native jit


【解决方案1】:

抖动比“反汇编”窗口中可见的要多得多。一方面,它生成一个描述局部变量的生命周期和存储的表。对于垃圾收集器来说非常重要,它需要该表来查找对象引用。

它会为异常生成一个展开表。它具有 非常 理想的属性,它使 try 声明免费。编写具有异常处理的代码的成本为零,无需代码即可进入 try 块。所以你在反汇编中看不到任何东西。没有简单的方法可以从调试器中找到该表。对它们的一个相当不错的描述is here

【讨论】:

  • try 语句很便宜但不是很免费 - 带有异常处理表的方法不适合 JIT 内联。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-08
    • 2019-10-30
    • 1970-01-01
    • 2011-11-11
    • 1970-01-01
    • 2012-05-10
    • 2011-06-05
    • 1970-01-01
    相关资源
    最近更新 更多