• No results found

Faulting Scheme

In document Distributed Java Virtual Machine (Page 50-52)

5.3 Shared Object Space

5.3.3 Faulting Scheme

As described in Section 4.4.4, a shared object has several possible states: Write, Read and for non-home shared objects Invalid. Depending on the actual state several operations are triggered when a particular operation is executed on the object. For example if a read operation happens on an invalid object, the DSM updates the object’s latest value from its home node and can then remove the invalid bit. A write to an object that is in the Read state, will change its state to Write, etc. Therefore, we require from our DSM that such faulting accesses are detected and handled correctly. For our faulting scheme we added software checks to the Baseline compiler3

of the JikesRVM because our DSM is object-based and does not have the support of a MMU 3

Because of the complexity of the Optimizing compiler (cf. Section 2.1.1) we decided to add the software checks to the Baseline compiler only. Extending the Optimizing compiler is a topic for further work.

for detecting traps. Since the Baseline compiler takes the bytecode of the Java application and compiles it into machine code, the software checks are added to those bytecode instructions that access the object heap. Particularly, we made adjustments to the compiler whenever machine code for one of the following instruction is emitted:

• Load an object field (bytecode instruction GETFIELD) • Store a value into an object field (PUTFIELD)

• Load a static field of a class (GETSTATIC) • Store a value into a static field (PUTSTATIC)

• Load a value of an array (xALOAD where x stands for the type of the array, i.e. A for reference type, I for integer type, etc.)

• Store a value into an array (xASTORE)

For the instructions GETFIELD/PUTFIELD and xALOAD/xASTORE the checks that have been added are quite similar, where as GETSTATIC/PUTSTATIC are treated a bit different (see Section 5.3.5). Since the states described above are only related to shared objects, we added the software checks directly in assembly language in the corresponding emitCode methods of the compiler.

Listing 5.1: Added software checks for loading a field of a primitive type 1 if( obj == SHARED )

2 if( obj == INVALID )

3 updateInvalidObject ( obj )

4 load ( obj . value )

Listing 5.1 shows the pseudocode for loading an object field of a primitive type. Since the object’s state information is included in its header, the checks are done by emitting assembly instructions. For the case that the object need not be updated (i.e. the shared object is not invalid), we can jump directly to the load instruction meaning that we have one load instruction for the header, two compare instructions for the shared and invalid state check and one potential jump instruction as an overhead per load. Using this implementation, we postpone the expensive call of the function updateInvalidObject() (implemented in the class SharedObjectManager) to fetch the latest object values from its home node. Since in our DSM implementation dangling pointers are used, i.e. for references into the GUID table the last bit is set to 1, we need to add one more check whenever a reference field is loaded as shown in Listing 5.2.

As described in Section 5.3.2, if the reference’s least significant bit is set to 1, the requestObject() method is executed by passing the 4-byte-aligned reference. The parameter is then actually a pointer to the GUID object. In the requestObject() method, we first check if the GUID is mapped against a valid object address because it could have been faulted-in in the meantime. If

36 5. Implementation Listing 5.2: Added software checks for loading a reference field

1 if( obj == SHARED )

2 if( obj == INVALID )

3 updateInvalidObject ( obj )

4 if( addrOf ( obj . ref ) == invalid )

5 requestObject ( obj . ref )

6 load ( obj . ref )

this is the case, the address is stored into the object’s reference field. Otherwise the object is faulted-in from its home node that can be determined by the parameter GUID.

In [18] they pursue a different approach to avoid dealing with dangling pointers. Instead of storing invalid addresses into reference fields when an object is faulted-in, they allocate so-called dummy objects for each reference. Dummy objects have the same size as the “real” object, but are not initialized and therefore contain no data. In the reference fields of the faulted-in object, they store the address of the dummy objects such that no invalid pointers exist. In our approach we have an additional compare instruction when loading reference fields but we save much more space since we are not required to allocate space for these dummy objects. Additionally, these allocated dummy objects might never be accessed and if one object referencing these dummy objects is still reachable during garbage collection, they will not be removed from the heap.

An alternative implementation of the faulting scheme is to make use of the JikesRVM trap handler. Java threads use a predefined set of system calls to communicate with the underlying operating system. If a trap occurs, a special trap handler forwards the signal from the OS back to the calling Java thread as an exception. The faulting access could therefore be handled in the trap handler. Since handling traps is much more expensive because of user and kernel space switching, it makes sense to do the checks in software if many shared objects have to be faulted-in. Because this approach is more complex than adding software checks, this is a topic for future work (see Section 7.2.9.

In [23] an object table is used and object reference are indices into this table. Each entry consists of two fields, a local object pointer and a remote object pointer which is similar to our idea that not yet faulted-in object references points into the GUID table. However, since local object references also point into this object table, an additional load instruction is done for every object access. In our DSM only one compare instruction is needed to detected if a object is shared or local. The latter can be directly loaded from memory without loading the address in the object table entry first.

In document Distributed Java Virtual Machine (Page 50-52)

Related documents