Как CLR узнает, было ли уже инициализировано статическое поле?

Есть одна вещь, которая меня всегда интересовала в отношении static fields / constructors.

static class инициализируется при первой ссылке на одно из его полей, это легко.

Но как CLR узнает, что это первый раз?


person Patrick Braunstorfer    schedule 11.03.2016    source источник


Ответы (2)


CLR поддерживает таблицу всех загруженных типов и состояния их инициализации. Если A использует статическое поле B, CLR знает, что A использует B, и при инициализации A она также инициализирует B. Таким образом, проверка инициализации зависимостей не выполняется при каждом доступе. Это обеспечивается графом зависимостей типов.

Если вас интересуют подробности реализации, вы можете взглянуть на метод IsClassInitialized для DomainLocalModule в CoreCLR и его использование при создании экземпляров классов.

Надеюсь, это ответит на ваш вопрос.

person Kolja    schedule 11.03.2016
comment
Да, большое спасибо. Хотя я должен признать, что это кажется дорогим, чтобы проверять каждый раз при доступе к полю (например, по сравнению с С++) - person Patrick Braunstorfer; 11.03.2016
comment
Чтобы было ясно, эта проверка не выполняется при каждом доступе к классу. Состояние загрузки класса проверяется при загрузке используемого класса. Если ваш класс A использует статическое свойство B.foo, то при использовании A проверяются его зависимости. Поэтому, когда A инициализируется, B тоже будет инициализирован. доступ к B.foo не дает таких проверок. - person Kolja; 11.03.2016
comment
Я внес некоторые изменения в ответ, чтобы сделать его более понятным, когда происходит эта проверка. - person Kolja; 11.03.2016

static class инициализируется при первой ссылке на одно из его полей, это легко.

Нет, это не так просто. Игнорируя вызовы методов, класс static должен быть инициализирован непосредственно перед первым доступом к одному из его полей, если он не помечен beforefieldinit. Если он помечен beforefieldinit, его можно инициализировать раньше. (У Джона Скита есть статья с большим количеством дополнительной информации о beforefieldinit.)

Как это влияет на проверку инициализации класса? Поскольку CLR использует JIT-компиляцию, она проверяет инициализацию класса при JIT-компиляции метода. Если класс помечен beforefieldinit и он еще не инициализирован, JIT-компилятор сразу его инициализирует. Затем он фактически компилирует метод, где он может предположить, что класс уже инициализирован, поэтому никаких проверок не требуется.

Без beforefieldinit, если класс еще не инициализирован, JIT-компилятор должен выдать код, проверяющий инициализацию перед каждым потенциальным доступом к первому полю. Но если класс уже инициализирован и выполняется JIT-компиляция другого метода, JIT-компилятору больше не нужно выдавать там проверки.

В некоторых случаях это может негативно сказаться на производительности. Из вышеизложенного понятно, что для защиты от этого нужно убедиться, что проблемные классы помечены beforefieldinit. И способ сделать это из C# - не иметь конструктора static, использовать только инициализаторы полей static.

person svick    schedule 11.03.2016
comment
Интересно. Я предполагаю, что это было бы особенно проблематично, если бы доступ к потенциальному первому полю был дико разбросан по всему решению и сильно зависел бы от внешнего ввода. Однозначно спасибо за развернутый ответ. - person Patrick Braunstorfer; 15.03.2016