Partager via


JIT compiler and type constructors (.cctors)

I get this question a lot

‘When do class constructors (.cctor) get run’

My answer is usually:

‘It depends’

From Partition 1 of the ECMA spec (https://msdn.microsoft.com/netframework/programming/clr/default.aspx)

**

The semantics of when, and what triggers execution of such type initialization methods, is as follows:

1. A type may have a type-initializer method, or not.

2. A type may be specified as having a relaxed semantic for its type-initializer method (for convenience below, we call this relaxed semantic BeforeFieldInit)

3. If marked BeforeFieldInit then the type’s initializer method is executed at, or sometime before, first access to any static field defined for that type

4. If not marked BeforeFieldInit then that type’s initializer method is executed at (i.e., is triggered by):

· first access to any static or instance field of that type, or

· first invocation of any static, instance or virtual method of that type

5. Execution of any type's initializer method will not trigger automatic execution of any initializer methods defined by its base type, nor of any interfaces that the type implements

Note: BeforeFieldInit behavior is intended for initialization code with no interesting side-effects, where exact timing does not matter. Also, under BeforeFieldInit semantics, type initializers are allowed to be executed at or before first access to any static field of that Type -- at the discretion of the CLI

If a language wishes to provide more rigid behavior -- e.g. type initialization automatically triggers execution of parents initializers, in a top-to-bottom order, then it can do so by either:

· defining hidden static fields and code in each class constructor that touches the hidden static field of its parent and/or interfaces it implements, or

· by making explicit calls to System.Runtime.CompilerServices.Runtime-Helpers.RunClassConstructor (see Partition IV).

Besides the difference in the number of ‘events’ we look at (static access vs static access + any method) there is the difference in timing, for beforefieldinit classes, the CLR is allowed to run the .cctor at any point before it’s actually required by the beforefieldinit semantics, whereas for precise types, we have to run them exactly at the trigger points mentioned in the spec (where ‘exactly’ means that we have to maintain the order of all visible side effects).

What does this mean for your code?

-          For beforefieldinit types, you can’t control the exact point where your .cctor is run. On the other side, it gives the CLR a better opportunity to optimize

-          With precise types, you know exactly when the .cctor is going to be called.

Let’s see it with a C# example. In C#, a class has precise semantics when it has an explicit .cctor (ie, static Type() { } ). If it doesn’t have an explicit .cctor, the class gets tagged with the BeforeFieldInit attributee.

using System;

class Precise

{

            public static int i = CCtorHelper();

           

            public static int CCtorHelper()

            {

                        Console.WriteLine("Running inside Precise .cctor");

                        return 20;

            }

           

            static Precise()

            {

            }

}

class BeforeFieldInit

{

            public static int CCtorHelper()

            {

                        Console.WriteLine("Running inside BeforeFieldInit .cctor");

                        return 20;

            }

           

            public static int i = CCtorHelper();

}

class Test

{

            static public void Main()

            {

                        Console.WriteLine("Visible side effect");

                        Console.WriteLine("Precise.i = {0}" , Precise.i);

                        Console.WriteLine("BeforeFieldInit.i = {0}", BeforeFieldInit.i);

            }

}

 

Now lets take a look at the code that gets generated:

G_M398_IG01:

            push EDI

            push ESI

G_M398_IG02:

            mov ECX, gword ptr [01A53270H] 'Visible side effect' // Use WriteLine as a side effect

            call System.Console.WriteLine(ref)

            mov EDI, gword ptr [01A53274H]

            mov ECX, 0x31fb688

            call CORINFO_HELP_NEWSFAST

            mov ESI, EAX

            mov EDX, 1 // call .cctor for precise

            mov ECX, 0x30b0d18

            call CORINFO_HELP_CCTOR

            mov EAX, dword ptr [classVar[0x30b0ef8]]

            mov dword ptr [ESI+4], EAX

            mov EDX, ESI

            mov ECX, EDI

            call [System.Console.WriteLine(ref,ref)]

            mov EDI, gword ptr [01A53278H] 'BeforeFieldInit.i = {0}'

            mov ECX, 0x31fb688

            call CORINFO_HELP_NEWSFAST

            mov ESI, EAX

            mov EAX, dword ptr [classVar[0x30b10e8]]

            mov dword ptr [ESI+4], EAX

            mov EDX, ESI

            mov ECX, EDI

            call [System.Console.WriteLine(ref,ref)]

__epilog:

            pop ESI

            pop EDI

            ret

You may notice that there is only one .cctor call, you’ll have to take my word that it is the call for Precise’s .cctor. So where is BeforeFieldInit’s cctor? it’s certainly not in Main(). Is it not going to run? Is this a bug?

Let’s take look at the output

 

 

Running inside BeforeFieldInit .cctor

Visible side effect

Running inside Precise .cctor

Precise.i = 20

BeforeFieldInit.i = 20

 

 

So it does appear that the .cctor is running, and before the Precise .cctor. The CLR is allowed to run BeforeFieldInit’s cctor anytime before the access to BeforeFieldInit.i, and it’s taking advantage of that by doing it at the time the JIT compiles Main(). This way you don’t spend any instructions (size and size matters here)  calling a helper to make sure the .cctor has already run. You could argue that this is a JIT compiler and that we could be generating a new function when the Precise .cctor ran that doesn’t call the helper. While this may be ok in server scenarios (where throughput is all that matters), client scenarios have different requirements (low number of private pages and working set are more important than in server), and Precise .cctor classes are not frequent (you have to go out of your way to get them).

So when should you be using Precise semantics? When do you need precise control of where the .cctor gets run? One example can be a Singleton that protects an expensive resource.

For example

Class Singleton

{

            Public Foo f;

            Static Singleton()

  {

                        F = new VeryExpensiveObject()

}

}

Int main()

{

            If ( DoStuff() == FAILED )

            {

                        Singleton.f.ReportError();

            }

            …

}

If going through the fail path is not frequent, getting Singleton’s cctor to run when main() is jitted may be a waste of resources, better be lazy about it, which is what the precise semantics give you.

Now that I’ve told you a bit about how this works, just some final words of my personal experience about .cctors. While I see how they make code easier to write, .cctors, as any code that gets run behind your back (and that does not have an explicit call in your code) can get tricky to debug. Given the nature of beforefieldinit semantics, .cctors can run at any time, so you can find that an unrelated change in your codebase changes the order that .cctors get run, causing subtle bugs. This is specially true in sets classes that have circular dependencies. So while .cctors provide a nice invariant that can avoid some simple bugs, it’s very easy to get into a place where you will have some other subtle bugs, that depend on timing or what other code ran before you, so if you feel that you really need them, keep them simple.

Comments

  • Anonymous
    February 09, 2005
    David, what about NGEN scenario. When do you call .ctor for beforefieldinit classes?
  • Anonymous
    February 09, 2005
    Very good question. You will see that the output of the test program if you ngen it is different. In Whidbey, it looks like this:

    Visible side effect
    Running inside Precise .cctor
    Precise.i = 20
    Running inside BeforeFieldInit .cctor
    BeforeFieldInit.i = 20

    This is related to statics and ngen. I will need at least 2 posts to cover this, but FWIW, you're still better off having BeforeFieldInit if you care for throughput.


  • Anonymous
    February 13, 2005
    Blog link of the week 06
  • Anonymous
    March 07, 2005
    <p>&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.brustblog.com/PermaLink,guid,14fdb863-4733-4a0d-a690-4604437c380b.aspx&quot;&gt;NS8.0 és az IE&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://statgep.hu/mra.sx&quot;&gt;Készül a statgép&lt;/a&
  • Anonymous
    July 19, 2005
    I’ve seen some confusion in some MS internal mailing lists about when singletons are instantiated for...
  • Anonymous
    April 05, 2008
    PingBack from http://copyrightrenewalsblog.info/david-notarios-weblog/