CLR 如何知道静态字段是否已经初始化?

How does the CLR know if a static field has already been initialized?

有一件事我一直想知道 static fields / constructors

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

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

CLR 维护一个 table 所有已加载的类型及其初始化状态。如果 A 正在使用 B 的静态字段,那么 CLR 知道 A 正在使用 B 并且在初始化 A 时它也会初始化 B.因此,不会在每次访问时都检查依赖项是否已初始化。通过类型的依赖图来确保。

如果您对实现细节感兴趣,可以在创建 类 实例时查看 CoreCLR and its usageDomainLocalModuleIsClassInitialized 方法。

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

The static class initialized the first time one of it's fields is referenced, that is easy.

不,没那么简单。忽略方法调用,static class 必须在它的一个字段第一次被访问之前初始化,如果它没有被标记为 beforefieldinit。如果标记为beforefieldinit,则可以早于此进行初始化。 (乔恩双向飞碟 an article with lots more information about beforefieldinit。)

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

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

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