/* 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.
- $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 "rootset.h"
#include "mm/memory.h"
#include "toolbox/logging.h"
-#include "vm/finalizer.h"
-#include "vm/vm.h"
-#include "vmcore/rt-timing.h"
+
+#include "vm/finalizer.hpp"
+#include "vm/rt-timing.h"
+#include "vm/vm.hpp"
/* Global Variables ***********************************************************/
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;
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();
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))
}
+/* 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;
+ }
+ }
+
+ 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);
+}
+
+
/* gc_collect ******************************************************************
This is the main machinery which manages a collection. It should be run by
void gc_collect(s4 level)
{
rootset_t *rs;
- 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
- /* this is only quick'n'dirty check, but is NOT thread safe */
- if (gc_pending || gc_running) {
- GC_LOG( dolog("GC: Preventing reentrance!"); );
- return;
- }
-
- /* TODO: some global GC lock!!! */
+ /* 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);
GC_LOG( dolog("GC: Suspending threads ..."); );
GC_LOG( threads_dump(); );
threads_stopworld();
- GC_LOG( threads_dump(); );
+ /*GC_LOG( threads_dump(); );*/
GC_LOG( dolog("GC: Suspension finished."); );
#endif
- /* sourcestate of the current thread */
+#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 */
/* find the global and local rootsets */
rs = rootset_readout();
- GC_LOG( rootset_print(rs); );
+
+#if !defined(NDEBUG)
+ /* print the rootsets if debugging is enabled */
+ if (opt_GCDebugRootSet)
+ rootset_print(rs);
+#endif
RT_TIMING_GET_TIME(time_rootset);
/* 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( rootset_print(rs); );*/
+ /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
#if defined(ENABLE_MEMCHECK)
/* invalidate the rest of the main region */
RT_TIMING_GET_TIME(time_compact);
+ /* 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);
+
#else
/* copy the heap to new region */
gcstat_println();
#endif
-#if defined(GCCONF_FINALIZER)
- /* does the finalizer need to be notified */
- if (gc_notify_finalizer)
- finalizer_notify();
-#endif
-
/* we are no longer running */
- /* REMEBER: keep this below the finalizer notification */
gc_running = false;
#if defined(ENABLE_THREADS)
/*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_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 (opt_verbosegc)
dolog("GC: Forced Collection ...");
+ GCSTAT_COUNT(gcstat_collections_forced);
+
gc_collect(0);
if (opt_verbosegc)
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)
{
- vm_abort("gc_finalize_all: IMPLEMENT ME!");
+#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();
}
/* 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);