/* src/mm/cacao-gc/gc.c - main garbage collector methods
- Copyright (C) 2006 R. Grafl, A. Krall, C. Kruegel,
- C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
- E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
- J. Wenninger, Institut f. Computersprachen - TU Wien
+ Copyright (C) 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
This file is part of CACAO.
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Contact: cacao@cacaojvm.org
-
- Authors: Michael Starzinger
-
- $Id$
-
*/
#include "config.h"
+
#include <signal.h>
+#include <stdint.h>
+
#include "vm/types.h"
-#if defined(ENABLE_THREADS)
-# include "threads/native/threads.h"
-#else
-/*# include "threads/none/threads.h"*/
-#endif
+#include "threads/lock.hpp"
+#include "threads/thread.hpp"
#include "compact.h"
+#include "copy.h"
+#include "final.h"
#include "gc.h"
#include "heap.h"
#include "mark.h"
#include "region.h"
+#include "rootset.h"
#include "mm/memory.h"
#include "toolbox/logging.h"
-#include "vm/exceptions.h"
-/*#include "vm/options.h"*/
+
+#include "vm/finalizer.hpp"
+#include "vm/rt-timing.h"
+#include "vm/vm.hpp"
+
+
+/* Global Variables ***********************************************************/
+
+bool gc_pending;
+bool gc_running;
+bool gc_notify_finalizer;
+
+list_t *gc_reflist_strong;
+list_t *gc_reflist_weak;
+
+#if !defined(ENABLE_THREADS)
+executionstate_t *_no_threads_executionstate;
+sourcestate_t *_no_threads_sourcestate;
+#endif
/* gc_init *********************************************************************
dolog("GC: Initialising with heap-size %d (max. %d)",
heapstartsize, heapmaxsize);
+#if defined(ENABLE_HANDLES)
+ /* check our indirection cells */
+ if (OFFSET(java_handle_t, heap_object) != 0)
+ vm_abort("gc_init: indirection cell offset is displaced: %d", OFFSET(java_handle_t, heap_object));
+ if (OFFSET(hashtable_classloader_entry, object) != 0)
+ vm_abort("gc_init: classloader entry cannot be used as indirection cell: %d", OFFSET(hashtable_classloader_entry, object));
+ if (OFFSET(hashtable_global_ref_entry, o) != 0)
+ vm_abort("gc_init: global reference entry cannot be used as indirection cell: %d", OFFSET(hashtable_global_ref_entry, o));
+#endif
+
+ /* finalizer stuff */
+ final_init();
+
+ /* set global variables */
+ gc_pending = false;
+ gc_running = false;
+
+ /* create list for external references */
+ gc_reflist_strong = list_create(OFFSET(list_gcref_entry_t, linkage));
+ gc_reflist_weak = list_create(OFFSET(list_gcref_entry_t, linkage));
+
/* region for uncollectable objects */
heap_region_sys = NEW(regioninfo_t);
if (!region_create(heap_region_sys, GC_SYS_SIZE))
- exceptions_throw_outofmemory_exit();
+ vm_abort("gc_init: region_create failed: out of memory");
/* region for java objects */
heap_region_main = NEW(regioninfo_t);
if (!region_create(heap_region_main, heapstartsize))
- exceptions_throw_outofmemory_exit();
+ vm_abort("gc_init: region_create failed: out of memory");
heap_current_size = heapstartsize;
heap_maximal_size = heapmaxsize;
+}
+
+
+/* gc_reference_register *******************************************************
+
+ Register an external reference which points onto the Heap. The
+ reference needs to be cleared (set to NULL) when registering and
+ has to be set after it has been registered (to avoid a race condition).
+
+ STRONG REFERENCE: gets updated and keeps objects alive
+ WEAK REFERENCE: only gets updated (or maybe cleared)
+
+*******************************************************************************/
+
+static void gc_reference_register_intern(list_t *list, java_object_t **ref, int32_t reftype)
+{
+ list_gcref_entry_t *re;
+
+ /* the global GC lock also guards the reference lists */
+ GC_MUTEX_LOCK;
+
+ GC_LOG2( printf("Registering Reference at %p\n", (void *) ref); );
+
+ /* the reference needs to be registered before it is set, so make sure the
+ reference is not yet set */
+ GC_ASSERT(*ref == NULL);
+
+#if !defined(NDEBUG)
+ /* check if this reference is already registered */
+ for (re = list_first(list); re != NULL; re = list_next(list, re)) {
+ if (re->ref == ref)
+ vm_abort("gc_reference_register_intern: reference already registered");
+ }
+#endif
+
+ /* create a new reference entry */
+ re = NEW(list_gcref_entry_t);
+
+ re->ref = ref;
+#if !defined(NDEBUG)
+ re->reftype = reftype;
+#endif
+
+ /* add the entry to the given list */
+ list_add_last(list, re);
+
+ /* the global GC lock also guards the reference lists */
+ GC_MUTEX_UNLOCK;
+}
+
+void gc_reference_register(java_object_t **ref, int32_t reftype)
+{
+ gc_reference_register_intern(gc_reflist_strong, ref, reftype);
+}
+
+void gc_weakreference_register(java_object_t **ref, int32_t reftype)
+{
+ gc_reference_register_intern(gc_reflist_weak, ref, reftype);
+}
+
+
+/* gc_reference_unregister *****************************************************
+
+ Unregister a previously registered external reference.
+
+*******************************************************************************/
+
+static void gc_reference_unregister_intern(list_t *list, java_object_t **ref)
+{
+ list_gcref_entry_t *re;
+
+ /* the global GC lock also guards the reference lists */
+ GC_MUTEX_LOCK;
+
+ GC_LOG2( printf("Un-Registering Reference at %p\n", (void *) ref); );
+
+ /* search for the appropriate reference entry */
+ for (re = list_first(list); re != NULL; re = list_next(list, re)) {
+ if (re->ref == ref) {
+ /* remove the entry from the given list */
+ list_remove(list, re);
+
+ /* free the reference entry */
+ FREE(re, list_gcref_entry_t);
+
+ break;
+ }
+ }
- /* TODO: remove these */
- heap_free_size = heap_current_size;
- heap_used_size = 0;
+ vm_abort("gc_reference_unregister_intern: reference not found");
+
+ /* the global GC lock also guards the reference lists */
+ GC_MUTEX_UNLOCK;
+}
+
+void gc_reference_unregister(java_object_t **ref)
+{
+ gc_reference_unregister_intern(gc_reflist_strong, ref);
+}
+
+void gc_weakreference_unregister(java_object_t **ref)
+{
+ gc_reference_unregister_intern(gc_reflist_weak, ref);
}
void gc_collect(s4 level)
{
rootset_t *rs;
- regioninfo_t *src, *dst;
- s4 dumpsize;
+ int32_t dumpmarker;
+#if !defined(NDEBUG)
+ stacktrace_t *st;
+#endif
+#if defined(ENABLE_RT_TIMING)
+ struct timespec time_start, time_suspend, time_rootset, time_mark, time_compact, time_end;
+#endif
+
+ /* enter the global gc lock */
+ GC_MUTEX_LOCK;
/* remember start of dump memory area */
- dumpsize = dump_size();
+ DMARKER;
+
+ GCSTAT_COUNT(gcstat_collections);
+
+ RT_TIMING_GET_TIME(time_start);
+
+ /* let everyone know we want to do a collection */
+ GC_ASSERT(!gc_pending);
+ gc_pending = true;
+
+ /* finalizer is not notified, unless marking tells us to do so */
+ gc_notify_finalizer = false;
+
+#if defined(ENABLE_THREADS)
+ /* stop the world here */
+ GC_LOG( dolog("GC: Suspending threads ..."); );
+ GC_LOG( threads_dump(); );
+ threads_stopworld();
+ /*GC_LOG( threads_dump(); );*/
+ GC_LOG( dolog("GC: Suspension finished."); );
+#endif
+
+#if !defined(NDEBUG)
+ /* get the stacktrace of the current thread and make sure it is non-empty */
+ GC_LOG( printf("Stacktrace of current thread:\n"); );
+ st = stacktrace_get_current();
+ if (st == NULL)
+ vm_abort("gc_collect: no stacktrace available for current thread!");
+ GC_LOG( stacktrace_print(st); );
+#endif
+
+ /* sourcestate of the current thread, assuming we are in the native world */
+ GC_LOG( dolog("GC: Stackwalking current thread ..."); );
+#if defined(ENABLE_THREADS)
+ GC_ASSERT(THREADOBJECT->flags & THREAD_FLAG_IN_NATIVE);
+#endif
+ replace_gc_from_native(THREADOBJECT, NULL, NULL);
+
+ /* everyone is halted now, we consider ourselves running */
+ GC_ASSERT(!gc_running);
+ gc_pending = false;
+ gc_running = true;
+
+ RT_TIMING_GET_TIME(time_suspend);
GC_LOG( heap_println_usage(); );
- /*GC_LOG( heap_dump_region(heap_base, heap_ptr, false); );*/
+ /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
- /* find the global rootset and the rootset for the current thread */
- rs = DNEW(rootset_t);
- /*TODO: mark_rootset_create(rs);*/
- /*TODO: mark_rootset_from_globals(rs);*/
- mark_rootset_from_thread(THREADOBJECT, rs);
- GC_LOG( mark_rootset_print(rs); );
+ /* find the global and local rootsets */
+ rs = rootset_readout();
+
+#if !defined(NDEBUG)
+ /* print the rootsets if debugging is enabled */
+ if (opt_GCDebugRootSet)
+ rootset_print(rs);
+#endif
+
+ RT_TIMING_GET_TIME(time_rootset);
#if 1
/* mark the objects considering the given rootset */
mark_me(rs);
- /*GC_LOG( heap_dump_region(heap_region_main, true); );*/
+ /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
+
+ RT_TIMING_GET_TIME(time_mark);
/* compact the heap */
compact_me(rs, heap_region_main);
- /*GC_LOG( heap_dump_region(heap_region_main, false); );
- GC_LOG( mark_rootset_print(rs); );*/
+ /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
#if defined(ENABLE_MEMCHECK)
/* invalidate the rest of the main region */
region_invalidate(heap_region_main);
#endif
-#else
+ RT_TIMING_GET_TIME(time_compact);
- /* copy the heap to new region */
- dst = DNEW(regioninfo_t);
- region_init(dst, heap_current_size);
- gc_copy(heap_region_main, dst, rs);
+ /* check if we should increase the heap size */
+ if (gc_get_free_bytes() < gc_get_heap_size() / 3) /* TODO: improve this heuristic */
+ heap_increase_size(rs);
- /* invalidate old heap */
- /*memset(heap_base, 0x5a, heap_current_size);*/
+#else
+ /* copy the heap to new region */
+ {
+ regioninfo_t *src, *dst;
+
+ src = heap_region_main;
+ dst = NEW(regioninfo_t);
+ region_create(dst, heap_current_size);
+ copy_me(heap_region_main, dst, rs);
+ heap_region_main = dst;
+
+ /* invalidate old heap */
+ memset(src->base, 0x66, src->size);
+ }
#endif
/* TODO: check my return value! */
/*heap_increase_size();*/
/* write back the rootset to update root references */
- GC_LOG( mark_rootset_print(rs); );
- mark_rootset_writeback(rs);
+ GC_LOG( rootset_print(rs); );
+ rootset_writeback(rs);
#if defined(ENABLE_STATISTICS)
if (opt_verbosegc)
gcstat_println();
#endif
+ /* we are no longer running */
+ gc_running = false;
+
+#if defined(ENABLE_THREADS)
+ /* start the world again */
+ GC_LOG( dolog("GC: Reanimating world ..."); );
+ threads_startworld();
+ /*GC_LOG( threads_dump(); );*/
+#endif
+
+#if defined(GCCONF_FINALIZER)
+ /* does the finalizer need to be notified */
+ if (gc_notify_finalizer)
+ finalizer_notify();
+#endif
+
+ RT_TIMING_GET_TIME(time_end);
+
+ RT_TIMING_TIME_DIFF(time_start , time_suspend, RT_TIMING_GC_SUSPEND);
+ RT_TIMING_TIME_DIFF(time_suspend, time_rootset, RT_TIMING_GC_ROOTSET1)
+ RT_TIMING_TIME_DIFF(time_rootset, time_mark , RT_TIMING_GC_MARK);
+ RT_TIMING_TIME_DIFF(time_mark , time_compact, RT_TIMING_GC_COMPACT);
+ RT_TIMING_TIME_DIFF(time_compact, time_end , RT_TIMING_GC_ROOTSET2);
+ RT_TIMING_TIME_DIFF(time_start , time_end , RT_TIMING_GC_TOTAL);
+
/* free dump memory area */
- dump_release(dumpsize);
+ DRELEASE;
+
+ /* leave the global gc lock */
+ GC_MUTEX_UNLOCK;
+
+ /* XXX move this to an appropriate place */
+ lock_hashtable_cleanup();
+}
+
+
+#if defined(ENABLE_THREADS)
+bool gc_suspend(threadobject *thread, u1 *pc, u1 *sp)
+{
+ codeinfo *code;
+
+ /* check if the thread suspended itself */
+ if (pc == NULL) {
+ GC_LOG( dolog("GC: Suspended myself!"); );
+ return true;
+ }
+
+ /* thread was forcefully suspended */
+ GC_LOG( dolog("GC: Suspending thread (tid=%p)", thread->tid); );
+
+ /* check where this thread came to a halt */
+ if (thread->flags & THREAD_FLAG_IN_NATIVE) {
+
+ if (thread->gc_critical) {
+ GC_LOG( dolog("\tNATIVE & CRITICAL -> retry"); );
+
+ GC_ASSERT(0);
+
+ /* wait till this thread suspends itself */
+ return false;
+
+ } else {
+ GC_LOG( dolog("\tNATIVE & SAFE -> suspend"); );
+
+ /* we assume we are in a native! */
+ replace_gc_from_native(thread, pc, sp);
+
+ /* suspend me now */
+ return true;
+
+ }
+
+ } else {
+ code = code_find_codeinfo_for_pc_nocheck(pc);
+
+ if (code != NULL) {
+ GC_LOG( dolog("\tJIT (pc=%p) & KNOWN(codeinfo=%p) -> replacement",
+ pc, code); );
+
+ /* arm the replacement points of the code this thread is in */
+ replace_activate_replacement_points(code, false);
+
+ /* wait till this thread suspends itself */
+ return false;
+
+ } else {
+ GC_LOG( dolog("\tJIT (pc=%p) & UN-KNOWN -> retry", pc); );
+
+ /* re-suspend me later */
+ /* TODO: implement me! */
+ /* TODO: (this is a rare race condition which was not yet triggered) */
+ GC_ASSERT(0);
+ return false;
+
+ }
+
+ }
+
+ /* this point should never be reached */
+ GC_ASSERT(0);
}
+#endif
/* gc_call *********************************************************************
Forces a full collection of the whole Java Heap.
- This is the function which is called by System.VMRuntime.gc()
+ This is the function which is called by java.lang.Runtime.gc()
*******************************************************************************/
if (opt_verbosegc)
dolog("GC: Forced Collection ...");
+ GCSTAT_COUNT(gcstat_collections_forced);
+
gc_collect(0);
if (opt_verbosegc)
}
-/* Informational getter functions *********************************************/
-
-s8 gc_get_heap_size(void) { return heap_current_size; }
-s8 gc_get_free_bytes(void) { return heap_free_size; }
-s8 gc_get_total_bytes(void) { return heap_used_size; }
-s8 gc_get_max_heap_size(void) { return heap_maximal_size; }
+/* gc_invoke_finalizers ********************************************************
+ Forces invocation of all the finalizers for objects which are reclaimable.
+ This is the function which is called by the finalizer thread.
-/* Thread specific stuff ******************************************************/
+*******************************************************************************/
-#if defined(ENABLE_THREADS)
-int GC_signum1()
+void gc_invoke_finalizers(void)
{
- return SIGUSR1;
+ if (opt_verbosegc)
+ dolog("GC: Invoking finalizers ...");
+
+ final_invoke();
+
+ if (opt_verbosegc)
+ dolog("GC: Invoking finalizers finished.");
}
-int GC_signum2()
+
+/* gc_finalize_all *************************************************************
+
+ Forces the finalization of all objects on the Java Heap.
+ This is the function which is called by java.lang.Runtime.exit()
+
+ We do this by setting all objects with finalizers to reclaimable,
+ which is inherently dangerouse because objects may still be alive.
+
+*******************************************************************************/
+
+void gc_finalize_all(void)
{
- return SIGUSR2;
-}
+#if !defined(NDEBUG)
+ /* doing this is deprecated, inform the user */
+ dolog("gc_finalize_all: Deprecated!");
#endif
+ /* set all objects with finalizers to reclaimable */
+ final_set_all_reclaimable();
+
+ /* notify the finalizer thread */
+ finalizer_notify();
+}
+
+
+/* Informational getter functions *********************************************/
+
+s8 gc_get_heap_size(void) { return heap_current_size; }
+s8 gc_get_free_bytes(void) { return heap_region_main->free; }
+s8 gc_get_total_bytes(void) { return heap_region_main->size - heap_region_main->free; }
+s8 gc_get_max_heap_size(void) { return heap_maximal_size; }
+
/* Statistics *****************************************************************/
#if defined(ENABLE_STATISTICS)
+int gcstat_collections;
+int gcstat_collections_forced;
int gcstat_mark_depth;
int gcstat_mark_depth_max;
int gcstat_mark_count;
void gcstat_println()
{
+ printf("\nGCSTAT - General Statistics:\n");
+ printf("\t# of collections: %d\n", gcstat_collections);
+ printf("\t# of forced collections: %d\n", gcstat_collections_forced);
+
printf("\nGCSTAT - Marking Statistics:\n");
printf("\t# of objects marked: %d\n", gcstat_mark_count);
printf("\tMaximal marking depth: %d\n", gcstat_mark_depth_max);