5.3 Shared Object Space
5.3.5 Statics
Since the JikesRVM is written in Java, the VM code needs an underlying runtime environment to be able to execute itself. The JikesRVM uses its own runtime environment that is also shared with the application code. One data structure that is used by both is the so-called Java Table Of Contents (JTOC), which is a table of a fixed size that contains all static variables of the VM and application code. In a VM running on a single machine, static variables can be considered as global. In the DJVM although, these static variables must be distinguished into global instances within the application and local instances which are held within a cluster node because the JikesRVM uses
38 5. Implementation N1 N2 Home node of o1 T1 locks L1 T1 writes o1 Thread Switch T2 locks L2 writeback o1 T2 invalidates o1 T2 unlocks L2 Thread Switch T1 faults invalid o1 in T1 unlocks L1 N1 has cached-copy of o1 T1 reads o1
Figure 5.3: Cache coherence protocol.
some static variables to maintain its runtime support structures such as type dictionaries, thread queues, GC maps, etc. These runtime support structures are node specific and must become local to each node within the cluster. Our approach for the separation is done during classloading. Since every loaded class is represented by a VM_Class object internally, we set a flag if the class belongs to the JikesRVM package. If a class implements an interface belonging to the JikesRVM package, it will also be considered as a static local variable4
Accesses to static fields are different from accessing field of object instances. VM internally, every static field is represented by a VM_Field object which contains an offset into the JTOC. Whenever a static field is accessed, the offset is pushed onto the stack and together with the address of the JTOC which is always held in a register in the Baseline compiler, the value can be read at or written to the corresponding JTOC slot. Because of our replication steps during classloading (cf. Section 4.5), we can ensure a consistent view of the JTOC since static fields are inserted into the JTOC during classloading. For globally accessible static fields, we decided that they are always accessed via the master node’s JTOC whereas static fields that are local per node are accessed via their local JTOC.
Due to the different access pattern for static fields, the faulting scheme must be adjusted because 4In [36] they use an empty interface LocalOnlyStatic to tag all classes that are considered local-only.
the concept of detecting shared objects does not apply for static variables. A previous idea was that every static field represented by VM_Field object should also become shared objects with the master node as the home node. The problem that arose was that some static fields might never be detected as shared. The two Examples 5.3 and 5.4 should illustrate why our shared object concept does not apply for static fields.
Listing 5.3: Simple classes with static fields - Working 1 class Foo {
2 public static int staticInt = 10;
3 } 4
5 class Bar {
6 public static Foo foo = new Foo ();
7 }
When an instance of Bar becomes a shared object, the static field to the object of type Foo will also be marked as shared. If the static type Foo is accessed later via Bar.foo.staticInt, the object foo is faulted-in and its VM_Field representing the staticInt will also be marked as shared. In this case, we know that every VM_Field representing a static field is marked as shared and must be accessed over the master node.
The Listing 5.4 though shows an exception case. When an instance of type Bar is marked as shared, the VM_Field representing the staticInt of Foo will not be shared since Bar has no static fields declared. However, when the method returnFooStaticInt is executed, the static field of Foo must be considered as a global static but with our shared object detection scheme, it will not be detected as such.
Listing 5.4: Simple classes with static fields - Not working 1 class Foo {
2 public static int staticInt = 10;
3 } 4
5 class Bar {
6 public int returnFooStaticInt () {
7 return Foo . staticInt ;
8 }
9 }
As a consequence, we introduce a different access pattern when global statics are accessed. A worker node has to send the offset into the JTOC to the master node. If a primitive type is accessed, the master node will send the value back to the worker node or store the value into its JTOC if it was a write operation. Reference types although are treated similar to our faulting scheme. When a worker node wants to read a static reference, the master node checks if the referenced object is locally available and marks the object as shared. The master node sends the GUID back to the
40 5. Implementation worker node which is then able to request the object from the corresponding home node. When a global static reference is written into the master node’s JTOC, the worker node marks the referenced object as shared and then sends the GUID to the master node where the GUID address is stored into the corresponding JTOC slot by setting the last bit of the address to 1. Whenever a static reference is loaded from the JTOC, we must check if the object corresponding to this GUID is available locally or if it must be requested from its home node.