Merged trunk and subtype.
[cacao.git] / src / mm / cacao-gc / heap.c
1 /* mm/cacao-gc/heap.c - GC module for heap management
2
3    Copyright (C) 2006, 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 #include "vm/types.h"
28
29 #include "threads/lock-common.h"
30
31 #include "gc.h"
32 #include "final.h"
33 #include "heap.h"
34 #include "mark.h"
35 #include "region.h"
36 #include "mm/memory.h"
37 #include "native/include/java_lang_String.h"
38 #include "native/llni.h"
39 #include "toolbox/logging.h"
40
41 #include "vm/global.h"
42 #include "vm/options.h"
43 #include "vm/rt-timing.h"
44 #include "vm/string.hpp"
45 #include "vm/vm.hpp"
46
47
48 /* Global Variables ***********************************************************/
49
50 s4 heap_current_size;  /* current size of the heap */
51 s4 heap_maximal_size;  /* maximal size of the heap */
52 regioninfo_t *heap_region_sys;
53 regioninfo_t *heap_region_main;
54
55
56 void heap_init_objectheader(java_object_t *o, u4 bytelength)
57 {
58         u4 wordcount;
59
60         /* initialize the header flags */
61         o->hdrflags = 0;
62
63         /* align the size */
64         /* TODO */
65
66         /* calculate the wordcount as stored in the header */
67     /* TODO: improve this to save wordcount and without header bytes */
68     if ((bytelength & 0x03) == 0) {
69         GC_ASSERT((bytelength & 0x03) == 0);
70         wordcount = (bytelength >> 2);
71         GC_ASSERT(wordcount != 0);
72     } else {
73         wordcount = GC_SIZE_DUMMY;
74     }
75
76         /* set the wordcount in the header */
77     if (wordcount >= GC_SIZE_DUMMY) {
78         GC_SET_SIZE(o, GC_SIZE_DUMMY);
79     } else {
80         GC_SET_SIZE(o, wordcount);
81     }
82
83 }
84
85
86 void heap_update_references(rootset_t *rs, regioninfo_t *region, u4 offset)
87 {
88         java_object_t  *o;
89         java_object_t  *ref;
90         java_object_t **refptr;
91         u1* start;
92         u1* end;
93         int i;
94
95         GC_LOG( dolog("GC: Updating all references (offset=%x) ...", offset); );
96
97         start = region->base - offset;
98         end = region->ptr - offset;
99         GC_LOG( printf("Region previously was [ %p ; %p]\n", start, end); );
100
101         GC_LOG2( printf("updating in root-sets ..."); );
102
103         /* walk through all rootsets */
104         while (rs) {
105
106                 /* walk through the references of this rootset */
107                 for (i = 0; i < rs->refcount; i++) {
108
109                         /* load the reference */
110                         refptr = rs->refs[i].ref;
111                         ref = *( refptr );
112
113                         GC_LOG2( printf("\troot pointer to %p\n", (void *) ref); );
114
115                         /* update the references */
116                         if (POINTS_INTO(ref, start, end))
117                                 *refptr = ((u1 *) ref) + offset;
118
119                 }
120
121                 /* skip to next rootset in chain */
122                 rs = rs->next;
123
124         }
125
126
127         o = region->base;
128         while (o < region->ptr) {
129
130                 GC_LOG2( printf("updating in %p ...\n", (void *) o); );
131
132                 if (IS_ARRAY(o)) {
133
134                         /* walk through the references of an Array */
135                         FOREACH_ARRAY_REF(o,ref,refptr,
136
137                                 GC_LOG2( printf("\tarray-entry %p -> %p\n", (void *) ref, ((u1 *) ref) + offset); );
138
139                                 if (POINTS_INTO(ref, start, end))
140                                         *refptr = ((u1 *) ref) + offset;
141
142                         );
143
144                 } else {
145
146                         /* walk through the references of an Object */
147                         FOREACH_OBJECT_REF(o,ref,refptr,
148
149                                 GC_LOG2( printf("\tobject-field %p -> %p\n", (void *) ref, ((u1 *) ref) + offset); );
150
151                                 if (POINTS_INTO(ref, start, end))
152                                         *refptr = ((u1 *) ref) + offset;
153
154                         );
155
156                 }
157
158                 /* skip to next object */
159                 o = ((u1 *) o) + get_object_size(o);
160
161         }
162
163 }
164
165
166 void heap_increase_size(rootset_t *rs)
167 {
168         s4 newsize;
169         s4 resize_offset;
170
171         /* only a quick sanity check */
172         GC_ASSERT(heap_current_size <= heap_maximal_size);
173
174         /* check if we are allowed to enlarge the heap */
175         if (heap_current_size == heap_maximal_size)
176                 vm_abort("heap_increase_size: reached maximal heap size: out of memory");
177
178         /* find out how much to increase the heap??? */
179         newsize = 2 * heap_current_size; /* XXX TODO: better heuristic here */
180         dolog("GC: Increasing Heap Size to %d bytes", newsize); /* XXX remove me */
181         GC_LOG( dolog("GC: Increasing Heap Size to %d bytes", newsize); );
182
183         /* resize the main heap region */
184         resize_offset = region_resize(heap_region_main, newsize);
185
186         /* update all references if necesarry */
187         if (resize_offset != 0)
188                 heap_update_references(rs, heap_region_main, resize_offset);
189         else
190                 dolog("GC WARNING: References are not updated after heap resizing!");
191
192         /* set the new values */
193         heap_current_size = newsize;
194
195         GC_LOG( dolog("GC: Increasing Heap Size was successful");
196                         heap_println_usage(); );
197
198         /* only a quick sanity check */
199         GC_ASSERT(heap_current_size <= heap_maximal_size);
200
201 }
202
203
204 s4 heap_get_hashcode(java_object_t *o)
205 {
206         s4 hashcode;
207
208         if (!o)
209                 return 0;
210
211         /* TODO: we need to lock the object here i think!!! */
212
213         /* check if there is a hash attached to this object */
214         if (GC_TEST_FLAGS(o, HDRFLAG_HASH_ATTACHED)) {
215
216                 hashcode = *( (s4 *) ( ((u1 *) o) + get_object_size(o) - SIZEOF_VOID_P ) ); /* TODO: clean this up!!! */
217                 GC_LOG2( dolog("GC: Hash re-taken: %d (0x%08x)", hashcode, hashcode); );
218
219         } else {
220
221                 GC_SET_FLAGS(o, HDRFLAG_HASH_TAKEN);
222
223                 hashcode = (s4) (ptrint) o;
224                 GC_LOG2( dolog("GC: Hash taken: %d (0x%08x)", hashcode, hashcode); );
225
226         }
227
228         return hashcode;
229 }
230
231
232 static java_object_t *heap_alloc_intern(u4 bytelength, regioninfo_t *region, bool collect)
233 {
234         java_object_t *p;
235
236         /* only a quick sanity check */
237         GC_ASSERT(region);
238         GC_ASSERT(bytelength >= sizeof(java_object_t));
239
240 #if !defined(NDEBUG) && defined(ENABLE_THREADS)
241         /* check the current VM state for sanity */
242         GC_ASSERT(!THREADOBJECT->gc_critical);
243         GC_ASSERT(THREADOBJECT->flags & THREAD_FLAG_IN_NATIVE);
244 #endif
245
246         /* align objects in memory */
247         bytelength = GC_ALIGN(bytelength, GC_ALIGN_SIZE);
248
249         /* lock the region */
250         LOCK_MONITOR_ENTER(region);
251
252 #if !defined(NDEBUG)
253         /* heavy stress test */
254         if (opt_GCStress && collect)
255                 gc_collect(0);
256 #endif
257
258         /* check for sufficient free space */
259         if (bytelength > region->free) {
260                 dolog("GC: Region out of memory! (collect=%d)", collect);
261
262                 if (collect) {
263                         gc_collect(0);
264 #if 0
265                         GC_ASSERT(region->free >= bytelength);
266 #else
267                         if (region->free < bytelength) {
268                                 dolog("GC: OOM OOM OOM OOM OOM OOM OOM OOM OOM OOM");
269                                 exceptions_throw_outofmemoryerror();
270                                 return NULL;
271                         }
272 #endif
273                 } else
274                         return NULL;
275         }
276
277         /* allocate the object in this region */
278         p = (java_object_t *) region->ptr;
279         region->ptr += bytelength;
280         region->free -= bytelength;
281
282         /* unlock the region */
283         LOCK_MONITOR_EXIT(region);
284
285         /* clear allocated memory region */
286         GC_ASSERT(p);
287         MSET(p, 0, u1, bytelength);
288
289         /* set the header information */
290         heap_init_objectheader(p, bytelength);
291
292         return p;
293 }
294
295
296 /* heap_alloc ******************************************************************
297
298    Allocates memory on the Java heap.
299
300 *******************************************************************************/
301
302 void *heap_alloc(u4 size, u4 references, methodinfo *finalizer, bool collect)
303 {
304         java_object_t *p;
305         java_handle_t *h;
306 #if defined(ENABLE_RT_TIMING)
307         struct timespec time_start, time_end;
308 #endif
309
310         RT_TIMING_GET_TIME(time_start);
311
312         p = heap_alloc_intern(size, heap_region_main, collect);
313
314         if (p == NULL)
315                 return NULL;
316
317 #if defined(GCCONF_HDRFLAG_REFERENCING)
318         /* We can't use a bool here for references, as it's passed as a
319            bitmask in builtin_new.  Thus we check for != 0. */
320         if (references != 0) {
321                 GC_SET_FLAGS(p, HDRFLAG_REFERENCING);
322         }
323 #endif
324
325         /* register the finalizer for this object */
326         if (finalizer != NULL) {
327                 final_register(p, finalizer);
328         }
329
330         h = LLNI_WRAP(p);
331
332         RT_TIMING_GET_TIME(time_end);
333         RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_GC_ALLOC);
334
335         return h;
336 }
337
338
339 void *heap_alloc_uncollectable(u4 size)
340 {
341         java_object_t *p;
342
343         /* loader.c does this a lot for classes with fieldscount equal zero */
344         if (size == 0)
345                 return NULL;
346
347         p = heap_alloc_intern(size, heap_region_sys, false);
348
349         if (p == NULL)
350                 return NULL;
351
352         /* TODO: can this be overwritten by cloning??? */
353         /* remember this object as uncollectable */
354         GC_SET_FLAGS(p, HDRFLAG_UNCOLLECTABLE);
355
356         return p;
357 }
358
359
360 void heap_free(void *p)
361 {
362         GC_LOG( dolog("GC: Free %p", p); );
363         GC_ASSERT(0);
364 }
365
366
367 /* Debugging ******************************************************************/
368
369 #if !defined(NDEBUG)
370 void heap_println_usage()
371 {
372         printf("Current Heap Usage: Size=%d Free=%d\n",
373                         heap_current_size, heap_region_main->free);
374
375         GC_ASSERT(heap_current_size == heap_region_main->size);
376 }
377 #endif
378
379
380 #if !defined(NDEBUG)
381 void heap_print_object_flags(java_object_t *o)
382 {
383         printf("0x%02x [%s%s%s%s]",
384                 GC_GET_SIZE(o),
385                 GC_TEST_FLAGS(o, HDRFLAG_HASH_ATTACHED) ? "A" : " ",
386                 GC_TEST_FLAGS(o, HDRFLAG_HASH_TAKEN)    ? "T" : " ",
387                 GC_TEST_FLAGS(o, HDRFLAG_UNCOLLECTABLE) ? "U" : " ",
388                 GC_TEST_FLAGS(o, GC_FLAG_MARKED)        ? "M" : " ");
389 }
390 #endif
391
392
393 #if !defined(NDEBUG)
394 void heap_print_object(java_object_t *o)
395 {
396         java_array_t *a;
397         classinfo    *c;
398
399         /* check for null pointers */
400         if (o == NULL) {
401                 printf("(NULL)");
402                 return;
403         }
404
405         /* print general information */
406 #if SIZEOF_VOID_P == 8
407         printf("0x%016llx: ", (unsigned long long) o);
408 #else
409         printf("0x%08lx: ", (unsigned long) o);
410 #endif
411
412         /* check for invalid heap references */
413         if (!POINTS_INTO(o, heap_region_main->base, heap_region_main->end) &&
414                 !POINTS_INTO(o, heap_region_sys->base, heap_region_sys->end))
415         {
416                 printf("<<< No Heap Reference >>>");
417                 return;
418         }
419
420         /* print object flags */
421         heap_print_object_flags(o);
422         printf(" ");
423
424         GC_ASSERT(o->vftbl);
425
426         /* TODO */
427         /* maybe this is not really an object */
428         if (/*IS_CLASS*/ o->vftbl->class == class_java_lang_Class) {
429
430                 /* get the class information */
431                 c = (classinfo *) o;
432
433                 /* print the class information */
434                 printf("CLS ");
435                 class_print(c);
436
437         } else if (/*IS_ARRAY*/ o->vftbl->arraydesc != NULL) {
438
439                 /* get the array information */
440                 a = (java_array_t *) o;
441                 c = o->vftbl->class;
442
443                 /* print the array information */
444                 printf("ARR ");
445                 /*class_print(c);*/
446                 utf_display_printable_ascii_classname(c->name);
447                 printf(" (size=%d)", a->size);
448
449         } else /*IS_OBJECT*/ {
450
451                 /* get the object class */
452                 c = o->vftbl->class;
453
454                 /* print the object information */
455                 printf("OBJ ");
456                 /*class_print(c);*/
457                 utf_display_printable_ascii_classname(c->name);
458                 if (c == class_java_lang_String) {
459                         printf(" (string=\"");
460                         utf_display_printable_ascii(
461                                         javastring_toutf((java_lang_String *) o, false));
462                         printf("\")");
463                 }
464
465         }
466 }
467 #endif
468
469 #if !defined(NDEBUG)
470 void heap_dump_region(regioninfo_t *region, bool marked_only)
471 {
472         java_object_t *o;
473         u4             o_size;
474
475         /* some basic sanity checks */
476         GC_ASSERT(region->base <= region->ptr);
477
478         printf("Heap-Dump:\n");
479
480         /* walk the region in a linear style */
481         o = (java_object_t *) region->base;
482         while (o < region->ptr) {
483
484                 if (!marked_only || GC_IS_MARKED(o)) {
485                         printf("\t");
486                         heap_print_object(o);
487                         printf("\n");
488                 }
489
490                 /* get size of object */
491                 o_size = get_object_size(o);
492
493                 /* walk to next object */
494                 GC_ASSERT(o_size != 0);
495                 o = ((u1 *) o) + o_size;
496         }
497
498         printf("Heap-Dump finished.\n");
499 }
500 #endif
501
502
503 s4 get_object_size(java_object_t *o)
504 {
505         java_array_t *a;
506         classinfo    *c;
507         s4            o_size;
508
509         /* we can assume someone initialized the header */
510         GC_ASSERT(o->hdrflags != 0);
511
512         /* get the wordcount from the header */
513         o_size = GC_GET_SIZE(o);
514
515         /* maybe we need to calculate the size by hand */
516         if (o_size != GC_SIZE_DUMMY) {
517                 GC_ASSERT(o_size != 0);
518                 o_size = o_size << 2;
519         } else {
520
521                 /* TODO */
522                 /* maybe this is not really an object */
523                 if (/*IS_CLASS*/ o->vftbl->class == class_java_lang_Class) {
524                         /* we know the size of a classinfo */
525                         o_size = sizeof(classinfo);
526
527                 } else if (/*IS_ARRAY*/ o->vftbl->arraydesc != NULL) {
528                         /* compute size of this array */
529                         a = (java_array_t *) o;
530                         c = o->vftbl->class;
531                         o_size = c->vftbl->arraydesc->dataoffset +
532                                         a->size * c->vftbl->arraydesc->componentsize;
533
534                 } else /*IS_OBJECT*/ {
535                         /* get the object size */
536                         c = o->vftbl->class;
537                         o_size = c->instancesize;
538                         GC_LOG( dolog("Got size (from Class): %d bytes", o_size); );
539                 }
540         
541         }
542
543         /* align the size */
544         o_size = GC_ALIGN(o_size, GC_ALIGN_SIZE);
545
546         /* the hashcode attached to this object might increase the size */
547         if (GC_TEST_FLAGS(o, HDRFLAG_HASH_ATTACHED))
548                 o_size += SIZEOF_VOID_P;
549
550         return o_size;
551 }
552
553
554 /*
555  * These are local overrides for various environment variables in Emacs.
556  * Please do not remove this and leave it at the end of the file, where
557  * Emacs will automagically detect them.
558  * ---------------------------------------------------------------------
559  * Local variables:
560  * mode: c
561  * indent-tabs-mode: t
562  * c-basic-offset: 4
563  * tab-width: 4
564  * End:
565  * vim:noexpandtab:sw=4:ts=4:
566  */