代码如下,问在release版本中,函数get_Length执行几次?(凸老头,显摆还不给答案)

static int get_Length(string s)
{
return s.Length;
}

static void Test(string s)
{
for (var i = 0; i < get_Length(s); i++)
{
Console.WriteLine(s[i]);
}
}

static void Main()
{
var s
= new String('a', 10);
Test(s);
Console.Read();
}

 

 

解析:在VS里设置断点察看,get_Length函数察看是11次,但是,这到底对不对呢? 我们要透过现象看本质,废话少说,上工具-〉Windbg

  首先,我们先在windbg里面为get_Length设置断点。

       〉.load sos

       〉!bpmd Learn_Test Learn.Test.Program.get_Length

断点添加成功。

  

执行程序,〉 g  

 

   

 

程序执行结束。。。咦,为什么断点处没有执行,有猫腻,继续分析......  

 

加载jit

 〉sxe ld:mscorjit

 〉g

 Test函数处设置断点  

 

〉!bpmd Learn_Test Learn.Test.Program.Test  

 

执行到断点处

〉g 

 

  

 

察看Test函数信息  

〉!name2ee Learn_Test Learn.Test.Program.Test  

察看Test函数jit CODE

〉!u  000007ff00180170

 0:000> !u 000007ff00180170
Normal JIT generated code
Learn.Test.Program.Test(System.String)
Begin 000007ff00180170, size 3f
>>> 000007ff`00180170 53              push    rbx
000007ff`00180171 56              push    rsi
000007ff`00180172 57              push    rdi
000007ff`00180173 4883ec20        sub     rsp,20h
000007ff`00180177 488bf9          mov     rdi,rcx
000007ff`0018017a 33db            xor     ebx,ebx
000007ff`0018017c 8b770c          mov     esi,dword ptr [rdi+0Ch]
000007ff`0018017f 90              nop

 //-----------------------------------------------------------------------------------------------------
000007ff`00180180 3bde            cmp     ebx,esi
000007ff`00180182 7d1c            jge     000007ff`001801a0
000007ff`00180184 8b470c          mov     eax,dword ptr [rdi+0Ch]
000007ff`00180187 3bd8            cmp     ebx,eax
000007ff`00180189 731e            jae     000007ff`001801a9
000007ff`0018018b 4863cb          movsxd  rcx,ebx
000007ff`0018018e 668b4c4f10      mov     cx,word ptr [rdi+rcx*2+10h]
000007ff`00180193 e8980867f8      call    mscorlib_ni+0x930a30 (000007fe`f87f0a30) (System.Console.WriteLine(Char), mdToken: 060007c0)
000007ff`00180198 83c301          add     ebx,1
000007ff`0018019b ebe3            jmp     000007ff`00180180
000007ff`0018019d 0f1f00          nop     dword ptr [rax]

 //-----------------------------------------------------------------------------------------------------
000007ff`001801a0 4883c420        add     rsp,20h
000007ff`001801a4 5f              pop     rdi
000007ff`001801a5 5e              pop     rsi
000007ff`001801a6 5b              pop     rbx
000007ff`001801a7 f3c3            rep ret
000007ff`001801a9 e8b2a14df9      call    mscorwks!JIT_RngChkFail (000007fe`f965a360)
000007ff`001801ae 90              nop

 需要注意的是中间那段,可以看出,ebx相当于for循环里的i,字符串的长度取自 [rdi+0Ch],并没有调用get_Lengt函数。

 我们可以来看一下.net中字符串的存储情况,在Main函数设置断点,察看寄存器得到s的地址指针。

 0:000> !do 23dc740
Name: System.String
MethodTable: 000007fef882ec90
EEClass: 000007fef843b038
Size: 46(0x2e) bytes
 (C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: aaaaaaaaaa
Fields:
                            MT    Field          Offset                 Type VT     Attr            Value Name
000007fef8835f00  4000096        8         System.Int32  1 instance               11 m_arrayLength
000007fef8835f00  4000097        c         System.Int32  1 instance               10 m_stringLength
000007fef88306d8  4000098       10          System.Char  1 instance               61 m_firstChar
000007fef882ec90  4000099       20        System.String  0   shared           static Empty
                                 >> Domain:Value  000000000028cad0:00000000023d1308 <<
000007fef8830588  400009a       28        System.Char[]  0   shared           static WhitespaceChars
                                 >> Domain:Value  000000000028cad0:00000000023d1a80 <<  

 

在这里可以看到,offset c 处就是字符串的length, 在32位机器上应该是offset 8

 

 

.NET CLR STRING 内存结构:

 【4字节方法表指针(64位机器为8字节)】【4字节附加字段】【长度】【字符串内容】

  对于这个 get_Length 方法,编译器肯定试图做inline 处理,接着二次优化会发现目标具有固定性,因为在 .net 中 string 是不可修改的。因此 main 中 string.ctor 后已经直接返回了字符串的指针,那么直接将 eax + c 就可以获得字符串长度,将这个作为 "参数" 提交给 test 方法就可以了。因此 JIT 根本不打算编译 get_Length,也无需做内联处理。(技术支持:雨老大) 

posted on 2010-01-04 14:19  Jeremy Ma  阅读(312)  评论(1)    收藏  举报