I know there are a number of Microsoft readers out there. Does anyone have the definitive answer to the performance implications of using static members?
It seems that static constructors are to be avoided. There is an FXCop rule against their usage in favor of static initializers. This is related to setting of the beforefieldinit flag of a class.
Classes with static constructors have the beforefieldinit set to false. Classes with static initializers, but no static constructors, have the beforefieldinit set to true. Internally, a class with static initializers automatically gets a static constructor, but with the flag set to false.
My guess from prior reading is they have an adverse impact on performance on NGen'ed images, requiring multiple ubiquitous initialization checks whenever class members are accessed.
When beforefieldinit is true, checks are only limited to accesses to static fields, since the static constructor is only required to run before the first access to a static field. When false, the static initializer must run the first time any of the following occurs: an instance is created, a static method is called, or a static field is accessed.
On JITted images, the JIT compiler can optimized the checks away because it knows the first time a class is accessed, because it compiles each class on the fly and invokes the static constructor, the first time it pulls in the class's metadata and IL. There might not even be a difference in behavior for JITted classes between the on and off setting.
One can get around the effect of beforefieldinit being false for static constructors by moving the static constructor and fields to another auxiliary class, or even a nested class.
I know that both WinForms and Avalon create and initialize an extensive number of globals. WinForms caches standard brushes and other GDI+ objects in static fields. In addition, it uses vanilla objects as keys to identify one of its many properties or events. ( By vanilla objects, I mean it calls calling "new object()" to create an underived object for its reference-based equality behavior. "object" is not an abstract type. ) Avalon creates and stores DependencyProperties, an extension of the CLR property mechanism, as static fields in hundreds of classes.
For the most part, these globals are meant to be constant, never changing throughout the life of the program. Since static variables make up the root, having many of them may have an enormous impact on all three levels of garbage collection, from minimalist generation 0 to the full-blown generation 2 GC.
I say "may have", since the collector could be smart and treat the globals like the older generation 2 space, since most global references tend to change infrequently, if at all. Static variables would then only be examined during a GC 0 collection, if a write barrier was triggered in the space for global roots.
Rico Mariani points out that gen 0 collections can perform slowly because of too many roots; the example he gives is about references stored in a deep stack created by recursive functions. My question is: is the runtime is "smart" about global roots?