vm$ mkdir gc_copying +- + + Implementing the GC + Algorithm
+ +- + + Running the VM with the Custom + GC
+ +
Index: gc-howto-content.html =================================================================== --- gc-howto-content.html (revision 454321) +++ gc-howto-content.html (working copy) @@ -1,359 +1,354 @@ - - + + +
- - - -This document provides instructions on creating a custom garbage collector implementation -in C++ and configuring the DRL virtual machine to use it. The document describes -the major steps of this procedure, namely:
-
+ How to + Write DRL GC+ |
+
| + |
+ Contents+ |
+
+
Note
-Plugging-in a user-designed garbage collector presupposes an operating DRL -virtual machine built according to the instructions of the README.txt file -supplied with the VM source package. - - -1. Establishing the build infrastructure-
- At this stage, you create the directory and set up the build infrastructure to -build the dynamic library. At the end of this stage, you will be fully set for -adding the garbage collector code and building it. -DRLVM can load a custom garbage collector from a dynamic library. It is -recommended that you build your dynamic library using a DRLVM build -infrastructure. Below is an example of creating of a -build descriptor on the Windows* / IA-32 architecture. -1.1. Create a directory for a new GC module, for example:-
-
- vm$ mkdir gc_copying + |
+
+ 1. About + This Document+ +This document provides instructions + on creating a custom garbage collector + implementation (GC, version 1.0, + 2006-07-20) in C++ and configuring the + DRL virtual machine to use it. The + document describes the major steps of + this procedure, namely: establishing + the build infrastructure, implementing + the GC interface, implementing the GC + algorithm, and running the VM with the + custom GC. + +Note + +Plugging-in a
+ user-designed garbage collector
+ presupposes an operating DRL virtual
+ machine built according to the
+ instructions of the
+ |
+
+ + + 2. Establishing the Build + Infrastructure+ +At this stage, you create the + directory and set up the build + infrastructure to build the dynamic + library. At the end of this stage, you + will be fully set for adding the + garbage collector code and building + it. + +DRLVM can load a custom garbage + collector from a dynamic library. It is + recommended that you build your dynamic + library using a DRLVM build + infrastructure. Below is an example of + creating of a build descriptor on the + Windows/IA-32 architecture. + |
+
+ + + 2.1 Creating a Directory for a New GC + Module+ +The example below shows how to + create a directory for a new GC + module: ++vm$ mkdir gc_copying vm$ mkdir gc_copying/src -vm$ cd gc_copying/src- - That is where you will put the source code, see Section 3, -Implementing the garbage collector algorithm. -1.2. Create a build descriptor file-Create the build descriptor file build/make/components/vm/gc_copying.xml -with the following content: -
-
- <project name="vm.gc_copying"> +vm$ cd gc_copying/src ++ + In the newly created directory you + will store the source code. For more + information, refer to the + + Implementing the GC Algorithm + section. + |
+
+ + + 2.2 Creating a Build Descriptor + File+ +Create the build descriptor file
+
+<project name="vm.gc_copying">
<target name="init" depends="common_vm">
<property name="build.depends" value="extra.apr,vm.vmcore" />
<property name="outtype" value="shared" />
@@ -372,7 +367,7 @@
</includepath>
<fileset dir="${src}/gc_copying/src">
- <include name="*.cpp" />
+ <include name="*.cpp" />
</fileset>
<defineset define="BUILDING_GC" />
@@ -389,262 +384,452 @@
</select>
</linker>
</target>
-</project>
-
-You can add other macro definitions, include directories or compiler-specific -command-line options to match your needs. -1.3. Create a C++ file with essential includes, namely:-
-
- #include "open/gc.h" +</project> ++ + You can add other macro definitions, + include directories or + compiler-specific command-line options + to match your needs. + |
+
+ + + 2.3 Creating a C++ file with essential + include files+ +Create a C++ file with essential + include files, namely: ++#include "open/gc.h" #include "open/vm_gc.h" #include "open/vm.h" #define LOG_DOMAIN "gc" -#include "cxxlog.h"- - These include files are located in directories vm/include/open and -vm/port/include. Consult their content for documentation and details of the -interface. -1.4. Test the configuration-Run the build system to test whether the infrastructure is set up correctly: -
-
-
- build$ build.bat -DCOMPONENTS=vm.gc_copying- On a successful build, the .dll file is placed to the VM build directory -build/win_ia32_icl_debug/deploy/jre/bin/. The name of the directory may differ -depending on your system and the compiler used. This empty library will not -work, you have to write your GC first! - -2. Implementing the GC interface-
- This section lists the functions that a garbage collector interface must -implement. Declarations of these functions are in gc.h. For details, consult -the Developer's Guide and documentation in gc.h and vm_gc.h. -2.1. GC lifecycle-
|
+
+ + 2.4 + Testing the Configuration-gc_wrapup() shuts down the GC +Run the build system to test whether + the infrastructure is set up + correctly: ++build$ build.bat -DCOMPONENTS=vm.gc_copying +- - On a successful build, the
+ Note -This empty library + will not work, you have to write your + GC first. + |
+
+ + + 3. Implementing a Collector that Uses the GC Interface- -This section lists the functions
+ that a garbage collector interface must
+ implement. Declarations of these
+ functions are in |
+
+ 3.1 GC + Lifecycle- - -2.2. Object allocation-
|
+
+ 3.2. + Object allocation- -
See the Root set enumeration section in the Developer's Guide for details. -2.3. Miscellaneous-
See the + + Root Set Enumeration section in the + Developer's + Guide for details. + |
+
+ 3.3. + Miscellaneous- -
2.4. Optional-The virtual machine can operate without the functions listed below, but certain features -will be unavailable. -
|
+
+ 3.4. + Optional- -The virtual machine can operate + without the functions listed below, but + certain features will be + unavailable. -gc_pin_object() requests that the GC does not move an object +
gc_pin_object()
+ requests that the GC does not move
+ an objectgc_unpin_object()
+ removes the restriction on not
+ moving an objectgc_get_next_live_object()
+ iterates over live objects during
+ the stop-the-world phase of garbage
+ collectiongc_finalize_on_exit()
+ transfers finalizable queue
+ contents to the VM core on
+ shutdowngc_time_since_last_gc()
+ returns the amount of time that
+ elapsed since the previous
+ collection, in millisecondsgc_total_memory()
+ returns the overall amount of
+ memory used for the Java heapgc_max_memory()
+ returns the maximum amount of
+ memory that can be used for the
+ Java heap |
+
+
+
+ 3.5. The
-
- |
+
+ + 4. + Implementing the GC + Algorithm+ +This section gives step-by-step + instructions on how to implement the + garbage collection algorithm. The + example shows a semispace copying + collector. + +Note + +This example does + not implement object finalization and + weak references. + |
+
+ 4.1. + Algorithm Overview+ +The heap is divided into two equally + sized contiguous semispaces. During + normal operation, only one semispace is + used (current semispace), and + the other one is reserved for + garbage collection. Allocation requests + are satisfied by contiguous allocation + from the current semispace. Each + application thread reserves a + thread-local allocation buffer + (TLAB) under a global lock, and + serves most of the allocation requests + without locking, by incrementing the + allocation pointer local to the + buffer. + +When the application requests an + allocation that does not fit into the + remaining free space of the current + semispace, a garbage collection is + initiated. The current semispace + becomes the evacuation space + (fromspace), and the reserved + semispace becomes the destination + space (tospace). The VM + suspends all application threads and + enumerates root references. + +The GC copies the objects reachable + from root references to the destination + space. When an object is copied from + evacuation space to destination space, + the GC installs the forwarding pointer + in the old copy. Root references are + updated to point to new object + locations. + +After the root set enumeration is + complete, the GC scans objects in the + destination space. Each reached object + is copied to the destination space, the + forwarding pointer is installed in the + old copy, and the scanned object fields + are updated. For objects with + forwarding pointers installed, the GC + updates object fields. In this way, the + GC ensures that all live objects are + copied to the destination space exactly + once. + +The destination space serves as a + queue of objects to be scanned when + more and more objects are copied to the + destination space during heap + traversal. Once all live objects are + reached and copied, the scan queue + stops growing, and the GC updates + object fields only during the last part + of the scanning process. + +The GC completes the scanning + process when the scan pointer reaches + the allocation pointer in the + destination space. At this stage, all + live objects have been evacuated to the + destination space, and the evacuation + space can be safely reclaimed. The GC + then changes the semispace roles: it + uses the destination space for further + allocation and reserves the evacuation + space for the next garbage collection. + The change of the semispace roles is + commonly referred to as + flip. + +After the semispace flip, the GC + resumes user threads. + +Please refer to the excellent survey + for detailed description of this + algorithm and other basic garbage + collection techniques, "Uniprocessor + Garbage Collection Techniques", Paul R. + Wilson. + +3.2. Source code explained+ +The full source code of the
+ collector is available in
+ The structure
+// This structure is allocated for each user thread.
// It contains the thread-local allocation area.
struct TLS {
byte* current; // the allocation pointer
byte* limit; // the end of the allocation area
-};
-
-Define the main GC structure to contain the Java heap and the data necessary -for GC operation, as shown below. -
-
-
- // Encapsulates all GC data. +}; ++ + Define the main GC structure to + contain the Java heap and the data + necessary for GC operation, as shown + below. +
+// Encapsulates all GC data.
struct GC {
unsigned int semisize; // the size of the semispace
@@ -687,13 +872,14 @@
std::list<InteriorPointer> interior_pointers;
void repoint_all_roots_with_interior_points();
-};
-The following structure stores object information: the object field layout and -the object size. -
-
-
- // Structure OI (from "object information") +}; ++ + The following structure stores + object information: the object field + layout and the object size. +
+// Structure OI (from "object information")
// is used to cache GC information for each Java class
// loaded by the virtual machine.
// Each VTable stores the pointer to an OI (Object information) structure.
@@ -707,107 +893,141 @@
int size; // the object size or the array element size
int* offsets; // zero-terminated list of slot offsets in an object
// undefined for array
-};
-The data stored in the OI structure is initialized and accessed by the GC only. -The following structures convey the static assumptions that GC makes about -object layout. The VM must use the same object layout assumptions for the -correct GC operation. -The VTable structure contains the virtual table of the object methods, -and is linked from the object header. The VM reserves some space (at least 4 bytes) -for exclusive use by GC. The GC uses 4 bytes of GC-private space to put the pointer -to the object information structure struct OI. -
-
-
- // The VTable structure has 4 bytes reserved +}; ++ + The data stored in the
+ The following structures convey the + static assumptions that GC makes about + object layout. The VM must use the same + object layout assumptions for the + correct GC operation. + +The
+// The VTable structure has 4 bytes reserved
// for GC use at the beginning.
// The pointer to the OI structure is stored there.
struct VTable {
OI* oi;
// Other VTable fields are not used in GC.
-};
-The GC assumes that each Java* object has a fixed header: (1) a pointer -to the VTable structure, and then a (2) 32 bit word with flags. -The 25 highest bits are used by the VM Thread Manager component to -implement Java* monitors and 7 lowest bits are used by GC and for -storing the object hash code. -
-
-
- // Describes the object header format assumed by GC. +}; ++ + The GC assumes that each Java object
+ has a fixed header: (1) a pointer to
+ the
+// Describes the object header format assumed by GC.
struct Object {
VTable *vt;
uint32 lockword;
-};
-The array objects have the same header, and a 4 byte length field -at the offset 8. -
-
-
- // Describes the array header format assumed by GC. +}; ++ + The array objects have the same + header, and a 4 byte length field at + the offset 8. +
+// Describes the array header format assumed by GC.
struct Array {
VTable *vt;
uint32 lockword;
uint32 length;
-};
-Note
-The layout described is valid for the IA-32 platform only. -A number of convenience functions use object layout knowledge to perform -various data manipulations. The function init_vt() writes the VTable pointer -to an object. -
-
-
- void init_vt(Managed_Object_Handle p, Allocation_Handle ah) {
+};
+
+
+ Note + +The layout + described is valid for the IA-32 + platform only. + +A number of convenience functions
+ use object layout knowledge to perform
+ various data manipulations. The
+ function
+void init_vt(Managed_Object_Handle p, Allocation_Handle ah) {
Object* obj = (Object*)p;
obj->vt = (VTable*)ah;
-}
-The function obj_oi() retrieves object information structure -pointer from an object. -
-
-
- OI* obj_oi(Managed_Object_Handle p) {
+}
+
+
+ The function
+OI* obj_oi(Managed_Object_Handle p) {
Object* obj = (Object*)p;
return obj->vt->oi;
-}
-The function array_length() retrieves the length of an array -object. -
-
-
- int array_length(Managed_Object_Handle p) {
+}
+
+
+ The function
+
+int array_length(Managed_Object_Handle p) {
Array* array = (Array*)p;
return array->length;
-}
-The function vt_oi() retrieves the OI structure pointer -from the VTable pointer. -
-
-
- OI* vt_oi(VTable_Handle p) {
+}
+
+
+ The function
+OI* vt_oi(VTable_Handle p) {
VTable* vt = (VTable*)p;
return vt->oi;
-}
-The function ah_oi() retrieves the OI structure pointer -using Allocation_Handle. On 32-bit architectures, the -VTable pointer is a 32-bit pointer, and Allocation_Handle is a 32-bit -integer. -
-
-
- OI* ah_oi(Allocation_Handle ah) {
+}
+
+
+ The function
+OI* ah_oi(Allocation_Handle ah) {
// Allocation_Handle is a VTable pointer on 32-bit platforms.
return vt_oi((VTable_Handle)ah);
-}
-The object_size() function computes the size of an object. Array size is -calculated by summing the header size and the element size multiplied by array -length. Afterwards the size is aligned to be multiple of 4. The non-array -object size is cached in the OI structure. -
-
-
- int object_size (Managed_Object_Handle obj) {
+}
+
+
+ The
+int object_size (Managed_Object_Handle obj) {
OI* oi = obj_oi(obj);
if (oi->is_array) {
// 4-byte alignment
@@ -815,29 +1035,37 @@
} else {
return oi->size;
}
-}
-In this example, the garbage collector is created statically as a global -instance of structure GC: -
-
-
- GC gc;- The function init() statically configures size parameters. Normally, this -function uses the function vm_get_property() to read configuration options -specified as property values on the command line. In this example, we use -constant values for simplicity. -
-
-
- void GC::init() {
+}
+
+
+ In this example, the garbage + collector is created statically as a + global instance of structure GC: ++GC gc; ++ + The function
+void GC::init() {
semisize = 500*1024*1024;
- chunk_size = 64*1024;
-As the next step, the init() function allocates space for the heap, divides it -into two semispaces, and initializes the allocation semispace. -
-
-
- space = (byte*) malloc(semisize*2); + chunk_size = 64*1024; ++ + As the next step, the
+
+ space = (byte*) malloc(semisize*2);
assert(space); assert(((int)space & 3) == 0);
fromspace = space;
tospace = fromspace + semisize; assert(((int)tospace & 3) == 0);
@@ -854,14 +1082,19 @@
memset(current, 0, limit - current);
interior_pointers.clear();
-}
-The global allocation function uses a lock to protect the heap from -simultaneous access from multiple threads. The locking mechanism -is trivially implemented in a platform-dependent way. See the full source code in gc_copying.cpp. -
-
-
- byte* GC::galloc(unsigned size) {
+}
+
+
+ The global allocation function uses
+ a lock to protect the heap from
+ simultaneous access from multiple
+ threads. The locking mechanism is
+ trivially implemented in a
+ platform-dependent way. See the full
+ source code in
+
+byte* GC::galloc(unsigned size) {
byte* r = NULL;
lock.lock();
if (current + size <= limit) {
@@ -870,15 +1103,18 @@
}
lock.unlock();
return r;
-}
-The local allocation function uses the thread-local allocation area for object -allocation, and uses galloc() to allocate a new chunk for a thread-local -allocation area as needed. -
-
-
- byte* GC::alloc(unsigned size, TLS* tls) {
+}
+
+ The local allocation function uses
+ the thread-local allocation area for
+ object allocation, and uses
+
+byte* GC::alloc(unsigned size, TLS* tls) {
+
byte* obj = NULL;
assert(NULL == tls->current || fromspace <= tls->current);
@@ -907,26 +1143,30 @@
}
return obj;
-}
-The forwarding pointers are installed in the lockword structure, the second word -of an object. -
-
-
- byte* GC::forwarded (void* obj) {
+}
+
+
+ The forwarding pointers are + installed in the lockword structure, + the second word of an object. +
+byte* GC::forwarded (void* obj) {
int* p = (int*)obj + 1;
int lockword = *p;
if (lockword & 1)
return (byte*)(lockword & (~1));
else
return NULL;
-}
-The function move() copies the object data to the evacuation semispace and -installs the forwarding pointer in the old object copy. -
-
-
- byte* GC::move (void* obj) {
+}
+
+
+ The function
+byte* GC::move (void* obj) {
int size = object_size(obj);
assert(tospace <= copy); assert(copy + size <= toend);
@@ -940,15 +1180,20 @@
*plockword = ((int)nobj) | 1;
return nobj;
-}
-The function root() handles one root during root set enumeration. If the root -points to an object already reached, the root is updated with the forwarded -pointer value. Otherwise, the GC moves the object to the destination space and -installs the forwarding pointer in the old object copy. -
-
-
- void GC::root(void** root) {
+}
+
+
+ The function
+void GC::root(void** root) {
byte* obj = (byte*)(*root);
byte* nobj = forwarded(obj);
if (NULL == nobj) {
@@ -957,12 +1202,13 @@
TRACE2("gc.root", "root " << root << " repointed from "
<< (void*)obj << " to " << (void*)nobj);
*root = nobj;
-}
-The function trace() scans one object. -
-
-
- void GC::trace (byte* obj) {
+}
+
+
+ The function
+void GC::trace (byte* obj) {
OI* oi = obj_oi(obj);
TRACE2("gc.trace", "trace " << (void*)obj
<< " (" << (void*)object_size(obj) << ", " << oi->name << ")");
@@ -998,22 +1244,36 @@
*field = nobj;
}
}
-}
-The function collect_alloc() is the main function controlling garbage -collection. This function reclaims unused memory and the retries the allocation. The GC -attempts to allocate the memory before resuming other threads. This prevents the -thread that triggered the garbage collection from starving. -Note
-The thread is starving when it gets no resources for a long time -because other threads grab the resource before it can even try. -If the garbage collector resumes user threads before retrying the allocation, -these threads may use all available space quickly before the allocation succeeds. -In this case, the allocation will fail for an indefinite number of times. -
-
-
- byte* GC::collect_alloc(unsigned size, TLS* tls) {
+}
+
+ The function
+ Note + +The thread is + starving when it gets no + resources for a long time because other + threads grab the resource before it can + even try. If the garbage collector + resumes user threads before retrying + the allocation, these threads may use + all available space quickly before the + allocation succeeds. In this case, the + allocation will fail for an indefinite + number of times. +
+byte* GC::collect_alloc(unsigned size, TLS* tls) {
+
scan = tospace;
copy = tospace;
toend = tospace + semisize;
@@ -1063,22 +1323,34 @@
vm_resume_threads_after();
return obj;
-}
-The exported GC interface is mostly implemented by delegating the task to the -method of the structure GC. The GC initialization function init() is called -from gc_init(). -
-
+
+
-
-void gc_init() {
+}
+
+
+ The exported GC interface is mostly
+ implemented by delegating the task to
+ the method of the structure
+
+
-
+
+
-Thread local allocation areas are reset on thread creation and thread -termination events. -
-
+
+
-
+ void gc_thread_init(void* tp) {
+}
+
+ Thread local allocation areas are + reset on thread creation and thread + termination events. + +
+
-
+
+
-The slow path allocation function gc_alloc() checks whether the allocation space is -exhausted and starts garbage collection when necessary. -
-
-
- Managed_Object_Handle gc_alloc (unsigned size, Allocation_Handle ah, void *tp) {
+}
+
+
+ The slow path allocation
+ function
+
+Managed_Object_Handle gc_alloc (unsigned size, Allocation_Handle ah, void *tp) {
Managed_Object_Handle obj;
TLS* tls = (TLS*) tp;
@@ -1150,13 +1426,18 @@
assert(NULL == obj ||
(gc.fromspace <= obj && obj < gc.limit && ((int)obj & 3) == 0));
return obj;
-}
-If the memory is exhausted, the no-collection allocation function -gc_alloc_fast() returns NULL, and does not start garbage collection. -
-
-
- Managed_Object_Handle gc_alloc_fast (unsigned size, Allocation_Handle ah, void *tp) {
+}
+
+
+ If the memory is exhausted,
+ the no-collection allocation
+ function
+
+Managed_Object_Handle gc_alloc_fast (unsigned size, Allocation_Handle ah, void *tp) {
Managed_Object_Handle obj;
TLS* tls = (TLS*) tp;
size = size & 0x3fffffff;
@@ -1171,24 +1452,31 @@
assert(NULL == obj ||
(gc.fromspace <= obj && obj < gc.limit && ((int)obj & 3) == 0));
return obj;
-}
-The root set enumeration function passes the root reference to the root() -function. -
-
-
- void gc_add_root_set_entry(Managed_Object_Handle *ref, Boolean is_pinned) {
+}
+
+
+ The root set enumeration
+ function passes the root
+ reference to the
+
+void gc_add_root_set_entry(Managed_Object_Handle *ref, Boolean is_pinned) {
assert(!is_pinned);
TRACE2("gc.root", "gc_add_root_set_entry " << ref << " -> " << *ref);
if (NULL == *ref) return;
gc.root(ref);
-}
-The function build_slot_offset_array() is used to construct a NULL-terminated -list of offsets of reference fields. -
-
-
- static int *build_slot_offset_array(Class_Handle ch) +} ++ + The function
+
+static int *build_slot_offset_array(Class_Handle ch)
{
unsigned num_ref_fields = 0;
// Get the total number of fields including primitive fields.
@@ -1225,13 +1513,15 @@
*p = 0;
return ref_array;
-}
-The GC caches object layout information when the function gc_class_prepared() -is called. -
-
-
- void gc_class_prepared (Class_Handle ch, VTable_Handle vth) {
+}
+
+
+ The GC caches object layout
+ information when the function
+
+void gc_class_prepared (Class_Handle ch, VTable_Handle vth) {
TRACE2("gc.prepared", "gc_class_prepared("
<< class_get_name(ch) << ")");
OI** vt = (OI**) vth;
@@ -1257,45 +1547,102 @@
oi->offsets = build_slot_offset_array(ch);
oi->has_slots = (oi->offsets != NULL);
}
-}
-The function gc_force_gc() starts a forced garbage collection using the global -GC lock to ensure that only one thread is doing a collection at any time. It -passes null arguments to collect_alloc(), because it requires no -allocation. -
-
-
- void gc_force_gc () {
+}
+
+
+ The function
+
+void gc_force_gc () {
vm_gc_lock_enum();
gc.collect_alloc(0, NULL);
vm_gc_unlock_enum();
-}
-Other functions of the GC interface are empty or trivial, and not described in -this document. You can see the full listing in the gc_copying.cpp file. -After you completed coding the garbage collector, you can build a GC dynamic -library, as described above, by typing -
-
-
- build$ build.bat -DCOMPONENTS=vm.gc_copying- 4. Running VM with the custom GC-
-
-
-
+}
+
+
+ This section describes how to run the DRL virtual machine with the custom -garbage collector library. -You can specify the name of the dynamic library on the command line. For -example, to load a GC gc_copying.dll, execute the following: -
-
-
- ij -Dvm.dlls=gc_copying Hello- The virtual machine searches for a dynamic library gc_copying.dll in the -default locations, that is, the value for the PATH variable and the location of -executable ij.exe. -The default garbage collector is gc.dll located in the same bin/ directory as ij.exe. -Other functions of the GC
+ interface are empty or trivial,
+ and not described in this
+ document. You can see the full
+ listing in the
+ After you completed coding + the garbage collector, you can + build a GC dynamic library, as + described above, by typing ++build$ build.bat -DCOMPONENTS=vm.gc_copying ++ |
+
+ + + 5. Running the VM with the Custom + GC+ +This section describes how to run + the DRL virtual machine with the custom + garbage collector library. + +You can specify the name of the
+ dynamic library on the command line.
+ For example, to load a GC
+ +ij -Dvm.dlls=gc_copying Hello ++ + The virtual machine searches for a
+ dynamic library
+ |
+