Пример кода ниже произошел естественно. Внезапно мой код стал очень неприятным FatalExecutionEngineError
исключением. Я потратил хорошие 30 минут, пытаясь изолировать и минимизировать образец виновника. Скомпилируйте это, используя Visual Studio 2012 в качестве консольного приложения:
class A<T>
{
static A() { }
public A() { string.Format("{0}", string.Empty); }
}
class B
{
static void Main() { new A<object>(); }
}
Должно выдать эту ошибку на .NET Framework 4 и 4.5:
Это известная ошибка, в чем причина и что я могу сделать, чтобы ее устранить? Моя текущая работа - не использовать string.Empty
, но я лаю не на том дереве? Изменение чего-либо в этом коде делает его работу ожидаемой - например, удаление пустого статического конструктора A
или изменение параметра типа с object
на на int
.
Я попробовал этот код на своем ноутбуке, и он не жаловался. Тем не менее, я попробовал свое основное приложение, и оно также зависло на ноутбуке. Я, должно быть, что-то испортил, уменьшая проблему, я посмотрю, смогу ли я выяснить, что это было.
Мой ноутбук завис с тем же кодом, что и выше, с фреймворком 4.0, но основной сбой даже с 4.5. Обе системы используют VS'12 с последними обновлениями (июль?).
Больше информации :
- IL-код (скомпилированный Debug / Any CPU / 4.0 / VS2010 (разве IDE не имеет значения?)): Http://codepad.org/boZDd98E
- Не видел VS 2010 с 4.0. Не вылетает с / без оптимизаций, другой целевой процессор, отладчик подключен / не подключен и т. Д. - Тим Медора
- Сбои в 2010 году, если я использую AnyCPU, нормально в x86. Сбои в Visual Studio 2010 с пакетом обновления 1 (SP1) при использовании Platform Target = AnyCPU, но хорошо при использовании Platform Target = x86. На этой машине также установлен VS2012RC, поэтому 4.5, возможно, выполняет замену на месте. Используйте AnyCPU и TargetPlatform = 3.5, тогда он не падает, поэтому выглядит как регрессия в Framework.- colinsmith
- Невозможно воспроизвести на x86, x64 или AnyCPU в VS2010 с 4.0. - Фудзи
- Только для x64, (2012rc, Fx4.5) - Хенк Холтерман
- VS2012 RC на Win8 RP. Изначально не видел этот MDA при таргетинге на .NET 4.5. При переключении на таргетирование .NET 4.0 появился MDA. Затем после переключения на .NET 4.5 MDA остается. - Уэйн
Ответы:
Это тоже не полный ответ, но у меня есть несколько идей.Я полагаю, что нашел такое хорошее объяснение, какое мы найдем без ответа от кого-либо из команды .NET JIT.
ОБНОВИТЬ
Я посмотрел немного глубже, и я думаю, что я нашел источник проблемы. По-видимому, это вызвано сочетанием ошибки в логике инициализации типа JIT и изменением в компиляторе C #, основанном на предположении, что JIT работает должным образом. Я думаю, что ошибка JIT существовала в .NET 4.0, но была обнаружена изменением компилятора для .NET 4.5.
Я не думаю, что
beforefieldinit
это единственная проблема здесь. Я думаю, что это проще, чем это.Тип
System.String
в mscorlib.dll из .NET 4.0 содержит статический конструктор:В .NET 4.5 версии mscorlib.dll
String.cctor
(статический конструктор) явно отсутствует:В обеих версиях
String
тип украшенbeforefieldinit
:Я попытался создать тип, который скомпилировался бы в IL аналогично (чтобы у него были статические поля, но без статического конструктора
.cctor
), но я не смог этого сделать. Все эти типы имеют.cctor
метод в IL:Я предполагаю, что две вещи изменились между .NET 4.0 и 4.5:
Во-первых: EE был изменен так, чтобы он автоматически инициализировался
String.Empty
из неуправляемого кода. Это изменение, вероятно, было сделано для .NET 4.0.Второе: компилятор изменился так, что он не генерировал статический конструктор для строки, зная, что
String.Empty
он будет назначен с неуправляемой стороны. Похоже, это изменение было сделано для .NET 4.5.Похоже, что EE не назначает
String.Empty
достаточно быстро по некоторым путям оптимизации. Изменения, внесенные в компилятор (или любые изменения, внесенные дляString.cctor
исчезновения), предполагали, что EE выполнит это назначение до выполнения любого пользовательского кода, но кажется, что EE не выполняет это назначение до того, какString.Empty
оно используется в методах базовых классов reified ссылочного типа.Наконец, я считаю, что ошибка указывает на более глубокую проблему в логике инициализации типа JIT. Похоже, что изменение в компиляторе является особым случаем
System.String
, но я сомневаюсь, что JIT сделал особый случай здесьSystem.String
.оригинал
Прежде всего, WOW Люди BCL стали очень креативными с некоторыми оптимизациями производительности. Многие из
String
методов теперь выполняются с использованием статического кешируемогоStringBuilder
объекта Thread .Некоторое время я следовал этому примеру, но
StringBuilder
не использовался вTrim
пути кода, поэтому решил, что это не может быть статической проблемой Thread.Я думаю, что нашел странное проявление той же ошибки, хотя.
Этот код завершается с нарушением прав доступа:
Однако, если вы раскомментировать
//new A<int>(out s);
вMain
том код работает просто отлично. На самом деле, еслиA
преобразование выполняется с любым ссылочным типом, программа завершается сбоем, но еслиA
преобразование выполняется с любым типом значения, то код не завершается ошибкой. Также, если вы закомментируетеA
статический конструктор, код никогда не завершится с ошибкой. Покопавшись вTrim
иFormat
, становится ясно, что проблема заключается в том, чтоLength
он встроен, и что в этих образцах вышеString
тип не был инициализирован. В частности, внутри телаA
конструктораstring.Empty
не правильно назначено, хотя внутри телаMain
,string.Empty
назначается правильно.Мне удивительно, что инициализация типа
String
так или иначе зависит от того,A
является ли тип типизированным. Моя единственная теория состоит в том, что существует некоторый оптимизирующий путь кода JIT для инициализации универсального типа, который является общим для всех типов, и что этот путь делает предположения о ссылочных типах BCL («специальные типы?») И их состоянии. Краткий обзор других классов BCL сpublic static
полями показывает, что в основном все они реализуют статический конструктор (даже классы с пустыми конструкторами и без данных, например,System.DBNull
иSystem.Empty
. Типы значений BCL сpublic static
полями, по-видимому, не реализуют статический конструктор (System.IntPtr
например) Кажется, это указывает на то, что JIT делает некоторые предположения об инициализации ссылочного типа BCL.К вашему сведению, код JIT для двух версий:
A<object>.ctor(out string)
:A<int32>.ctor(out string)
:Остальная часть кода (
Main
) идентична между двумя версиями.РЕДАКТИРОВАТЬ
Кроме того, IL из двух версий идентичен, за исключением вызова
A.ctor
inB.Main()
, где IL для первой версии содержит:против
во-вторых.
Следует также отметить, что код JITed для
A<int>.ctor(out string)
: такой же, как и в неуниверсальной версии.источник
string.Empty
""
typeof(string).GetField("Empty").SetValue(null, "Hello world!"); Console.WriteLine(string.Empty);
дает разные результаты в .NET 4.0 по сравнению с .NET 4.5. Связано ли это изменение с изменением, описанным выше? Как .NET 4.5 может технически игнорировать меня при изменении значения поля? Может быть, я должен задать новый вопрос по этому поводу?Я сильно подозреваю, что это вызвано этой оптимизацией (связанной с
BeforeFieldInit
) в .NET 4.0.Если я правильно помню:
Когда вы объявляете статический конструктор явно,
beforefieldinit
он генерируется, сообщая среде выполнения, что статический конструктор должен быть запущен до любого доступа к статическому члену .Моя догадка:
Я предполагаю , что они какие - то образом испортили этот факт на 64 JITer, так что , когда различный типа по члену статического доступа из класса , чьи собственные статический конструктор уже запущены, оно как - то скачет работает (или выполняется в неправильном порядке) Статический конструктор - и, следовательно, вызывает сбой. ( Вероятно, вы не получите исключение нулевого указателя потому что оно не инициализировано нулем.)
Я не запускал ваш код, так что эта часть может быть неправильной - но если бы мне пришлось сделать еще одно предположение, я бы сказал, что это может быть что-то
string.Format
(илиConsole.WriteLine
, что похоже) необходимо для внутреннего доступа, который вызывает сбой, например, возможно локалите о связанном классе , который нуждается в явной статическую конструкции.Опять же, я не проверял это, но это мое лучшее предположение о данных.
Не стесняйтесь проверить мою гипотезу и дайте мне знать, как это происходит.
источник
B
нет статического конструктора, и не возникает, когдаA
используется тип значения. Я думаю, что это немного сложнее.B
наличие статического конструктора не имеет большого значения. Так какA
имеет статический ctor, среда выполнения портит порядок, в котором она запускается, по сравнению с некоторым классом, связанным с локалью, в каком-то другом пространстве имен. Так что это поле еще не инициализировано. Однако, если вы создаете экземплярA
с типом значения, то это может быть второй проход во время выполненияA
(например, CLR уже предварительно создал его со ссылочным типом в качестве оптимизации), поэтому порядок срабатывает, когда он запускается во второй раз. ,beforefieldinit
оптимизация является основной причиной. Возможно, что некоторые из фактических объяснений отличаются от того, что я упоминал, но основная причина, вероятно, та же самая.A<object>.ctor()
.Наблюдение, но DotPeek показывает декомпилированную строку.
Если я объявлю свой собственный
Empty
таким же образом, за исключением без атрибута, я больше не получу MDA:источник
""
решает это.