A peculiar .net compiler optimization issue
Recently, i found a strange issue when trouble shouting my project. The short story is like this: i wrote a library and tested it with NUnit, all things work fine, however, when it’s deployed into a real testing environment, a strange exception was thrown out, it seems that the static constructor caused this issue. So i tried to write a simpler demo to reproduce it. Fortunately, i caught it again.
here is the demo code:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“Optimization test…”);
ClassA.Initialize();
Console.WriteLine(“IN class B”);
ClassB inst = new ClassB();
Console.WriteLine(“Dingo!”);
Console.ReadKey();
}
}
class ClassA
{
private static bool initialized = false;
public static void Initialize()
{
if (initialized)
return;
Console.WriteLine(“Initialization ClassA…”);
initialized = true;
}
public static int SomeMethodOrProperty()
{
Console.WriteLine(“Invoking some static method or property in ClassA…”);
if (!initialized)
throw new ApplicationException(“ClassA is not initialized yet”);
return 10;
}
}
class ClassB
{
static int number = ClassA.SomeMethodOrProperty();
}
When i build the program in “Debug” mode, it works fine. And then i tried to build it in “Release” mode (Code optimization is turned on), it fails when invoking ClassA.SomeMethodOrProperty() in ClassB , a stranger issue is: when i tried to start this program from visual studio(F5 to debug the program in “Release” mode ), it also works. although it fails when pressing “Ctrl+F5″.
So i guess there must be something wrong when .Net is optimizing the code. then i tried to modify the class ClassB:
Case 1:
class ClassB
{
int number = ClassA.SomeMethodOrProperty();
}
it also works very well.
Case 2:
class ClassB
{
static int number = 0;
static ClassB()
{
number = ClassA.SomeMethodOrProperty();
}
}
it still works…seems the optimization trick is in the static constructor of ClassB
Case 3:
class ClassB
{
static int number = ClassA.SomeMethodOrProperty();
static ClassB()
{
}
}
Works again, unbelievable, huh?
although i don’t know the exact reason why .Net compiler works so differently when “ClassB” is organized slightly differently in code but identical in logic, I am sure the optimizer reset the static variables when invoking from ClassB.
Then i tried a bit further, run the original code (the “bad” one which may throw exception) in release mode, then I attach the process and debug it from visual studio. it works again even the release binary file is not recompiled. So i can conclude that the JIT compiler make the wrong optimization in runtime. but how and why? I don’t know it by now, i will dig this issue further.
An update:
Now i am very sure that it’s caused by .Net JIT code optimizer. When i add a ini file to disable JIT optimizer in the binary folder, the problematic “Release” code also works.
the content of the ini file (OptTest.ini) is:
[.NET Framework Debugging Control]
AllowOptimize =0
Yet another update:
I posted this issue to MSDN forum, and some guru give me a further explanation so then i know how the JIT optimizer take different actions when dealing with implicit and explicit type constructors, the story is like that:
When using implicit type constructor in ClassB, JIT will call it in the very beginning, even ClassA is not initialized. at this point, type constructor will get the exception by ClassA and wrap it into a TypeInitializationException. And the TypeInitializationException is not thrown out immediately by JIT, instead, it will record this exception. When the first method is called by the code, that is the ClassB constructo, JIT will not call type constructor again, but just throw the recorded TypeInitializationException. That is why this demo failed in release build.
the referenced explanation is from an article in MSDN magazine.
http://msdn.microsoft.com/en-us/magazine/cc163857.aspx
Comment from Liang(明之)
Time December 18, 2008 at 4:07 PM
The same issue still exists in Visual studio express 2008.