* configure.ac: New switch for disabling -O2 (--disable-optimizations).
[cacao.git] / src / mm / cacao-gc / gc.c
1 /* src/mm/cacao-gc/gc.c - main garbage collector methods
2
3    Copyright (C) 2006, 2007, 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <signal.h>
29 #include <stdint.h>
30
31 #include "vm/types.h"
32
33 #include "threads/lock.hpp"
34 #include "threads/thread.hpp"
35
36 #include "compact.h"
37 #include "copy.h"
38 #include "final.h"
39 #include "gc.h"
40 #include "heap.h"
41 #include "mark.h"
42 #include "region.h"
43 #include "rootset.h"
44 #include "mm/memory.hpp"
45 #include "toolbox/logging.hpp"
46
47 #include "vm/finalizer.hpp"
48 #include "vm/rt-timing.h"
49 #include "vm/vm.hpp"
50
51
52 /* Global Variables ***********************************************************/
53
54 bool gc_pending;
55 bool gc_running;
56 bool gc_notify_finalizer;
57
58 list_t *gc_reflist_strong;
59 list_t *gc_reflist_weak;
60
61 #if !defined(ENABLE_THREADS)
62 executionstate_t *_no_threads_executionstate;
63 sourcestate_t    *_no_threads_sourcestate;
64 #endif
65
66
67 /* gc_init *********************************************************************
68
69    Initializes the garbage collector.
70
71 *******************************************************************************/
72
73 #define GC_SYS_SIZE (20*1024*1024)
74
75 void gc_init(u4 heapmaxsize, u4 heapstartsize)
76 {
77         if (opt_verbosegc)
78                 dolog("GC: Initialising with heap-size %d (max. %d)",
79                         heapstartsize, heapmaxsize);
80
81 #if defined(ENABLE_HANDLES)
82         /* check our indirection cells */
83         if (OFFSET(java_handle_t, heap_object) != 0)
84                 vm_abort("gc_init: indirection cell offset is displaced: %d", OFFSET(java_handle_t, heap_object));
85         if (OFFSET(hashtable_classloader_entry, object) != 0)
86                 vm_abort("gc_init: classloader entry cannot be used as indirection cell: %d", OFFSET(hashtable_classloader_entry, object));
87         if (OFFSET(hashtable_global_ref_entry, o) != 0)
88                 vm_abort("gc_init: global reference entry cannot be used as indirection cell: %d", OFFSET(hashtable_global_ref_entry, o));
89 #endif
90
91         /* finalizer stuff */
92         final_init();
93
94         /* set global variables */
95         gc_pending = false;
96         gc_running = false;
97
98         /* create list for external references */
99         gc_reflist_strong = list_create(OFFSET(list_gcref_entry_t, linkage));
100         gc_reflist_weak   = list_create(OFFSET(list_gcref_entry_t, linkage));
101
102         /* region for uncollectable objects */
103         heap_region_sys = NEW(regioninfo_t);
104         if (!region_create(heap_region_sys, GC_SYS_SIZE))
105                 vm_abort("gc_init: region_create failed: out of memory");
106
107         /* region for java objects */
108         heap_region_main = NEW(regioninfo_t);
109         if (!region_create(heap_region_main, heapstartsize))
110                 vm_abort("gc_init: region_create failed: out of memory");
111
112         heap_current_size = heapstartsize;
113         heap_maximal_size = heapmaxsize;
114 }
115
116
117 /* gc_reference_register *******************************************************
118
119    Register an external reference which points onto the Heap. The
120    reference needs to be cleared (set to NULL) when registering and
121    has to be set after it has been registered (to avoid a race condition).
122    
123    STRONG REFERENCE: gets updated and keeps objects alive
124    WEAK REFERENCE:   only gets updated (or maybe cleared)
125
126 *******************************************************************************/
127
128 static void gc_reference_register_intern(list_t *list, java_object_t **ref, int32_t reftype)
129 {
130         list_gcref_entry_t *re;
131
132         /* the global GC lock also guards the reference lists */
133         GC_MUTEX_LOCK;
134
135         GC_LOG2( printf("Registering Reference at %p\n", (void *) ref); );
136
137         /* the reference needs to be registered before it is set, so make sure the
138            reference is not yet set */
139         GC_ASSERT(*ref == NULL);
140
141 #if !defined(NDEBUG)
142         /* check if this reference is already registered */
143         for (re = list_first(list); re != NULL; re = list_next(list, re)) {
144                 if (re->ref == ref)
145                         vm_abort("gc_reference_register_intern: reference already registered");
146         }
147 #endif
148
149         /* create a new reference entry */
150         re = NEW(list_gcref_entry_t);
151
152         re->ref     = ref;
153 #if !defined(NDEBUG)
154         re->reftype = reftype;
155 #endif
156
157         /* add the entry to the given list */
158         list_add_last(list, re);
159
160         /* the global GC lock also guards the reference lists */
161         GC_MUTEX_UNLOCK;
162 }
163
164 void gc_reference_register(java_object_t **ref, int32_t reftype)
165 {
166         gc_reference_register_intern(gc_reflist_strong, ref, reftype);
167 }
168
169 void gc_weakreference_register(java_object_t **ref, int32_t reftype)
170 {
171         gc_reference_register_intern(gc_reflist_weak, ref, reftype);
172 }
173
174
175 /* gc_reference_unregister *****************************************************
176
177    Unregister a previously registered external reference.
178
179 *******************************************************************************/
180
181 static void gc_reference_unregister_intern(list_t *list, java_object_t **ref)
182 {
183         list_gcref_entry_t *re;
184
185         /* the global GC lock also guards the reference lists */
186         GC_MUTEX_LOCK;
187
188         GC_LOG2( printf("Un-Registering Reference at %p\n", (void *) ref); );
189
190         /* search for the appropriate reference entry */
191         for (re = list_first(list); re != NULL; re = list_next(list, re)) {
192                 if (re->ref == ref) {
193                         /* remove the entry from the given list */
194                         list_remove(list, re);
195
196                         /* free the reference entry */
197                         FREE(re, list_gcref_entry_t);
198
199                         break;
200                 }
201         }
202
203         vm_abort("gc_reference_unregister_intern: reference not found");
204
205         /* the global GC lock also guards the reference lists */
206         GC_MUTEX_UNLOCK;
207 }
208
209 void gc_reference_unregister(java_object_t **ref)
210 {
211         gc_reference_unregister_intern(gc_reflist_strong, ref);
212 }
213
214 void gc_weakreference_unregister(java_object_t **ref)
215 {
216         gc_reference_unregister_intern(gc_reflist_weak, ref);
217 }
218
219
220 /* gc_collect ******************************************************************
221
222    This is the main machinery which manages a collection. It should be run by
223    the thread which triggered the collection.
224
225    IN:
226      XXX
227
228    STEPS OF A COLLECTION:
229      XXX
230
231 *******************************************************************************/
232
233 void gc_collect(s4 level)
234 {
235         rootset_t    *rs;
236         int32_t       dumpmarker;
237 #if !defined(NDEBUG)
238         stacktrace_t *st;
239 #endif
240 #if defined(ENABLE_RT_TIMING)
241         struct timespec time_start, time_suspend, time_rootset, time_mark, time_compact, time_end;
242 #endif
243
244         /* enter the global gc lock */
245         GC_MUTEX_LOCK;
246
247         /* remember start of dump memory area */
248         DMARKER;
249
250         GCSTAT_COUNT(gcstat_collections);
251
252         RT_TIMING_GET_TIME(time_start);
253
254         /* let everyone know we want to do a collection */
255         GC_ASSERT(!gc_pending);
256         gc_pending = true;
257
258         /* finalizer is not notified, unless marking tells us to do so */
259         gc_notify_finalizer = false;
260
261 #if defined(ENABLE_THREADS)
262         /* stop the world here */
263         GC_LOG( dolog("GC: Suspending threads ..."); );
264         GC_LOG( threads_dump(); );
265         threads_stopworld();
266         /*GC_LOG( threads_dump(); );*/
267         GC_LOG( dolog("GC: Suspension finished."); );
268 #endif
269
270 #if !defined(NDEBUG)
271         /* get the stacktrace of the current thread and make sure it is non-empty */
272         GC_LOG( printf("Stacktrace of current thread:\n"); );
273         st = stacktrace_get_current();
274         if (st == NULL)
275                 vm_abort("gc_collect: no stacktrace available for current thread!");
276         GC_LOG( stacktrace_print(st); );
277 #endif
278
279         /* sourcestate of the current thread, assuming we are in the native world */
280         GC_LOG( dolog("GC: Stackwalking current thread ..."); );
281 #if defined(ENABLE_THREADS)
282         GC_ASSERT(THREADOBJECT->flags & THREAD_FLAG_IN_NATIVE);
283 #endif
284         replace_gc_from_native(THREADOBJECT, NULL, NULL);
285
286         /* everyone is halted now, we consider ourselves running */
287         GC_ASSERT(!gc_running);
288         gc_pending = false;
289         gc_running = true;
290
291         RT_TIMING_GET_TIME(time_suspend);
292
293         GC_LOG( heap_println_usage(); );
294         /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
295
296         /* find the global and local rootsets */
297         rs = rootset_readout();
298
299 #if !defined(NDEBUG)
300         /* print the rootsets if debugging is enabled */
301         if (opt_GCDebugRootSet)
302                 rootset_print(rs);
303 #endif
304
305         RT_TIMING_GET_TIME(time_rootset);
306
307 #if 1
308
309         /* mark the objects considering the given rootset */
310         mark_me(rs);
311         /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
312
313         RT_TIMING_GET_TIME(time_mark);
314
315         /* compact the heap */
316         compact_me(rs, heap_region_main);
317         /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
318
319 #if defined(ENABLE_MEMCHECK)
320         /* invalidate the rest of the main region */
321         region_invalidate(heap_region_main);
322 #endif
323
324         RT_TIMING_GET_TIME(time_compact);
325
326         /* check if we should increase the heap size */
327         if (gc_get_free_bytes() < gc_get_heap_size() / 3) /* TODO: improve this heuristic */
328                 heap_increase_size(rs);
329
330 #else
331
332         /* copy the heap to new region */
333         {
334                 regioninfo_t *src, *dst;
335
336                 src = heap_region_main;
337                 dst = NEW(regioninfo_t);
338                 region_create(dst, heap_current_size);
339                 copy_me(heap_region_main, dst, rs);
340                 heap_region_main = dst;
341
342                 /* invalidate old heap */
343                 memset(src->base, 0x66, src->size);
344         }
345 #endif
346
347         /* TODO: check my return value! */
348         /*heap_increase_size();*/
349
350         /* write back the rootset to update root references */
351         GC_LOG( rootset_print(rs); );
352         rootset_writeback(rs);
353
354 #if defined(ENABLE_STATISTICS)
355         if (opt_verbosegc)
356                 gcstat_println();
357 #endif
358
359         /* we are no longer running */
360         gc_running = false;
361
362 #if defined(ENABLE_THREADS)
363         /* start the world again */
364         GC_LOG( dolog("GC: Reanimating world ..."); );
365         threads_startworld();
366         /*GC_LOG( threads_dump(); );*/
367 #endif
368
369 #if defined(GCCONF_FINALIZER)
370         /* does the finalizer need to be notified */
371         if (gc_notify_finalizer)
372                 finalizer_notify();
373 #endif
374
375         RT_TIMING_GET_TIME(time_end);
376
377         RT_TIMING_TIME_DIFF(time_start  , time_suspend, RT_TIMING_GC_SUSPEND);
378         RT_TIMING_TIME_DIFF(time_suspend, time_rootset, RT_TIMING_GC_ROOTSET1)
379         RT_TIMING_TIME_DIFF(time_rootset, time_mark   , RT_TIMING_GC_MARK);
380         RT_TIMING_TIME_DIFF(time_mark   , time_compact, RT_TIMING_GC_COMPACT);
381         RT_TIMING_TIME_DIFF(time_compact, time_end    , RT_TIMING_GC_ROOTSET2);
382         RT_TIMING_TIME_DIFF(time_start  , time_end    , RT_TIMING_GC_TOTAL);
383
384     /* free dump memory area */
385     DRELEASE;
386
387         /* leave the global gc lock */
388         GC_MUTEX_UNLOCK;
389
390         /* XXX move this to an appropriate place */
391         lock_hashtable_cleanup();
392 }
393
394
395 #if defined(ENABLE_THREADS)
396 bool gc_suspend(threadobject *thread, u1 *pc, u1 *sp)
397 {
398         codeinfo         *code;
399
400         /* check if the thread suspended itself */
401         if (pc == NULL) {
402                 GC_LOG( dolog("GC: Suspended myself!"); );
403                 return true;
404         }
405
406         /* thread was forcefully suspended */
407         GC_LOG( dolog("GC: Suspending thread (tid=%p)", thread->tid); );
408
409         /* check where this thread came to a halt */
410         if (thread->flags & THREAD_FLAG_IN_NATIVE) {
411
412                 if (thread->gc_critical) {
413                         GC_LOG( dolog("\tNATIVE &  CRITICAL -> retry"); );
414
415                         GC_ASSERT(0);
416
417                         /* wait till this thread suspends itself */
418                         return false;
419
420                 } else {
421                         GC_LOG( dolog("\tNATIVE & SAFE -> suspend"); );
422
423                         /* we assume we are in a native! */
424                         replace_gc_from_native(thread, pc, sp);
425
426                         /* suspend me now */
427                         return true;
428
429                 }
430
431         } else {
432                 code = code_find_codeinfo_for_pc_nocheck(pc);
433
434                 if (code != NULL) {
435                         GC_LOG( dolog("\tJIT (pc=%p) & KNOWN(codeinfo=%p) -> replacement",
436                                         pc, code); );
437
438                         /* arm the replacement points of the code this thread is in */
439                         replace_activate_replacement_points(code, false);
440
441                         /* wait till this thread suspends itself */
442                         return false;
443
444                 } else {
445                         GC_LOG( dolog("\tJIT (pc=%p) & UN-KNOWN -> retry", pc); );
446
447                         /* re-suspend me later */
448                         /* TODO: implement me! */
449                         /* TODO: (this is a rare race condition which was not yet triggered) */
450                         GC_ASSERT(0);
451                         return false;
452
453                 }
454
455         }
456
457         /* this point should never be reached */
458         GC_ASSERT(0);
459
460 }
461 #endif
462
463
464 /* gc_call *********************************************************************
465
466    Forces a full collection of the whole Java Heap.
467    This is the function which is called by java.lang.Runtime.gc()
468
469 *******************************************************************************/
470
471 void gc_call(void)
472 {
473         if (opt_verbosegc)
474                 dolog("GC: Forced Collection ...");
475
476         GCSTAT_COUNT(gcstat_collections_forced);
477
478         gc_collect(0);
479
480         if (opt_verbosegc)
481                 dolog("GC: Forced Collection finished.");
482 }
483
484
485 /* gc_invoke_finalizers ********************************************************
486
487    Forces invocation of all the finalizers for objects which are reclaimable.
488    This is the function which is called by the finalizer thread.
489
490 *******************************************************************************/
491
492 void gc_invoke_finalizers(void)
493 {
494         if (opt_verbosegc)
495                 dolog("GC: Invoking finalizers ...");
496
497         final_invoke();
498
499         if (opt_verbosegc)
500                 dolog("GC: Invoking finalizers finished.");
501 }
502
503
504 /* gc_finalize_all *************************************************************
505
506    Forces the finalization of all objects on the Java Heap.
507    This is the function which is called by java.lang.Runtime.exit()
508
509    We do this by setting all objects with finalizers to reclaimable,
510    which is inherently dangerouse because objects may still be alive.
511
512 *******************************************************************************/
513
514 void gc_finalize_all(void)
515 {
516 #if !defined(NDEBUG)
517         /* doing this is deprecated, inform the user */
518         dolog("gc_finalize_all: Deprecated!");
519 #endif
520
521         /* set all objects with finalizers to reclaimable */
522         final_set_all_reclaimable();
523
524         /* notify the finalizer thread */
525         finalizer_notify();
526 }
527
528
529 /* Informational getter functions *********************************************/
530
531 s8 gc_get_heap_size(void)     { return heap_current_size; }
532 s8 gc_get_free_bytes(void)    { return heap_region_main->free; }
533 s8 gc_get_total_bytes(void)   { return heap_region_main->size - heap_region_main->free; }
534 s8 gc_get_max_heap_size(void) { return heap_maximal_size; }
535
536
537 /* Statistics *****************************************************************/
538
539 #if defined(ENABLE_STATISTICS)
540 int gcstat_collections;
541 int gcstat_collections_forced;
542 int gcstat_mark_depth;
543 int gcstat_mark_depth_max;
544 int gcstat_mark_count;
545
546 void gcstat_println()
547 {
548         printf("\nGCSTAT - General Statistics:\n");
549         printf("\t# of collections: %d\n", gcstat_collections);
550         printf("\t# of forced collections: %d\n", gcstat_collections_forced);
551
552     printf("\nGCSTAT - Marking Statistics:\n");
553     printf("\t# of objects marked: %d\n", gcstat_mark_count);
554     printf("\tMaximal marking depth: %d\n", gcstat_mark_depth_max);
555
556         printf("\nGCSTAT - Compaction Statistics:\n");
557
558         printf("\n");
559 }
560 #endif /* defined(ENABLE_STATISTICS) */
561
562
563 /*
564  * These are local overrides for various environment variables in Emacs.
565  * Please do not remove this and leave it at the end of the file, where
566  * Emacs will automagically detect them.
567  * ---------------------------------------------------------------------
568  * Local variables:
569  * mode: c
570  * indent-tabs-mode: t
571  * c-basic-offset: 4
572  * tab-width: 4
573  * End:
574  * vim:noexpandtab:sw=4:ts=4:
575  */