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