我的编程空间,编程开发者的网络收藏夹
学习永远不晚

.Net7函数Ctor与CCtor使用及区别详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

.Net7函数Ctor与CCtor使用及区别详解

楔子

有小伙伴被面试官问到这个问题,本篇彻底解析下这个问题。

为了彻底点,注意本篇是最底层的.Net 7 RC CLR运行模型(汇编)为基础进行全局剖析,局部业务分析。

如有疏漏,请斧正。

目的非手段

这两个函数比较特殊的存在,.Ctor是非静态默认实例化。.CCtor是静态默认实例化。这两个函数伴随着.Net任何对象的实例化都自动存在于这个对象当中。

跟踪.CCtor可以在全局静态对象下断点,观察它的里面运行。跟踪.Ctor可以通过!name2ee模块 模块.类名..Ctor找到JITTED Code Address,观察它的运行。正如本段题所说,这只是手段,不是目的。所以下面看目的。

.Ctor目的

先来看下非静态默认构造函数.Ctor。上一段代码:

    internal class Program
    {
        public class ABC
        {
        }
        static void Main(string[] args)
        {
            ABC abc = new ABC();
            Console.ReadLine();
        }
    }

直接给它反编译:

00007FFDF2FA03B0 55                   push        rbp  
00007FFDF2FA03B1 48 83 EC 40          sub         rsp,40h  
00007FFDF2FA03B5 48 8D 6C 24 40       lea         rbp,[rsp+40h]  
00007FFDF2FA03BA C5 D8 57 E4          vxorps      xmm4,xmm4,xmm4  
00007FFDF2FA03BE C5 FA 7F 65 E8       vmovdqu     xmmword ptr [rbp-18h],xmm4  
00007FFDF2FA03C3 33 C0                xor         eax,eax  
00007FFDF2FA03C5 48 89 45 F8          mov         qword ptr [rbp-8],rax  
00007FFDF2FA03C9 48 89 4D 10          mov         qword ptr [rbp+10h],rcx  
00007FFDF2FA03CD 83 3D BC E9 19 00 00 cmp         dword ptr [7FFDF313ED90h],0  
00007FFDF2FA03D4 74 05                je          00007FFDF2FA03DB  
00007FFDF2FA03D6 E8 B5 BF 79 5E       call        JIT_DbgIsJustMyCode (07FFE5173C390h)  
00007FFDF2FA03DB 90                   nop  
00007FFDF2FA03DC 48 B9 30 F6 5B F3 FD 7F 00 00 mov         rcx,7FFDF35BF630h  
00007FFDF2FA03E6 E8 75 7C C1 5E       call        JIT_TrialAllocSFastMP_InlineGetThread (07FFE51BB8060h)  
00007FFDF2FA03EB 48 89 45 F0          mov         qword ptr [rbp-10h],rax  
00007FFDF2FA03EF 48 8B 4D F0          mov         rcx,qword ptr [rbp-10h]  
// 这个地方是调用了.Ctor
00007FFDF2FA03F3 FF 15 0F 8D 60 00    call        qword ptr [7FFDF35A9108h]  
00007FFDF2FA03F9 48 8B 45 F0          mov         rax,qword ptr [rbp-10h]  
00007FFDF2FA03FD 48 89 45 F8          mov         qword ptr [rbp-8],rax  
00007FFDF2FA0401 FF 15 A9 93 60 00    call        qword ptr [7FFDF35A97B0h]  
00007FFDF2FA0407 48 89 45 E8          mov         qword ptr [rbp-18h],rax  
00007FFDF2FA040B 90                   nop  
00007FFDF2FA040C 90                   nop  
00007FFDF2FA040D 48 83 C4 40          add         rsp,40h  
00007FFDF2FA0411 5D                   pop         rbp  
00007FFDF2FA0412 C3                   ret  

调用.Ctor的地方注释了下,如果直接进入会调用到PrecodeFixupThunk。所以这里需要在PreStubWorker下断点。一路跟踪下去发现这个.Ctor是利用预备的IL代码,让RyuJIt对它进行一个编译

.Ctor调用堆栈:

    coreclr.dll!MethodDesc::JitCompileCodeLocked 行 952	C++
 	coreclr.dll!MethodDesc::JitCompileCodeLockedEventWrapper 行 823	C++
 	coreclr.dll!MethodDesc::JitCompileCode 行 763	C++
 	coreclr.dll!MethodDesc::PrepareILBasedCode 行 426	C++
 	coreclr.dll!MethodDesc::PrepareCode 行 323	C++
 	coreclr.dll!CodeVersionManager::PublishVersionableCodeIfNecessary 行 1698	C++
 	coreclr.dll!MethodDesc::DoPrestub 行 2109	C++
 	coreclr.dll!PreStubWorker 行 1938	
 	coreclr.dll!ThePreStub(

JitCompileCodeLocked里面调用了UnsafeJitFunction为止,因为后面都是RyuJit的复杂编译过程,此处不述。

我们来看下UnsafeJitFunction返回的pCode地址处的汇编代码:

00007FFDF2F80430 55                   push        rbp  
00007FFDF2F80431 57                   push        rdi  
00007FFDF2F80432 48 83 EC 28          sub         rsp,28h  
00007FFDF2F80436 48 8D 6C 24 30       lea         rbp,[rsp+30h]  
00007FFDF2F8043B 48 89 4D 10          mov         qword ptr [rbp+10h],rcx  
00007FFDF2F8043F 83 3D 4A E9 19 00 00 cmp         dword ptr [7FFDF311ED90h],0  
00007FFDF2F80446 74 05                je          00007FFDF2F8044D  
00007FFDF2F80448 E8 43 BF 7B 5E       call        JIT_DbgIsJustMyCode (07FFE5173C390h)  
00007FFDF2F8044D 48 8B 4D 10          mov         rcx,qword ptr [rbp+10h]  
00007FFDF2F80451 FF 15 D9 0B E5 FF    call        qword ptr [7FFDF2DD1030h]  
00007FFDF2F80457 90                   nop  
00007FFDF2F80458 90                   nop  
00007FFDF2F80459 48 83 C4 28          add         rsp,28h  
00007FFDF2F8045D 5F                   pop         rdi  
00007FFDF2F8045E 5D                   pop         rbp  
00007FFDF2F8045F C3                   ret  

它里面就调用了一个Call,也就是这句话:

call  qword ptr [7FFDF2DD1030h]  

这个十六进制的7FFDF2DD1030h是个啥呢?继续跟进下:0x00007FFDF2DD1030 00007ffe50357230它里面包含了一个地址00007ffe50357230看下这个地址的汇编代码:

00007FFE50357230 C3   ret 

它直接返回了。

所以这得出了一个什么结论呢?也就是说在当前这个例子中,.Ctor啥都没做。

.CCtor目的

来看下静态的默认构造函数干了些啥。先上代码:

    internal class Program
    {
        static string a ="abcd";
        static void Main(string[] args)
        {
            string i = a;
            Console.WriteLine(a);
            Console.ReadLine();
        }
    }

同样反编译下:

00007FFDF01903B0 55                   push        rbp  
00007FFDF01903B1 57                   push        rdi  
00007FFDF01903B2 48 83 EC 28          sub         rsp,28h  
00007FFDF01903B6 48 8D 6C 24 30       lea         rbp,[rsp+30h]  
00007FFDF01903BB 33 C0                xor         eax,eax  
00007FFDF01903BD 48 89 45 F0          mov         qword ptr [rbp-10h],rax  
00007FFDF01903C1 48 89 4D 10          mov         qword ptr [rbp+10h],rcx  
00007FFDF01903C5 83 3D C4 E9 19 00 00 cmp         dword ptr [7FFDF032ED90h],0  
00007FFDF01903CC 74 05                je          00007FFDF01903D3  
00007FFDF01903CE E8 BD BF 7D 5E       call        JIT_DbgIsJustMyCode (07FFE4E96C390h)  
00007FFDF01903D3 90                   nop  
00007FFDF01903D4 48 B9 60 EF 32 F0 FD 7F 00 00 mov         rcx,7FFDF032EF60h  
00007FFDF01903DE BA 04 00 00 00       mov         edx,4  
// 可以看到这个 string 静态对象并没有调用.CCtor。
// 那是否说明上面的说法不对呢?注意看,他实际调用了
// JIT_GetSharedNonGCStaticBase_SingleAppDomain,
// 而这个就是关键所在
00007FFDF01903E3 E8 48 7E C5 5E       call        JIT_GetSharedNonGCStaticBase_SingleAppDomain (07FFE4EDE8230h)  
00007FFDF01903E8 8B 0D AA EB 19 00    mov         ecx,dword ptr [7FFDF032EF98h]  
00007FFDF01903EE FF 15 7C 94 60 00    call        qword ptr [7FFDF0799870h]  
00007FFDF01903F4 90                   nop  
00007FFDF01903F5 FF 15 9D 93 60 00    call        qword ptr [7FFDF0799798h]  
00007FFDF01903FB 48 89 45 F0          mov         qword ptr [rbp-10h],rax  
00007FFDF01903FF 90                   nop  
00007FFDF0190400 90                   nop  
00007FFDF0190401 48 83 C4 28          add         rsp,28h  
00007FFDF0190405 5F                   pop         rdi  
00007FFDF0190406 5D                   pop         rbp  
00007FFDF0190407 C3                   ret  
00007FFDF0190408 19 06                sbb         dword ptr [rsi],eax  

看这段代码上面的注释,这段代码里面并没有.CCtor被调用的痕迹。而它的奥秘在JIT_GetSharedNonGCStaticBase_SingleAppDomain函数里面。

JIT_GetSharedNonGCStaticBase_SingleAppDomain又调用了JIT_GetSharedNonGCStaticBase_Helper

看下堆栈

>	coreclr.dll!MethodTable::RunClassInitEx 行 3591	C++
 	coreclr.dll!MethodTable::DoRunClassInitThrowing 行 3792	C++
 	coreclr.dll!MethodTable::CheckRunClassInitThrowing 行 3929	C++
 	coreclr.dll!JIT_GetSharedNonGCStaticBase_Helper 行 1401	C++

函数RunClassInitEx代码如下:

BOOL MethodTable::RunClassInitEx(OBJECTREF *pThrowable)
{
            //为了方便观看 此处省略部分代码
            PCODE pCctorCode = pCanonMT->GetSlot(pCanonMT->GetClassConstructorSlot());
            //为了方便观看 此处省略部分代码
            PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pCctorCode);
            DECLARE_ARGHOLDER_ARRAY(args, 0);
            CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE;
            CALL_MANAGED_METHOD_NORET(args);
	    //为了方便观看 此处省略部分代码

变量pCctorCode就是.CCtor的函数头地址。而后面的一堆的宏定义实际上是调用了函数DispatchCallSimple,而DispatchCallSimple又调用了CallDescrWorkerWithHandler然后又调用了PrecodeFixupThunk下面调用了PreStubWorker

PreStubWorker通过call rax命令跳转到调用的函数的函数头地址,比如本例的.CCtor函数头的地址。

00007FFE8BB289C0 E8 DB FE 8F FF       call        PreStubWorker (07FFE8B4288A0h)  
00007FFE8BB289C5 66 0F 6F 44 24 20    movdqa      xmm0,xmmword ptr [rsp+20h]  
00007FFE8BB289CB 66 0F 6F 4C 24 30    movdqa      xmm1,xmmword ptr [rsp+30h]  
00007FFE8BB289D1 66 0F 6F 54 24 40    movdqa      xmm2,xmmword ptr [rsp+40h]  
00007FFE8BB289D7 66 0F 6F 5C 24 50    movdqa      xmm3,xmmword ptr [rsp+50h]  
00007FFE8BB289DD 48 8B 8C 24 B0 00 00 00 mov         rcx,qword ptr [rsp+0B0h]  
00007FFE8BB289E5 48 8B 94 24 B8 00 00 00 mov         rdx,qword ptr [rsp+0B8h]  
00007FFE8BB289ED 4C 8B 84 24 C0 00 00 00 mov         r8,qword ptr [rsp+0C0h]  
00007FFE8BB289F5 4C 8B 8C 24 C8 00 00 00 mov         r9,qword ptr [rsp+0C8h]  
00007FFE8BB289FD 48 83 C4 68          add         rsp,68h  
00007FFE8BB28A01 5F                   pop         rdi  
00007FFE8BB28A02 5E                   pop         rsi  
00007FFE8BB28A03 5B                   pop         rbx  
00007FFE8BB28A04 5D                   pop         rbp  
00007FFE8BB28A05 41 5C                pop         r12  
00007FFE8BB28A07 41 5D                pop         r13  
00007FFE8BB28A09 41 5E                pop         r14  
00007FFE8BB28A0B 41 5F                pop         r15  
// 这个rax 就是 .CCtor的函数头的地址
00007FFE8BB28A0D 48 FF E0             jmp  rax  

jmp rax跳转到了如下:

00007FFE2CFE8888 FF 25 FA 0F 00 00 jmp qword ptr [7FFE2CFE9888h]

7FFE2CFE9888h地址的值是00007FFE8A50C7A0

注意这句代码

static string a ="abcd";

它实际上被编译成了一个函数,当运行到.CCtor的时候,会调用它,然后对它进行赋值abcd

>>> 00007ffe`06ac29e0 55              push    rbp
00007ffe`06ac29e1 4883ec20        sub     rsp,20h
00007ffe`06ac29e5 488d6c2420      lea     rbp,[rsp+20h]
00007ffe`06ac29ea 833d9f410c0000  cmp     dword ptr [00007ffe`06b86b90],0
00007ffe`06ac29f1 7405            je      ConsoleApp3!ConsoleApp3.Program..cctor+0x18 (00007ffe`06ac29f8)
00007ffe`06ac29f3 e8e8a4cd5f      call    coreclr!JIT_DbgIsJustMyCode (00007ffe`6679cee0)
00007ffe`06ac29f8 48bad83000186c020000 mov rdx,26C180030D8h
00007ffe`06ac2a02 488b12          mov     rdx,qword ptr [rdx]
00007ffe`06ac2a05 48b9902e00186c020000 mov rcx,26C18002E90h
00007ffe`06ac2a0f e8fc85bb5f      call    coreclr!JIT_CheckedWriteBarrier (00007ffe`6667b010)
00007ffe`06ac2a14 90              nop
00007ffe`06ac2a15 4883c420        add     rsp,20h
00007ffe`06ac2a19 5d              pop     rbp
00007ffe`06ac2a1a c3              ret

JIT_CheckedWriteBarrier的原型如下:

extern "C" HCIMPL2_RAW(VOID, JIT_CheckedWriteBarrier, Object **dst, Object *ref)

很明显,他这就是把ref指向的object完整的传递给dst。也就是赋值给静态字符串a。寄存器rcx表示dstrdx表示ref。此处可以通过!dumpobj rdx来查被看对象。

那么总结下,.CCtor的作用就是把静态的全局变量对象进行一个初始化,这个结果也说明,静态全局变量不是在CLR初始化的时候初始化,而是在当前类的.CCtor里面初始化的。

以上就是.Net 7函数Ctor与CCtor使用及区别详解的详细内容,更多关于.Net 7函数Ctor CCtor的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

.Net7函数Ctor与CCtor使用及区别详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

.Net7函数Ctor与CCtor使用及区别详解

这篇文章主要为大家介绍了.Net7函数Ctor与CCtor使用及区别详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

java之scan.next()与scan.nextline()函数的使用及区别

这篇文章主要介绍了java之scan.next()与scan.nextline()函数的使用及区别,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-05-14

MySQL深入详解delete与Truncate及drop的使用区别

目录一、删除的内容deletetruncatedrop drop二、删除过程三、表和索引所占空间四、应用范围五、删除程度六、处理速度七、语句类型:八、语法区别九、总结deletetruncatedrop参考文章:链接一、删除的内容del
2022-07-25

JS中call(),apply(),bind()函数的区别与用法详解

这篇文章主要介绍了JS中call(),apply(),bind()函数的高级用法详解,需要的朋友可以参考下
2022-12-10

详解C语言中strcpy函数与memcpy函数的区别与实现

这篇文章主要介绍了C语言中字符串拷贝函数(strcpy)与内存拷贝函数(memcpy)的不同及内存拷贝函数的模拟实现,感兴趣的小伙伴可以跟随小编一起学习一下
2022-12-29

JavaScript箭头函数与普通函数的区别示例详解

这篇文章主要为大家介绍了JavaScript箭头函数与普通函数的区别示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

JavaScript函数防抖与函数节流的定义及使用详解

这篇文章主要为大家详细介绍一下JavaScript中函数防抖与函数节流的定义及使用,文中的示例代码讲解详细,对我们学习JS有一定帮助,需要的可以参考一下
2022-11-13

Oracle存储过程与函数的区别及应用场景解析

Oracle存储过程与函数的区别及应用场景解析在Oracle数据库中,存储过程和函数是两种常用的数据库对象,它们在实际的开发工作中扮演着重要的角色。本文将就Oracle存储过程与函数的区别进行详细比较,并结合具体的代码示例来说明它们的应用
Oracle存储过程与函数的区别及应用场景解析
2024-03-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录