válido specification software significado programa medicina language español detectó common abreviatura c# clr

c# - specification - common language runtime detectó un programa no válido



¿Cuál es la causa de este FatalExecutionEngineError en.NET 4.5 beta? (3)

El código de ejemplo siguiente se produjo de forma natural. De repente, mi código es una excepción FatalExecutionEngineError muy desagradable. Pasé unos buenos 30 minutos tratando de aislar y minimizar la muestra culpable. Compila esto usando Visual Studio 2012 como una aplicación de consola:

class A<T> { static A() { } public A() { string.Format("{0}", string.Empty); } } class B { static void Main() { new A<object>(); } }

Debería producir este error en .NET framework 4 y 4.5:

¿Es este un error conocido, cuál es la causa y qué puedo hacer para mitigarlo? Mi trabajo actual es no usar string.Empty . string.Empty , pero ¿estoy ladrando en el árbol equivocado? Cambiar cualquier cosa sobre ese código lo hace funcionar como era de esperar, por ejemplo, eliminando el constructor estático vacío de A , o cambiando el parámetro de tipo de object a int .

Intenté este código en mi computadora portátil y no me quejé. Sin embargo, probé mi aplicación principal y también se colgó en la computadora portátil. Debo haber destruido algo al reducir el problema, voy a ver si puedo averiguar qué fue eso.

Mi computadora portátil se colgó con el mismo código que el anterior, con el framework 4.0, pero los principales bloqueos incluso con 4.5. Ambos sistemas están usando VS''12 con las últimas actualizaciones (¿julio?).

Más información :

  • Código IL (depuración compilada / cualquier CPU / 4.0 / VS2010 (¿no debería importar ese IDE?)): http://codepad.org/boZDd98E
  • No se ve VS 2010 con 4.0. No falla con / sin optimizaciones, CPU objetivo diferente, depurador conectado / no conectado, etc. - Tim Medora
  • Bloqueos en 2010 si uso AnyCPU, está bien en x86. Se bloquea en Visual Studio 2010 SP1, utilizando Platform Target = AnyCPU, pero está bien con Platform Target = x86. Esta máquina también tiene VS2012RC instalado, por lo que 4.5 posiblemente realice un reemplazo in situ. Use AnyCPU y TargetPlatform = 3.5 y luego no se cuelga, por lo que parece una regresión en el Framework.- colinsmith
  • No se puede reproducir en x86, x64 o AnyCPU en VS2010 con 4.0. - Fuji
  • Solo sucede para x64, (2012rc, Fx4.5) - Henk Holterman
  • VS2012 RC en Win8 RP. Inicialmente no se ve esta MDA cuando se orienta a .NET 4.5. Cuando se cambió a la orientación de .NET 4.0 apareció la MDA. Luego, después de volver a .NET 4.5, la MDA permanece. - Wayne

Sospecho fuertemente que esto es causado por esta optimización (relacionada con BeforeFieldInit ) en .NET 4.0.

Si recuerdo correctamente:

Cuando declara explícitamente un constructor estático, se emite beforefieldinit , indicando al tiempo de ejecución que el constructor estático debe ejecutarse antes de que cualquier miembro estático acceda .

Mi conjetura:

Supongo que de alguna manera arruinaron este hecho en x64 JITer, de modo que cuando se accede a un miembro estático de un tipo diferente desde una clase cuyo propio constructor estático ya se ha ejecutado, de alguna manera se salta la ejecución (o ejecuta en el orden incorrecto) del constructor estático - y por lo tanto causa un bloqueo. (No obtiene una excepción de puntero nulo, probablemente porque no tiene inicialización nula).

No he ejecutado su código, así que esta parte puede estar equivocada, pero si tuviera que hacer otra suposición, diría que podría ser algo de string.Format . El string.Format (o Console.WriteLine , que es similar) necesita acceder internamente. causando el bloqueo, tal como una clase relacionada con el entorno local que necesita una construcción estática explícita.

Una vez más, no lo he probado, pero es mi mejor estimación de los datos.

Siéntase libre de probar mi hipótesis y dejarme saber cómo va.


Una observación, pero DotPeek muestra la cadena decompilada. Vacío así:

/// <summary> /// Represents the empty string. This field is read-only. /// </summary> /// <filterpriority>1</filterpriority> [__DynamicallyInvokable] public static readonly string Empty; internal sealed class __DynamicallyInvokableAttribute : Attribute { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public __DynamicallyInvokableAttribute() { } }

Si declaro mi propio Empty la misma manera, excepto sin el atributo, ya no obtengo el MDA:

class A<T> { static readonly string Empty; static A() { } public A() { string.Format("{0}", Empty); } }


Esta tampoco es una respuesta completa, pero tengo algunas ideas.

Creo que he encontrado una buena explicación que encontraremos sin que alguien del equipo .NET JIT responda.

ACTUALIZAR

Miré un poco más profundo, y creo que he encontrado la fuente del problema. Parece ser causado por una combinación de un error en la lógica de inicialización de tipo JIT y un cambio en el compilador C # que se basa en la suposición de que el JIT funciona como se esperaba. Creo que el error JIT existía en .NET 4.0, pero fue descubierto por el cambio en el compilador de .NET 4.5.

No creo que beforefieldinit sea ​​el único problema aquí. Creo que es más simple que eso.

El tipo System.String en mscorlib.dll de .NET 4.0 contiene un constructor estático:

.method private hidebysig specialname rtspecialname static void .cctor() cil managed { // Code size 11 (0xb) .maxstack 8 IL_0000: ldstr "" IL_0005: stsfld string System.String::Empty IL_000a: ret } // end of method String::.cctor

En la versión .NET 4.5 de mscorlib.dll, String.cctor (el constructor estático) brilla por su ausencia:

..... Ningún constructor estático :( .....

En ambas versiones, el tipo String está adornado con beforefieldinit :

.class public auto ansi serializable sealed beforefieldinit System.String

Traté de crear un tipo que compilara IL de manera similar (para que tenga campos estáticos pero no constructor estático .cctor ), pero no pude hacerlo. Todos estos tipos tienen un método .cctor en IL:

public class MyString1 { public static MyString1 Empty = new MyString1(); } public class MyString2 { public static MyString2 Empty = new MyString2(); static MyString2() {} } public class MyString3 { public static MyString3 Empty; static MyString3() { Empty = new MyString3(); } }

Mi suposición es que dos cosas cambiaron entre .NET 4.0 y 4.5:

Primero: El EE fue cambiado para que automáticamente inicialice String.Empty del código no administrado. Este cambio probablemente se realizó para .NET 4.0.

Segundo: el compilador cambió para que no emitiera un constructor estático para la cadena, sabiendo que String.Empty se asignaría desde el lado no administrado. Este cambio parece haberse realizado para .NET 4.5.

Parece que EE no asigna String.Empty suficientemente pronto a lo largo de algunas rutas de optimización. El cambio realizado en el compilador (o lo que sea que haya cambiado para hacer que String.cctor desaparezca) esperaba que EE realizara esta asignación antes de que se ejecute cualquier código de usuario, pero parece que EE no hace esta asignación antes de que String.Empty se use en métodos de referencia escriba las clases genéricas reificadas.

Por último, creo que el error es indicativo de un problema más profundo en la lógica de inicialización de tipo JIT. Parece que el cambio en el compilador es un caso especial para System.String , pero dudo que el JIT haya creado un caso especial aquí para System.String .

Original

Antes que nada, WOW La gente de BCL se volvió muy creativa con algunas optimizaciones de rendimiento. Muchos de los métodos String ahora se realizan utilizando un objeto StringBuilder caché estático en caché.

Seguí esa pista por un tiempo, pero StringBuilder no se usa en la ruta del código Trim , así que decidí que no podía ser un problema estático de subprocesos.

Creo que encontré una extraña manifestación del mismo error.

Este código falla con una violación de acceso:

class A<T> { static A() { } public A(out string s) { s = string.Empty; } } class B { static void Main() { string s; new A<object>(out s); //new A<int>(out s); System.Console.WriteLine(s.Length); } }

Sin embargo, si descomenta //new A<int>(out s); en Main , el código funciona bien. De hecho, si A se reifica con cualquier tipo de referencia, el programa falla, pero si A se reifica con cualquier tipo de valor, entonces el código no falla. Además, si comenta el constructor estático de A, el código nunca falla. Después de profundizar en Trim and Format , está claro que el problema es que Length está siendo inline, y que en estas muestras arriba, el tipo String no se ha inicializado. En particular, dentro del cuerpo del constructor de A, string.Empty no se asigna correctamente, aunque dentro del cuerpo de Main , string.Empty se asigna correctamente.

Es sorprendente para mí que la inicialización de tipos de String alguna manera dependa de si A está reificado con un tipo de valor. Mi única teoría es que hay alguna ruta de optimización del código JIT para la inicialización genérica de tipos que se comparte entre todos los tipos, y que esa ruta hace suposiciones sobre los tipos de referencia BCL (¿tipos especiales?) Y su estado. Una mirada rápida a través de otras clases BCL con campos public static muestra que básicamente todos ellos implementan un constructor estático (incluso aquellos con constructores vacíos y sin datos, como System.DBNull y System.Empty . Los tipos de valores BCL con campos public static no parecen para implementar un constructor estático ( System.IntPtr , por ejemplo). Esto parece indicar que el JIT hace algunas suposiciones sobre la inicialización del tipo de referencia BCL.

FYI Aquí está el código JITed para las dos versiones:

A<object>.ctor(out string) :

public A(out string s) { 00000000 push rbx 00000001 sub rsp,20h 00000005 mov rbx,rdx 00000008 lea rdx,[FFEE38D0h] 0000000f mov rcx,qword ptr [rcx] 00000012 call 000000005F7AB4A0 s = string.Empty; 00000017 mov rdx,qword ptr [FFEE38D0h] 0000001e mov rcx,rbx 00000021 call 000000005F661180 00000026 nop 00000027 add rsp,20h 0000002b pop rbx 0000002c ret }

A<int32>.ctor(out string) :

public A(out string s) { 00000000 sub rsp,28h 00000004 mov rax,rdx s = string.Empty; 00000007 mov rdx,12353250h 00000011 mov rdx,qword ptr [rdx] 00000014 mov rcx,rax 00000017 call 000000005F691160 0000001c nop 0000001d add rsp,28h 00000021 ret }

El resto del código ( Main ) es idéntico entre las dos versiones.

EDITAR

Además, el IL de las dos versiones es idéntico, excepto por la llamada a A.ctor en B.Main() , donde el IL para la primera versión contiene:

newobj instance void class A`1<object>::.ctor(string&)

versus

... A`1<int32>...

en el segundo.

Otra cosa a tener en cuenta es que el código JITed para A<int>.ctor(out string) : es el mismo que en la versión no genérica.