如何理解“使用”的JITed代码在C#中处理异常 [英] How to understand the JITed code for "using" with exception handling in C#

查看:172
本文介绍了如何理解“使用”的JITed代码在C#中处理异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在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 
正常JIT生成代码
SimpleConsole.Program.UsingClass()
开始000007fe87d30120,大小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\SimpleCo nsole \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

//我可以理解上面的代码(无异常抛出)。

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字节ptr [rax],0
000007fe`87d30190 488b4d08 mov rcx,qword ptr [rbp + 8]
000007fe`87d30194 4c8bda mov r11,rdx
000007fe`87d30197 ff12 call qword pt r [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管理noinlining
{
//方法从RVA开始0x23bc
//代码大小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
} //结束.try
终于
{
IL_000e:ldloc.0
IL_000f:brfalse.s IL_0017

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

IL_0017:endfinally
} // end handler

IL_0018:ret
} //方法结束程序:: UsingClass $ b但是我不认为它有帮助,因为IL几乎把所有的东西都放在C#里,就像完整的$ $ code> try ... finally
语句。我想要理解的是本地代码如何处理这里的异常。

解决方案

抖动比以前更容易看到拆卸窗口。一个,它生成一个描述本地变量的生命周期和存储的表。对于垃圾收集器非常重要,它需要该表来查找对象引用。



它会为异常生成展开表。哪个具有非常所需的属性,它使得尝试声明是免费的。编写具有异常处理代码的零成本,无需输入代码即可进入try块。所以你在拆装中看不到任何东西。没有简单的方法来从调试器中找到该表。一个很好的描述他们在这里


I've written a very simple class in 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

I've dumped the native code after JIT with WinDBG for it:

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

I could understand the code without exception (commented above), but how the code works when an exception was thrown? How the code goes into the code below the comment?

Update:

Some people think we should start from IL so I pasted the code below:

.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

But I don't think it helps since IL almost keeps everything here in C# like the complete try...finally statement. What I want to understand is how the native code handles exception here.

解决方案

The jitter does a lot more than what's ever visible from the Disassembly window. For one, it generates a table that describes the lifetime and storage of local variables. Very important for the garbage collector, it needs that table to find object references.

And it generates an unwind table for exceptions. Which has a very desirable property, it makes a try statement free. There is zero cost in writing code that has exception handling, no code is required to enter a try block. So you don't see any in the dissassembly. There is no easy way to locate that table from the debugger. A pretty decent description of them is here.

这篇关于如何理解“使用”的JITed代码在C#中处理异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆