【问题标题】:How does the CLR know if a static field has already been initialized?CLR 如何知道静态字段是否已经初始化?
【发布时间】:2016-03-11 11:07:29
【问题描述】:

关于static fields / constructors,我一直想知道一件事。

static class 在第一次引用它的一个字段时初始化,这很容易。

但是 CLR 怎么知道这是第一次呢?

【问题讨论】:

    标签: c# .net clr cil static-initialization


    【解决方案1】:

    CLR 维护一个表,其中包含已加载的所有类型及其初始化状态。如果A 正在使用B 的静态字段,则CLR 知道A 正在使用B,并且在初始化A 时,它还将初始化B。因此,不会在每次访问时检查依赖项是否已初始化。通过类型的依赖图来保证。

    如果您对实现细节感兴趣,可以查看CoreCLRDomainLocalModuleIsClassInitialized 方法,以及创建类实例时的usage

    我希望这能回答你的问题。

    【讨论】:

    • 是的,非常感谢。虽然我不得不承认,每次访问字段时检查似乎很昂贵(例如与 C++ 相比)
    • 需要明确的是,不会在每次访问类时执行此检查。加载使用类时检查类加载状态。如果您的 A 类使用 B.foo 的静态属性,则在使用 A 时检查其依赖关系。因此,当 A 被初始化时,B 也将被初始化。对 B.foo 的访问不会产生这样的检查。
    • 我已经对答案进行了一些编辑,以便在进行此检查时更加清楚。
    【解决方案2】:

    static class 在第一次引用其中一个字段时初始化,这很容易。

    不,没那么简单。忽略方法调用,如果没有标记beforefieldinit,则必须在第一次访问其中一个字段之前初始化static 类。如果它被标记为beforefieldinit,则可以比它更早地初始化。 (Jon Skeet 有an article with lots more information about beforefieldinit。)

    这对何时检查类初始化有何影响?由于 CLR 使用 JIT 编译,因此它会在 JIT 编译方法时检查类初始化。如果该类标记为beforefieldinit 并且尚未初始化,则 JIT 编译器会立即对其进行初始化。然后它实际编译该方法,它可以假定该类已经被初始化,因此不需要检查。

    如果没有beforefieldinit,如果类还没有被初始化,JIT 编译器必须在每次潜在的第一个字段访问之前发出检查初始化的代码。但如果该类已经初始化并且正在 JIT 编译另一个方法,则 JIT 编译器不必再在那里发出检查。

    在某些情况下,这可能会对性能产生负面影响。从上面可以清楚地看出,为了防止这种情况,您需要确保有问题的类被标记为beforefieldinit。从 C# 中做到这一点的方法是没有 static 构造函数,只使用 static 字段初始化器。

    【讨论】:

    • 有趣。如果潜在的第一个字段 Access 散布在整个解决方案中并且严重依赖于外部输入,我想这将是特别有问题的。非常感谢您的深入回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    相关资源
    最近更新 更多