How exactly do static fields work internally? [duplicate]
Solution 1:
While the exact details of the type system are implementation dependent, let me go into some more detail than just stating that it depends and you should not care. I'll describe how it approximately works in Microsoft's implementation (.NET) according to the book CLR via C# by Jeffrey Richter and the article See How the CLR Creates Runtime Objects by Hanu Kommalapati et al. (original MSDN May 2005 issue).
Say you have a class:
class Foo
{
// Instance fields
string myBar = "Foobar";
int myNum;
// Static fields
static string bar = "Foobar";
static int num;
}
Foo myFoo = new Foo();
Type typeOfFoo = typeof(Foo);
Where do the instance fields live?
Whenever you say new Foo()
, space is allocated and initialized for the object instance, and the constructor is called. This instance is shown as instance of Foo in the image below. Such as instance contains only the instance fields of the class (in this case myBar
and myNum
), and for objects allocated on the heap two extra fields used by the runtime (Sync block index
and Type handle
). The type handle is a pointer to a Type
object that describes the type of the instance, in this case type of Foo.
When you say new Foo()
again, new space is allocated which will again contain space for the instance fields of the type. As you can see, instance fields are associated with object instances.
The runtime puts each instance field at a fixed offset from the start of the object's data. For example, myBar
might live at offset +4. The address of the instance field is simply the address of the object plus the offset of the field.
Where do the static fields live?
Static fields in C# and Java are not associated with any object instance, but with a type. Classes, structs and enums are examples of types. Only once (per type) is some space allocated to hold the values of the static fields. It would make sense to allocate space for the static fields in the Type
structure that describes the type, since there is also only one Type
object per type. This is the approach taken by C# and Java.
The Type
object1 is created when the type is loaded by the runtime. This structure contains all sorts of information needed for the runtime to be able to allocate new instances, call methods and perform casting, among other things. It also contains the space for the static fields, in this case bar
and num
.
The runtime has put each static field at some offset from the start of the type's data. This is different for each type. For example, bar
might live at offset +64. The address of the static field is the address of the Type
object plus the offset of the field. The type is statically known.
1) In Microsoft .NET multiple different structures describe a type, such as the MethodTable and the EEClass structures.
Solution 2:
This completely depends on the implementation in question. For C# and Java, the runtime is allowed to determine where to store the memory for the variable. For C and most compiled languages, the compiler makes this determination.
That being said, in practice, it doesn't matter. The usage it determined by specification, so you are free to use the variable knowing the behavior will be guaranteed.
Solution 3:
For Java, objects referred to by static fields will reside on the heap like other objects:
The heap is the runtime data area from which memory for all class instances and arrays is allocated.
The field will be initialised (if the declaration contains an initialization) when the class is loaded, which happens immediately before the first occurrence of any one of the following:
- an instance of the class is created.
- a static method declared by the class is invoked.
- a static field declared by the class is assigned.
- a static field declared by the class is used and the field is not a constant variable (§4.12.4).
The access to the static field is done via 2 special JVM instructions, getstatic and putstatic. But apart from that distinction, static fields are similar to non static fields.
Solution 4:
This varies wildly from language to language, and can even vary wildly from platform to platform...
For example, on the .NET side, static members are "associated" with the governing EEClass
definition, which can be a heap-allocated OR a "wherever" allocated member (the C# spec doesn't specify heap/stack behavior, it's an implementation detail of the VM)