《CLR Via C#》学习笔记(五)【静态构造函数的性能】

在上一篇《CLR Via C# 学习笔记(4) 方法 构造函数 》中讲到了一些静态构造函数方面的知识,现在也回顾一下,大概总结如下:

  1. 静态构造函数是私有的(private) ,而且不能人为去修改访问修饰符。
  2. 静态构造函数不应该去调用基类的静态构造函数,因为静态字段不会被继承到子类。
  3. 静态构造函数在一个类型中有且仅有一个,并且是无参的。
  4. 静态构造函数中只能初始化静态字段。

从上面的第一点可以知道静态构造函数都是private的,所以不能显示区进行调用,关于JIT何时会去生成调用静态构造函数的代码。存在着两种说法。通常被称为PreciseBeforeFieldInit

  1. Precise方式JIT编译器生成调用的时机:首次创建类型的代码之前;访问类的非继承字段或成员代码之前。
  2. BeforeFieldInit方式JIT编译器生成调用的时机:在访问费继承静态字段代码之前。

这两种方式的主要区别就是选择调用静态构造函数的时机是否是确定的,Precise方式CLR会在确定的时刻调用静态构造函数,而BeforeFieldInit方式CLR可以自由选择调用静态构造函数的时机,利用这一点,CLR可以根据类型是否在程序域中加载来选择静态构造函数的调用次数,以便能生成执行更快的代码。

下面来看来个类分别展现了这两种方式

public classUserPrecise
{
    public static string_name = "内联赋值:oec2003";
    staticUserPrecise()
    {
        _name = "构造函数赋值:oec2003";
    }
}
public classUserBeforeFieldInit
{
    public static string_name = "内联赋值:oec2003";
}

通过IL代码可以看出在UserBeforeFieldInit 的元数据上有BeforeFieldInit的标记,如下图:

2010-12-29_173428

既然上面提到BeforeFieldInit方式CLR可以选择调用构造函数的次数从而来生成执行更快的代码,下面就写一段测试代码来看看究竟怎样。

public sealed class Program
{
    static void Main(string[] args)
    {
        const Int32 iterations = 1000 * 1000 * 1000;
        Test1(iterations);
        Test2(iterations);
    }

    private static void Test1(Int32 iterations)
    {
        Stopwatch sw = Stopwatch.StartNew();
        for (Int32 i = 0; i < iterations; i++)
        {
            UserBeforeFieldInit._name = "oec2003";
        }
        Console.WriteLine("Test1-UserBeforeFieldInit 用时:" + sw.Elapsed);

        sw = Stopwatch.StartNew();
        for (Int32 j = 0; j < iterations; j++)
        {
            UserPrecise._name = "oec2003";
        }
        Console.WriteLine("Test1-UserPrecise 用时:" + sw.Elapsed);
    }

    private static void Test2(Int32 iterations)
    {
        Stopwatch sw = Stopwatch.StartNew();
        for (Int32 i = 0; i < iterations; i++)
        {
            UserBeforeFieldInit._name = "oec2003";
        }
        Console.WriteLine("Test2-UserBeforeFieldInit 用时:" + sw.Elapsed);

        sw = Stopwatch.StartNew();
        for (Int32 j = 0; j < iterations; j++)
        {
            UserPrecise._name = "oec2003";
        }
        Console.WriteLine("Test2-UserPrecise 用时:" + sw.Elapsed);
    }
}

public class UserBeforeFieldInit
{
    public static string _name;
}

public class UserPrecise
{
    public static string _name;

    static UserPrecise()
    {
        _name = "oec2003";
    }
}

测试结果如下:
在这里插入图片描述

从上面结果来看,BeforeFieldInit方式的执行速度还是要快很多,但为什么第二次执行时,两种方式的速度差不多呢?因为经过第一次执行后JIT编译器知道类型的构造器已经被调用了,所以第二次执行时不会显示对构造函数进行调用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值