* src/mm/cacao-gc/gc.c (gc_init) [ENABLE_HANDLES]: Added offset check for
[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    $Id$
26
27 */
28
29
30 #include "config.h"
31 #include <signal.h>
32 #include "vm/types.h"
33
34 #include "threads/lock-common.h"
35 #include "threads/threads-common.h"
36
37 #include "compact.h"
38 #include "copy.h"
39 #include "final.h"
40 #include "gc.h"
41 #include "heap.h"
42 #include "mark.h"
43 #include "region.h"
44 #include "rootset.h"
45 #include "mm/memory.h"
46 #include "toolbox/logging.h"
47 #include "vm/finalizer.h"
48 #include "vm/vm.h"
49 #include "vmcore/rt-timing.h"
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;
59
60 #if defined(ENABLE_THREADS)
61 java_object_t *gc_global_lock;
62 #endif
63
64 #if !defined(ENABLE_THREADS)
65 executionstate_t *_no_threads_executionstate;
66 sourcestate_t    *_no_threads_sourcestate;
67 #endif
68
69
70 /* gc_init *********************************************************************
71
72    Initializes the garbage collector.
73
74 *******************************************************************************/
75
76 #define GC_SYS_SIZE (20*1024*1024)
77
78 void gc_init(u4 heapmaxsize, u4 heapstartsize)
79 {
80         if (opt_verbosegc)
81                 dolog("GC: Initialising with heap-size %d (max. %d)",
82                         heapstartsize, heapmaxsize);
83
84 #if defined(ENABLE_HANDLES)
85         /* check our indirection cells */
86         if (OFFSET(java_handle_t, heap_object) != 0)
87                 vm_abort("gc_init: indirection cell offset is displaced: %d", OFFSET(java_handle_t, heap_object));
88         if (OFFSET(hashtable_classloader_entry, object) != 0)
89                 vm_abort("gc_init: classloader entry cannot be used as indirection cell: %d", OFFSET(hashtable_classloader_entry, object));
90         if (OFFSET(hashtable_global_ref_entry, o) != 0)
91                 vm_abort("gc_init: global reference entry cannot be used as indirection cell: %d", OFFSET(hashtable_global_ref_entry, o));
92 #endif
93
94         /* finalizer stuff */
95         final_init();
96
97         /* set global variables */
98         gc_pending = false;
99         gc_running = false;
100
101         /* create list for external references */
102         gc_reflist = 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 void gc_reference_register(java_object_t **ref, int32_t reftype)
133 {
134         list_gcref_entry_t *re;
135
136         /* the reference needs to be registered before it is set, so make sure the
137            reference is not yet set */
138         GC_ASSERT(*ref == NULL);
139
140 #if defined(ENABLE_THREADS)
141         /* XXX dirty hack because threads_init() not yet called */
142         if (THREADOBJECT == NULL) {
143                 GC_LOG( dolog("GC: Unable to register Reference!"); );
144                 return;
145         }
146 #endif
147
148         GC_LOG2( printf("Registering Reference at %p\n", (void *) ref); );
149
150         re = NEW(list_gcref_entry_t);
151
152         re->ref     = ref;
153 #if !defined(NDEBUG)
154         re->reftype = reftype;
155 #endif
156
157         list_add_last(gc_reflist, re);
158 }
159
160
161 void gc_reference_unregister(java_object_t **ref)
162 {
163         vm_abort("gc_reference_unregister: IMPLEMENT ME!");
164 }
165
166
167 /* gc_collect ******************************************************************
168
169    This is the main machinery which manages a collection. It should be run by
170    the thread which triggered the collection.
171
172    IN:
173      XXX
174
175    STEPS OF A COLLECTION:
176      XXX
177
178 *******************************************************************************/
179
180 void gc_collect(s4 level)
181 {
182         rootset_t    *rs;
183         s4            dumpsize;
184 #if !defined(NDEBUG)
185         stackframeinfo   *sfi;
186         stacktracebuffer *stb;
187 #endif
188 #if defined(ENABLE_RT_TIMING)
189         struct timespec time_start, time_suspend, time_rootset, time_mark, time_compact, time_end;
190 #endif
191
192         /* enter the global gc lock */
193         LOCK_MONITOR_ENTER(gc_global_lock);
194
195         /* remember start of dump memory area */
196         dumpsize = dump_size();
197
198         GCSTAT_COUNT(gcstat_collections);
199
200         RT_TIMING_GET_TIME(time_start);
201
202         /* let everyone know we want to do a collection */
203         GC_ASSERT(!gc_pending);
204         gc_pending = true;
205
206         /* finalizer is not notified, unless marking tells us to do so */
207         gc_notify_finalizer = false;
208
209 #if defined(ENABLE_THREADS)
210         /* stop the world here */
211         GC_LOG( dolog("GC: Suspending threads ..."); );
212         GC_LOG( threads_dump(); );
213         threads_stopworld();
214         /*GC_LOG( threads_dump(); );*/
215         GC_LOG( dolog("GC: Suspension finished."); );
216 #endif
217
218 #if !defined(NDEBUG)
219         /* get the stacktrace of the current thread and make sure it is non-empty */
220         GC_LOG( printf("Stacktrace of current thread:\n"); );
221         sfi = STACKFRAMEINFO;
222         stb = stacktrace_create(sfi);
223         if (stb == NULL)
224                 vm_abort("gc_collect: no stacktrace available for current thread!");
225         GC_LOG( stacktrace_print_trace_from_buffer(stb); );
226 #endif
227
228         /* sourcestate of the current thread, assuming we are in the native world */
229         GC_LOG( dolog("GC: Stackwalking current thread ..."); );
230 #if defined(ENABLE_THREADS)
231         GC_ASSERT(THREADOBJECT->flags & THREAD_FLAG_IN_NATIVE);
232 #endif
233         replace_gc_from_native(THREADOBJECT, NULL, NULL);
234
235         /* everyone is halted now, we consider ourselves running */
236         GC_ASSERT(!gc_running);
237         gc_pending = false;
238         gc_running = true;
239
240         RT_TIMING_GET_TIME(time_suspend);
241
242         GC_LOG( heap_println_usage(); );
243         /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
244
245         /* find the global and local rootsets */
246         rs = rootset_readout();
247         GC_LOG( rootset_print(rs); );
248
249         RT_TIMING_GET_TIME(time_rootset);
250
251 #if 1
252
253         /* mark the objects considering the given rootset */
254         mark_me(rs);
255         /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
256
257         RT_TIMING_GET_TIME(time_mark);
258
259         /* compact the heap */
260         compact_me(rs, heap_region_main);
261         /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
262
263 #if defined(ENABLE_MEMCHECK)
264         /* invalidate the rest of the main region */
265         region_invalidate(heap_region_main);
266 #endif
267
268         RT_TIMING_GET_TIME(time_compact);
269
270         /* check if we should increase the heap size */
271         if (gc_get_free_bytes() < gc_get_heap_size() / 3) /* TODO: improve this heuristic */
272                 heap_increase_size(rs);
273
274 #else
275
276         /* copy the heap to new region */
277         {
278                 regioninfo_t *src, *dst;
279
280                 src = heap_region_main;
281                 dst = NEW(regioninfo_t);
282                 region_create(dst, heap_current_size);
283                 copy_me(heap_region_main, dst, rs);
284                 heap_region_main = dst;
285
286                 /* invalidate old heap */
287                 memset(src->base, 0x66, src->size);
288         }
289 #endif
290
291         /* TODO: check my return value! */
292         /*heap_increase_size();*/
293
294         /* write back the rootset to update root references */
295         GC_LOG( rootset_print(rs); );
296         rootset_writeback(rs);
297
298 #if defined(ENABLE_STATISTICS)
299         if (opt_verbosegc)
300                 gcstat_println();
301 #endif
302
303         /* we are no longer running */
304         gc_running = false;
305
306 #if defined(ENABLE_THREADS)
307         /* start the world again */
308         GC_LOG( dolog("GC: Reanimating world ..."); );
309         threads_startworld();
310         /*GC_LOG( threads_dump(); );*/
311 #endif
312
313 #if defined(GCCONF_FINALIZER)
314         /* does the finalizer need to be notified */
315         if (gc_notify_finalizer)
316                 finalizer_notify();
317 #endif
318
319         RT_TIMING_GET_TIME(time_end);
320
321         RT_TIMING_TIME_DIFF(time_start  , time_suspend, RT_TIMING_GC_SUSPEND);
322         RT_TIMING_TIME_DIFF(time_suspend, time_rootset, RT_TIMING_GC_ROOTSET1)
323         RT_TIMING_TIME_DIFF(time_rootset, time_mark   , RT_TIMING_GC_MARK);
324         RT_TIMING_TIME_DIFF(time_mark   , time_compact, RT_TIMING_GC_COMPACT);
325         RT_TIMING_TIME_DIFF(time_compact, time_end    , RT_TIMING_GC_ROOTSET2);
326         RT_TIMING_TIME_DIFF(time_start  , time_end    , RT_TIMING_GC_TOTAL);
327
328     /* free dump memory area */
329     dump_release(dumpsize);
330
331         /* leave the global gc lock */
332         LOCK_MONITOR_EXIT(gc_global_lock);
333
334 }
335
336
337 #if defined(ENABLE_THREADS)
338 bool gc_suspend(threadobject *thread, u1 *pc, u1 *sp)
339 {
340         codeinfo         *code;
341
342         /* check if the thread suspended itself */
343         if (pc == NULL) {
344                 GC_LOG( dolog("GC: Suspended myself!"); );
345                 return true;
346         }
347
348         /* thread was forcefully suspended */
349         GC_LOG( dolog("GC: Suspending thread (tid=%p)", thread->tid); );
350
351         /* check where this thread came to a halt */
352         if (thread->flags & THREAD_FLAG_IN_NATIVE) {
353
354                 if (thread->gc_critical) {
355                         GC_LOG( dolog("\tNATIVE &  CRITICAL -> retry"); );
356
357                         GC_ASSERT(0);
358
359                         /* wait till this thread suspends itself */
360                         return false;
361
362                 } else {
363                         GC_LOG( dolog("\tNATIVE & SAFE -> suspend"); );
364
365                         /* we assume we are in a native! */
366                         replace_gc_from_native(thread, pc, sp);
367
368                         /* suspend me now */
369                         return true;
370
371                 }
372
373         } else {
374                 code = code_find_codeinfo_for_pc_nocheck(pc);
375
376                 if (code != NULL) {
377                         GC_LOG( dolog("\tJIT (pc=%p) & KNOWN(codeinfo=%p) -> replacement",
378                                         pc, code); );
379
380                         /* arm the replacement points of the code this thread is in */
381                         replace_activate_replacement_points(code, false);
382
383                         /* wait till this thread suspends itself */
384                         return false;
385
386                 } else {
387                         GC_LOG( dolog("\tJIT (pc=%p) & UN-KNOWN -> retry", pc); );
388
389                         /* re-suspend me later */
390                         /* TODO: implement me! */
391                         /* TODO: (this is a rare race condition which was not yet triggered) */
392                         GC_ASSERT(0);
393                         return false;
394
395                 }
396
397         }
398
399         /* this point should never be reached */
400         GC_ASSERT(0);
401
402 }
403 #endif
404
405
406 /* gc_call *********************************************************************
407
408    Forces a full collection of the whole Java Heap.
409    This is the function which is called by java.lang.Runtime.gc()
410
411 *******************************************************************************/
412
413 void gc_call(void)
414 {
415         if (opt_verbosegc)
416                 dolog("GC: Forced Collection ...");
417
418         GCSTAT_COUNT(gcstat_collections_forced);
419
420         gc_collect(0);
421
422         if (opt_verbosegc)
423                 dolog("GC: Forced Collection finished.");
424 }
425
426
427 /* gc_invoke_finalizers ********************************************************
428
429    Forces invocation of all the finalizers for objects which are reclaimable.
430    This is the function which is called by the finalizer thread.
431
432 *******************************************************************************/
433
434 void gc_invoke_finalizers(void)
435 {
436         if (opt_verbosegc)
437                 dolog("GC: Invoking finalizers ...");
438
439         final_invoke();
440
441         if (opt_verbosegc)
442                 dolog("GC: Invoking finalizers finished.");
443 }
444
445
446 /* gc_finalize_all *************************************************************
447
448    Forces the finalization of all objects on the Java Heap.
449    This is the function which is called by java.lang.Runtime.exit()
450
451    We do this by setting all objects with finalizers to reclaimable,
452    which is inherently dangerouse because objects may still be alive.
453
454 *******************************************************************************/
455
456 void gc_finalize_all(void)
457 {
458 #if !defined(NDEBUG)
459         /* doing this is deprecated, inform the user */
460         dolog("gc_finalize_all: Deprecated!");
461 #endif
462
463         /* set all objects with finalizers to reclaimable */
464         final_set_all_reclaimable();
465
466         /* notify the finalizer thread */
467         finalizer_notify();
468 }
469
470
471 /* Informational getter functions *********************************************/
472
473 s8 gc_get_heap_size(void)     { return heap_current_size; }
474 s8 gc_get_free_bytes(void)    { return heap_region_main->free; }
475 s8 gc_get_total_bytes(void)   { return heap_region_main->size - heap_region_main->free; }
476 s8 gc_get_max_heap_size(void) { return heap_maximal_size; }
477
478
479 /* Statistics *****************************************************************/
480
481 #if defined(ENABLE_STATISTICS)
482 int gcstat_collections;
483 int gcstat_collections_forced;
484 int gcstat_mark_depth;
485 int gcstat_mark_depth_max;
486 int gcstat_mark_count;
487
488 void gcstat_println()
489 {
490         printf("\nGCSTAT - General Statistics:\n");
491         printf("\t# of collections: %d\n", gcstat_collections);
492         printf("\t# of forced collections: %d\n", gcstat_collections_forced);
493
494     printf("\nGCSTAT - Marking Statistics:\n");
495     printf("\t# of objects marked: %d\n", gcstat_mark_count);
496     printf("\tMaximal marking depth: %d\n", gcstat_mark_depth_max);
497
498         printf("\nGCSTAT - Compaction Statistics:\n");
499
500         printf("\n");
501 }
502 #endif /* defined(ENABLE_STATISTICS) */
503
504
505 /*
506  * These are local overrides for various environment variables in Emacs.
507  * Please do not remove this and leave it at the end of the file, where
508  * Emacs will automagically detect them.
509  * ---------------------------------------------------------------------
510  * Local variables:
511  * mode: c
512  * indent-tabs-mode: t
513  * c-basic-offset: 4
514  * tab-width: 4
515  * End:
516  * vim:noexpandtab:sw=4:ts=4:
517  */