Main 和 静态构造函数 到底谁先执行?

最近被问到一个很有意思的问题,到底是 Main函数 先执行还是 静态构造函数 先执行?参考如下代码:

class Program{static Program(){Console.WriteLine("我是 静态构造 函数!");}static void Main(string[] args){Console.WriteLine("我是 main 方法!");}}

想要知道答案很简单,把代码跑起来哈 😄😄😄。

4b9e709d6187059407db55c6d91fe5e7.png

从输出结果看,静态函数先执行,这就很困惑了,一直都被教育,执行流应该是 非托管层 -> 托管层 -> Main入口 这条思路,咋就先执行 静态构造函数 了呢?

一:寻找答案

如果你会玩 windbg 并且能看懂一些汇编,那就非常简单了,可以通过 windbg 对 Main 函数下一个断点。

0:000> !bpmd Program.cs:14
0:000> g
JITTED ConsoleApp3!ConsoleApp3.Program.Main(System.String[])
Setting breakpoint: bp 04C40450 [ConsoleApp3.Program.Main(System.String[])]
Breakpoint 0 hit
ChangeEngineState
eax=04c40450 ebx=0057f354 ecx=02669e7c edx=7897aa50 esi=0057f300 edi=001940b0
eip=04c40450 esp=0057f2ec ebp=0057f2f4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
04c40450 55              push    ebp
0:000> !U 04c40450
Normal JIT generated code
ConsoleApp3.Program.Main(System.String[])
ilAddr is 04DD205E pImport is 035F9978
Begin 04C40450, size 36D:\net5\ConsoleApp4\ConsoleApp3\Program.cs @ 14:
>>> 04c40450 55              push    ebp
04c40451 8bec            mov     ebp,esp
04c40453 50              push    eax
04c40454 894dfc          mov     dword ptr [ebp-4],ecx
04c40457 b9e0d9b004      mov     ecx,4B0D9E0h
04c4045c ba01000000      mov     edx,1
04c40461 e8fe2eb1fb      call    00753364 (JitHelp: CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE)
04c40466 833d38d9b00400  cmp     dword ptr ds:[4B0D938h],0
04c4046d 7405            je      04c40474
04c4046f e87cfcf473      call    coreclr!JIT_DbgIsJustMyCode (78b900f0)
04c40474 90              nopD:\net5\ConsoleApp4\ConsoleApp3\Program.cs @ 15:
04c40475 8b0d68206603    mov     ecx,dword ptr ds:[3662068h] ("")
04c4047b e8d0fcffff      call    04c40150 (System.Console.WriteLine(System.String), mdToken: 06000081)
04c40480 90              nopD:\net5\ConsoleApp4\ConsoleApp3\Program.cs @ 16:
04c40481 90              nop
04c40482 8be5            mov     esp,ebp
04c40484 5d              pop     ebp
04c40485 c3              ret

从输出信息看,一直被教育的 非托管层 -> 托管层 -> Main入口 这条链路是没有问题的,因为在执行 Console.WriteLine 之前有一条 call 00753364 (JitHelp: CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) 指令,接下来我们看看这条指令是干嘛的?

可以用 uf 00753364 反编译下这个方法,简化后的代码如下:

0:000> uf 00753364
00753364 89c8            mov     eax,ecx
00753366 f644101801      test    byte ptr [eax+edx+18h],1
0075336b 7401            je      0075336e  Branch0075336d c3              ret  Branch0075336e 55              push    ebp
0075336f 89e5            mov     ebp,esp
00753371 52              push    edx
00753372 e869c22f78      call    coreclr!JIT_GetSharedNonGCStaticBase_Portable (78a4f5e0)
00753377 5a              pop     edx
00753378 5d              pop     ebp
00753379 c3              ret0:000> uf 78a4f5e0
coreclr!JIT_GetSharedNonGCStaticBase_Portable [d:\a\_work\1\s\src\vm\jithelpers.cpp @ 1340]:1340 78a4f5e0 f644111801      test    byte ptr [ecx+edx+18h],11346 78a4f5e5 0f8403000000    je      coreclr!JIT_GetSharedNonGCStaticBase_Helper (78a4f5ee)  Branchcoreclr!JIT_GetSharedNonGCStaticBase_Portable+0xb [d:\a\_work\1\s\src\vm\jithelpers.cpp @ 1348]:1348 78a4f5eb 8bc1            mov     eax,ecx1355 78a4f5ed c3              retcoreclr!JIT_GetSharedNonGCStaticBase_Helper [d:\a\_work\1\s\src\vm\jithelpers.cpp @ 1399]:1399 78a4f5ee 6a5c            push    5Ch1399 78a4f5f0 b8aa9da978      mov     eax,offset coreclr!Debugger::GenericHijackFunc+0x1116a (78a99daa)1399 78a4f5f5 e87b110200      call    coreclr!_EH_prolog3_catch (78a70775)1399 78a4f5fa 8bfa            mov     edi,edx1399 78a4f5fc 8bf1            mov     esi,ecx1399 78a4f5fe 33db            xor     ebx,ebx...

从输出信息看,代码流程大概是: CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE -> JIT_GetSharedNonGCStaticBase_Portable -> JIT_GetSharedNonGCStaticBase_Helper -> ..., 调用链还是蛮深的,我就不往下追了,不过可以从名字上看,大概就是和 初始化静态变量 有关,那如何更好的验证呢?很简单,我们可以在 静态构造函数 上埋一个断点,然后查看调用链,看看上面有没有 JIT_GetSharedNonGCStaticBase_Helper 方法即可。

0:000> !bpmd Program.cs:9
0:000> g
JITTED ConsoleApp3!ConsoleApp3.Program..cctor()
Setting breakpoint: bp 04C50498 [ConsoleApp3.Program..cctor()]
Breakpoint 0 hit
ChangeEngineState
eax=04c50498 ebx=0057e848 ecx=0250ebb0 edx=0250ebb0 esi=0057e7c0 edi=00000000
eip=04c50498 esp=0057e7ac ebp=0057e7b4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
04c50498 55              push    ebp
0:000> k# ChildEBP RetAddr      
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 0057e7b4 78a6a0ef     0x4c50498
01 0057e7b4 78a433bf     coreclr!CallDescrWorkerInternal+0x34 [D:\a\_work\1\s\src\vm\i386\asmhelpers.asm @ 615] 
02 0057e7e0 78a74422     coreclr!CallDescrWorkerWithHandler+0x62 [d:\a\_work\1\s\src\vm\callhelpers.cpp @ 72] 
03 0057e824 789a6e3b     coreclr!DispatchCallDebuggerWrapper+0x25 [d:\a\_work\1\s\src\vm\callhelpers.cpp @ 163] 
04 0057e874 789a6d70     coreclr!DispatchCallSimple+0x63 [d:\a\_work\1\s\src\vm\callhelpers.cpp @ 230] 
05 0057e8e8 789a5bed     coreclr!MethodTable::RunClassInitEx+0xb4 [d:\a\_work\1\s\src\vm\methodtable.cpp @ 3377] 
06 0057f230 789a593e     coreclr!MethodTable::DoRunClassInitThrowing+0x249 [d:\a\_work\1\s\src\vm\methodtable.cpp @ 3607] 
07 0057f250 78a4f648     coreclr!MethodTable::CheckRunClassInitThrowing+0xb9 [d:\a\_work\1\s\src\vm\methodtable.cpp @ 3762] 
08 0057f2d0 007e3377     coreclr!JIT_GetSharedNonGCStaticBase_Helper+0x5a [d:\a\_work\1\s\src\vm\jithelpers.cpp @ 1410] 
09 0057f2dc 04c50466     0x7e3377
0a 0057f2e8 78a6a0ef     0x4c50466
0b 0057f2f4 789eff91     coreclr!CallDescrWorkerInternal+0x34 [D:\a\_work\1\s\src\vm\i386\asmhelpers.asm @ 615] 
0c (Inline) --------     coreclr!CallDescrWorkerWithHandler+0x55 [d:\a\_work\1\s\src\vm\callhelpers.cpp @ 70] 
0d 0057f380 78a2c895     coreclr!MethodDescCallSite::CallTargetWorker+0x167 [d:\a\_work\1\s\src\vm\callhelpers.cpp @ 604] 
0e (Inline) --------     coreclr!MethodDescCallSite::Call+0x11 [d:\a\_work\1\s\src\vm\callhelpers.h @ 468] 
0f 0057f4fc 78a2e292     coreclr!RunMain+0x1e4 [d:\a\_work\1\s\src\vm\assembly.cpp @ 1558] 
...

上面的 08 号栈帧就是调用链上的 coreclr!JIT_GetSharedNonGCStaticBase_Helper 方法,那就说明找对了, 00 号栈帧就是当前的静态构造函数。

二:总结

综合上面的信息可知,你所看到的 静态构造函数 先执行是一种假象,只不过是在 Main 函数中先做了一个 call 调用而已,本质上还是 Main 在先,cctor 在后,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/288287.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c 正则提取html,c – 正则表达式以获取HTML表格内容

确实没有可能的正则表达式解决方案适用于任意数量的表数据,并将每个单元格放入单独的后向引用中.这是因为通过反向引用,您需要为要创建的每个backref创建一个独特的开放式窗口,并且您不知道自己有多少个单元格.使用一种或另一种循环来提取数据没有任何问题.例如,在最后一个,在P…

(五)python3 只需3小时带你轻松入门—— 逻辑运算符

如果if判断中存在多个表达式判断,需要使用逻辑运算符。 例如有一个变量a,需要判断是否在1到5之间,那么则需要判断a是否大于1且a小于5。这个时候需要使用and逻辑运算符进行判断。 and python中使用and判断左右两边表达式是同时正确&#xff0c…

(四)python3 只需3小时带你轻松入门—— 流程控制

缩进 python中使用缩进代表代码块;每一个块代表一个层次(分支),每个单独的分支是独立的,但是从整体逻辑上又是相融的;就像一本书一样,每个知识点是独立的,但是每个知识点组成了这本书…

ASP.NET Core 正确获取查询字符串参数

前言有网友在交流群中询问,如何获取查询字符串参数:默认情况下,ASP.NET Core 的模型绑定以键值对的形式从 HTTP 请求中的以下列表中指示的顺序扫描源并获取数据:表单域请求正文路由数据查询字符串参数上传的文件因此,不…

(三)python3 只需3小时带你轻松入门—— 变量的简单运算

变量运算 在编程时,需要对数据进行计算,计算的形式不限于:字符串拼接、相加减、相乘除及普遍的数学运算、剔除或指定剔除、添加或指定添加等。 在python中: *表示乘法/表示除法表示加法-表示减法 a,b10,11 cog3 j2 print(ca)#加…

使用XMLConfiguration解析xml,properties等相应信息

org.apache.commons.configuration.XMLConfiguration; Apache Common-Configuration工具可以从Properties文件,XML文件,JNDI,JDBC数据源,System Properties,Applet parameters,Servlet Parameters等读取相应信息 使用步骤 前提,引入commons-c…

C#语法糖系列 —— 第二篇:聊聊 ref,in 修饰符底层玩法

自从 C# 7.3 放开 ref 之后,这玩法就太花哨了,也让 C# 这门语言变得越来越多范式,越来越重,这篇我们就来聊聊 ref,本质上来说 ref 的放开就是把 C/C 指针的那一套又拿回来了,而且还封装成一套自己的玩法&am…

(二)python3 只需3小时带你轻松入门——基本变量

输入 在程序运行过程中,数据从外部流向程序,称为输入。在程序运行过程中,接收用户从键盘上键入值,可以使用input()函数。 input("请输入你要输入的值:")在输入值时,一般是代表接下来的运算需要使用到用户所…

Fiddler之为什么我没有抓到网络请求的js链接

1 问题 我开了Fiddler,没有抓到js的连接请求,因为我需要替换js文件,我以为我是没有开启抓起https的连接,但是的确开启了。 2 原因 浏览器里面有缓存,部分js文件不会再进行请求。 2 解决办法 在浏览器页面按下F12,然…

html5一年四季的变化,家乡四季的变化作文(精选5篇)

家乡四季的变化作文(精选5篇)在日常生活或是工作学习中,大家或多或少都会接触过作文吧,写作文是培养人们的观察力、联想力、想象力、思考力和记忆力的重要手段。那么你有了解过作文吗?下面是小编精心整理的家乡四季的变化作文(精选5篇)&#…

ThinkPHP多次重复提交问题的根源

2019独角兽企业重金招聘Python工程师标准>>> 由于用户刷新网页,导致页面所有变量回归初始空值 $code_session2 session(code2); $code2 I(code2);if($code2 ! $code_session2) {session(code2, $code2);M(dati)->data($data)->add();} 解决方法是…

(一)python3 只需3小时带你轻松入门—— 编程尝试

什么是函数? 在编程中,函数和通常数学中的函数概念并不完全相同;编程中的函数更接近于一个写好的工具,在开发某些功能时,所需要到该函数,就把该函数拿过来使用。 输出/显示 运行python程序时显示指定的文本…

HTTP 笔记与总结(7)HTTP 缓存(配合 Apache 服务器)

在网络上&#xff0c;有一些缓存服务器&#xff0c;另外浏览器自身也有缓存功能。 例如&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title> </head> <body&…

httpcilent绕过证书

2019独角兽企业重金招聘Python工程师标准>>> 对接其他公司接口&#xff0c;测试环境没有问题&#xff0c;生产环境出现https证书认证的问题&#xff0c; 网上搜了许久才发现一个&#xff0c;链接&#xff1a;http://pan.baidu.com/s/1dEDSmY1 密码&#xff1a;dpsb …

(六)python3 只需3小时带你轻松入门——循环

for循环 使用循环可以重复执行某些代码&#xff0c;可以方便程序编写&#xff1b;但是不记效率的使用循环会使程序运行效率降低。 range 使用range()函数可以生成多个连续整数的range对象(这个概念后面会说)。基本格式&#xff1a;range(end)其中end是结尾数。range(10)则会生…

linux下查看mysql的当前连接情况

为什么80%的码农都做不了架构师&#xff1f;>>> 首先需要登录到mysql中。 总共有三个命令&#xff1a; 1、status mysql> status--------------mysql Ver 14.14 Distrib 5.5.30, for Linux (x86_64) using readline 5.1Connection id: 96Current data…

用计算机画图软件画画教程,电脑画图软件有什么使用技巧,电脑画图软件教程...

一、第一步是点击右下角的“开始”图标。在开头上方&#xff0c;出现一个对话框&#xff0c;您找到“所有应用程序”&#xff0c;您右键单击鼠标&#xff0c;在出现的对话框中&#xff0c;您找到“附件”&#xff0c;您左键单击鼠标&#xff0c;在“附件”的右侧出现一个对话框…

(七)python3 只需3小时带你轻松入门——List与dict

List列表 python中最基本的数据结构之一。序列&#xff08;或者说集合&#xff09;中的每个元素都分配一个数字用来表示它的位置&#xff08;索引&#xff09;&#xff0c;第一个索引是0&#xff0c;第二个索引是1&#xff0c;依此类推。 索引 索引最大值不能超过当前对象的最…

龙芯推出兼容IE的龙芯浏览器解决方案,全面支持ActiveX插件等应用类型

近期&#xff0c;龙芯中科推出了兼容IE的浏览器解决方案&#xff0c;可全面支持采用ActiveX插件等IE时代技术开发的网页应用。众所周知&#xff0c;IE浏览器因年代久远&#xff0c;已逐步退出历史舞台。但在我国行业和个人应用中&#xff0c;仍存在着大量基于IE浏览器开发的网页…

DPS软件做MK(Mann-Kendall)突变监测分析方法(附DPS 7.05软件下载地址)

Mann-Kendall是一种非参数统计检验方法&#xff0c;具有样本不遵从某一特定分布&#xff0c;不受个别异常值干扰&#xff0c;能够客观地表征样本序列整体变化趋势等优点。 虽然DPS软件具有强大的统计分析和数据可视化功能&#xff0c;但是相关的示例和教程却本博文演示Mann-Ke…