当前位置: 首页 > news >正文

.Net中间语言BeforeFieldInit

[转载自------红黑联盟]

 

如下代码:

using System;
namespace BeforeFieldInit
{
    internal class Foo
    {
        Foo(){ Console.WriteLine("Foo 对象构造函数");}
        public static string Field = GetString("初始化 Foo 静态成员变量!");

        public static string GetString(string s){
            Console.WriteLine(s);
            return s;
        }
    }

    internal class FooStatic
    {
        static FooStatic(){ Console.WriteLine("FooStatic 类构造函数"); }
        FooStatic(){ Console.WriteLine("FooStatic 对象构造函数"); }

        public static string Field = GetString("初始化 FooStatic 静态成员变量!");
        public static string GetString(string s){
            Console.WriteLine(s);
            return s;
        }
    }

    class Program
    {
       static void Main(string[] args){
            Console.WriteLine("Main 开始 ...");

            Foo.GetString("手动调用 Foo.GetString() 方法!");
            //string info = Foo.Field;

            FooStatic.GetString("手动调用 FooStatic.GetString() 方法!");
            //string infoStatic = FooStatic.Field;

            Console.ReadLine();
        }
    }
}

  Foo 和FooStatic 唯一的不同就是FooStatic 有静态的类构造函数。执行上面的代码,输出如下:

如果把被注释的读取静态字段Field的两行代码打开,再编译运行,输出:

对比上面的区别,FooStatic 始终是延迟装载的,也就是只有类被首次使用时,类对象才被构造,其静态成员以及静态构造函数才被初始化执行,而Foo 类对象的初始化则交给CLR 来决定。
如果用IL Dasm.exe对比两个类生成的中间代码,可以看到只有一处不同:FooStatic 比Foo 少了一个特性:beforefieldinit。

也就是说静态构造函数抑制了beforefieldinit 特性,而该特性会影响对调用该类的时机。
C# 里面的静态构造函数,也称为类型构造器,类型初始化器,它是私有的,就是在上图中的.cctor : void()。CLR保证一个静态构造函数在每个AppDomain中只执行一次,而且这种执行是线程安全的,所以在静态构造函数中非常适合于单例模式的初始化(初始化静态字段等同于在静态构造函数中初始化,但不完全相同,因为显式定义静态构造函数会抑制beforefieldinit标志。)。
JIT编译器在编译一个方法时,会查看代码中引用了哪些类型,任何一个类型定义了静态构造函数,JIT编译器都会检查针对当前AppDomain,是否执行了这个静态构造函数。如果类型构造去没有执行,JIT编译器就会在生成的本地代码中添加对静态构造函数的一个调用,否则就不会添加,因为类型已经初始化。同时CLR还保证在执行本地代码中生成的静态构造函代码的线程安全。
根据上面的描述,我们知道JIT 必须决定是否生成类型静态构造函数代码,还须决定何时调用它。具体在何时调用有两中方式:
precise:JIT编译器可以刚好在创建类型的第一个实例之前,或刚好在访问类的一个非继承的字段或成员之前生产这个调用。
beforefieldinit:JIT编译器可以在首次访问一个静态字段或者一个静态/实例方法之前,或者创建类型的第一个实例之前,随便找一个时间生成调用。具体调用时机由CLR决定,它只保证访问成员之前会执行静态构造函数,但可能会提前很早就执行。

 

CLI specification (ECMA 335) 在 8.9.5 节中提到:
1. If marked BeforeFieldInit then the type's initializer method is executed at, or sometime before, first access to any static field defined for that type
2. If not marked BeforeFieldInit then that type's initializer method is executed at (i.e., is triggered by):
• first access to any static or instance field of that type, or
• first invocation of any static, instance or virtual method of that type
简单点说就是beforefieldinit可能会提前调用一个类型的静态构造函数,而precise模式是非要等到用时才调用类型的静态构造函数,它是严格的延迟装载。
beforefieldinit 是首选的(如果没有自定义静态构造函数,默认就是这种方式),因为它使CLR能够自由选择调用静态构造函数的时机,而CLR会尽可能利用这一点来生成运行得更快的代码。比如说在一个循环中调用单例(且包含首次调用),beforefieldinit方式可以让CLR决定在循环之前就调用静态构造函数来优化,而precise模式则只会在循环体中来调用静态构造函数,并在之后的调用会检测静态构造函数是否已被执行的标志位,这样效率稍低一些。在前面使用静态Field的情况下,beforefieldinit 方式下CLR也认为提前执行静态构造函数是更好的选择。
C# 的单例实现,可以利用 precise 延迟调用这一点来延迟对单例对象的构造(饿汗模式),从而带来一丁点的优化,但是在绝大部分情况下这一丁点的优化作用并不大!

转载于:https://www.cnblogs.com/ShiningWay/p/7226363.html

相关文章:

  • java学习第二阶段day7
  • 如何检测出定制服务器中预装的恶意软件?
  • Unity中使用ulua的个人经验总结
  • Spring获取bean
  • erlang远程调用示例
  • AAC头部格式,RTP打包格式
  • JNDI学习总结(3)——Tomcat下使用C3P0配置JNDI数据源
  • 51 Nod 1057 N的阶乘【Java大数乱搞】
  • 矛与盾——扫描器盲打对主动安全防护的启示
  • Linux运行级别的配置文件
  • Swift Strings and Characters
  • Munin监控的安装与配置
  • [转载]PhotoShop性能优化
  • [转]公钥,私钥和数字签名这样最好理解
  • $translatePartialLoader加载失败及解决方式
  • Apache的80端口被占用以及访问时报错403
  • ES6 学习笔记(一)let,const和解构赋值
  • HTML5新特性总结
  • Idea+maven+scala构建包并在spark on yarn 运行
  • JS字符串转数字方法总结
  • maven工程打包jar以及java jar命令的classpath使用
  • MD5加密原理解析及OC版原理实现
  • mongodb--安装和初步使用教程
  • vue-cli在webpack的配置文件探究
  • vuex 笔记整理
  • 创建一种深思熟虑的文化
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 从0到1:PostCSS 插件开发最佳实践
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 开源SQL-on-Hadoop系统一览
  • 如何设计一个比特币钱包服务
  • 深入浅出webpack学习(1)--核心概念
  • 使用docker-compose进行多节点部署
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (分类)KNN算法- 参数调优
  • (汇总)os模块以及shutil模块对文件的操作
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)母版页和相对路径
  • .NET Core 中的路径问题
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET企业级应用架构设计系列之开场白
  • .Net语言中的StringBuilder:入门到精通
  • [383] 赎金信 js
  • [AIGC] MySQL存储引擎详解
  • [Android] Implementation vs API dependency
  • [C#]OpenCvSharp结合yolov8-face实现L2CS-Net眼睛注视方向估计或者人脸朝向估计
  • [C#]winform部署yolov5-onnx模型
  • [DevOps云实践] 彻底删除AWS云资源
  • [Google Guava] 2.1-不可变集合