2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright (C) 2005-2006 Novell, Inc
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
15 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
18 * Permission is hereby granted to use or copy this program
19 * for any purpose, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
24 * All the rest of the code is LGPL.
26 * Important: allocation provides always zeroed memory, having to do
27 * a memset after allocation is deadly for performance.
28 * Memory usage at startup is currently as follows:
30 * 64 KB internal space
32 * We should provide a small memory config with half the sizes
34 * We currently try to make as few mono assumptions as possible:
35 * 1) 2-word header with no GC pointers in it (firts vtable, second to store the
37 * 2) gc descriptor is the second word in the vtable (first word in the class)
38 * 3) 8 byte alignment is the minimum and enough (not true for special structures, FIXME)
39 * 4) there is a function to get an object's size and the number of
40 * elements in an array.
41 * 5) we know the special way bounds are allocated for complex arrays
43 * Always try to keep stack usage to a minimum: no recursive behaviour
44 * and no large stack allocs.
46 * General description.
47 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
48 * When the nursery is full we start a nursery collection: this is performed with a
50 * When the old generation is full we start a copying GC of the old generation as well:
51 * this will be changed to mark/compact in the future.
52 * The things that complicate this description are:
53 * *) pinned objects: we can't move them so we need to keep track of them
54 * *) no precise info of the thread stacks and registers: we need to be able to
55 * quickly find the objects that may be referenced conservatively and pin them
56 * (this makes the first issues more important)
57 * *) large objects are too expensive to be dealt with using copying GC: we handle them
58 * with mark/sweep during major collections
59 * *) some objects need to not move even if they are small (interned strings, Type handles):
60 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
61 * PinnedChunks regions
66 *) change the jit to emit write barrier calls when needed (we
67 can have specialized write barriers): done with icalls, still need to
68 use some specialized barriers
69 *) we could have a function pointer in MonoClass to implement
70 customized write barriers for value types
71 *) the write barrier code could be isolated in a couple of functions: when a
72 thread is stopped if it's inside the barrier it is let go again
73 until we stop outside of them (not really needed, see below GC-safe points)
74 *) investigate the stuff needed to advance a thread to a GC-safe
75 point (single-stepping, read from unmapped memory etc) and implement it
76 Not needed yet: since we treat the objects reachable from the stack/regs as
77 roots, we store the ptr and exec the write barrier so there is no race.
78 We may need this to solve the issue with setting the length of arrays and strings.
79 We may need this also for handling precise info on stacks, even simple things
80 as having uninitialized data on the stack and having to wait for the prolog
81 to zero it. Not an issue for the last frame that we scan conservatively.
82 We could always not trust the value in the slots anyway.
83 *) make the jit info table lock free
84 *) modify the jit to save info about references in stack locations:
85 this can be done just for locals as a start, so that at least
86 part of the stack is handled precisely.
87 *) Make the debug printf stuff thread and signal safe.
88 *) test/fix 64 bit issues
89 *) test/fix endianess issues
91 *) add batch moving profile info
92 *) add more timing info
93 *) there is a possible race when an array or string is created: the vtable is set,
94 but the length is set only later so if the GC needs to scan the object in that window,
95 it won't get the correct size for the object. The object can't have references and it will
96 be pinned, but a free memory fragment may be created that overlaps with it.
97 We should change the array max_length field to be at the same offset as the string length:
98 this way we can have a single special alloc function for them that sets the length.
99 Multi-dim arrays have the same issue for rank == 1 for the bounds data.
100 *) implement a card table as the write barrier instead of remembered sets?
101 *) some sort of blacklist support?
102 *) fin_ready_list is part of the root set, too
103 *) consider lowering the large object min size to 16/32KB or so and benchmark
104 *) once mark-compact is implemented we could still keep the
105 copying collector for the old generation and use it if we think
106 it is better (small heaps and no pinning object in the old
108 *) avoid the memory store from copy_object when not needed.
109 *) optimize the write barriers fastpath to happen in managed code
110 *) add an option to mmap the whole heap in one chunk: it makes for many
111 simplifications in the checks (put the nursery at the top and just use a single
112 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
113 not flexible (too much of the address space may be used by default or we can't
114 increase the heap as needed) and we'd need a race-free mechanism to return memory
115 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
116 was written to, munmap is needed, but the following mmap may not find the same segment
118 *) memzero the fragments after restarting the world and optionally a smaller chunk at a time
119 *) an additional strategy to realloc/expand the nursery when fully pinned is to start
120 allocating objects in the old generation. This means that we can't optimize away write
121 barrier calls in ctors (but that is not valid for other reasons, too).
122 *) add write barriers to the Clone methods
130 #include <semaphore.h>
134 #include <sys/types.h>
135 #include <sys/stat.h>
136 #include <sys/mman.h>
137 #include <sys/time.h>
140 #include "metadata/metadata-internals.h"
141 #include "metadata/class-internals.h"
142 #include "metadata/gc-internal.h"
143 #include "metadata/object-internals.h"
144 #include "metadata/threads.h"
145 #include "metadata/sgen-gc.h"
146 #include "metadata/mono-gc.h"
147 #include "utils/mono-mmap.h"
151 * ######################################################################
152 * ######## Types and constants used by the GC.
153 * ######################################################################
155 #if SIZEOF_VOID_P == 4
156 typedef guint32 mword;
158 typedef guint64 mword;
161 static int gc_initialized = 0;
162 static int gc_debug_level = 0;
163 static FILE* gc_debug_file;
166 mono_gc_flush_info (void)
168 fflush (gc_debug_file);
171 #define MAX_DEBUG_LEVEL 9
172 #define DEBUG(level,a) do {if ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level) a;} while (0)
174 #define TV_DECLARE(name) struct timeval name
175 #define TV_GETTIME(tv) gettimeofday (&(tv), NULL)
176 #define TV_ELAPSED(start,end) (int)((((end).tv_sec - (start).tv_sec) * 1000000) + end.tv_usec - start.tv_usec)
178 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
188 /* each request from the OS ends up in a GCMemSection */
189 typedef struct _GCMemSection GCMemSection;
190 struct _GCMemSection {
194 /* pointer where more data could be allocated if it fits */
198 * scan starts is an array of pointers to objects equally spaced in the allocation area
199 * They let use quickly find pinned objects from pinning pointers.
202 /* in major collections indexes in the pin_queue for objects that pin this section */
205 unsigned short num_scan_start;
209 /* large object space struct: 64+ KB */
210 /* we could make this limit much smaller to avoid memcpy copy
211 * and potentially have more room in the GC descriptor: need to measure
212 * This also means that such small OS objects will need to be
213 * allocated in a different way (using pinned chunks).
214 * We may want to put large but smaller than 64k objects in the fixed space
215 * when we move the object from one generation to another (to limit the
216 * pig in the snake effect).
217 * Note: it may be worth to have an optimized copy function, since we can
218 * assume that objects are aligned and have a multiple of 8 size.
219 * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
220 * true if MONO_ZERO_LEN_ARRAY is nonzero.
222 typedef struct _LOSObject LOSObject;
225 mword size; /* this is the object size */
226 int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN and data starting at same alignment */
229 char data [MONO_ZERO_LEN_ARRAY];
232 /* Pinned objects are allocated in the LOS space if bigger than half a page
233 * or from freelists otherwise. We assume that pinned objects are relatively few
234 * and they have a slow dying speed (like interned strings, thread objects).
235 * As such they will be collected only at major collections.
236 * free lists are not global: when we need memory we allocate a PinnedChunk.
237 * Each pinned chunk is made of several pages, the first of wich is used
238 * internally for bookeeping (here think of a page as 4KB). The bookeeping
239 * includes the freelists vectors and info about the object size of each page
240 * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
241 * a size is assigned to it, the page is divided in the proper chunks and each
242 * chunk is added to the freelist. To not waste space, the remaining space in the
243 * first page is used as objects of size 16 or 32 (need to measure which are more
245 * We use this same structure to allocate memory used internally by the GC, so
246 * we never use malloc/free if we need to alloc during collection: the world is stopped
247 * and malloc/free will deadlock.
248 * When we want to iterate over pinned objects, we just scan a page at a time
249 * linearly according to the size of objects in the page: the next pointer used to link
250 * the items in the freelist uses the same word as the vtable. Since we keep freelists
251 * for each pinned chunk, if the word points outside the pinned chunk it means
253 * We could avoid this expensive scanning in creative ways. We could have a policy
254 * of putting in the pinned space only objects we know about that have no struct fields
255 * with references and we can easily use a even expensive write barrier for them,
256 * since pointer writes on such objects should be rare.
257 * The best compromise is to just alloc interned strings and System.MonoType in them.
258 * It would be nice to allocate MonoThread in it, too: must check that we properly
259 * use write barriers so we don't have to do any expensive scanning of the whole pinned
260 * chunk list during minor collections. We can avoid it now because we alloc in it only
261 * reference-free objects.
263 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
264 #define MAX_FREELIST_SIZE 2048
265 #define PINNED_PAGE_SIZE (4096)
266 #define PINNED_CHUNK_MIN_SIZE (4096*8)
267 typedef struct _PinnedChunk PinnedChunk;
268 struct _PinnedChunk {
271 int *page_sizes; /* a 0 means the page is still unused */
274 void *data [1]; /* page sizes and free lists are stored here */
278 * The young generation is divided into fragments. This is because
279 * we can hand one fragments to a thread for lock-less fast alloc and
280 * because the young generation ends up fragmented anyway by pinned objects.
281 * Once a collection is done, a list of fragments is created. When doing
282 * thread local alloc we use smallish nurseries so we allow new threads to
283 * allocate memory from gen0 without triggering a collection. Threads that
284 * are found to allocate lots of memory are given bigger fragments. This
285 * should make the finalizer thread use little nursery memory after a while.
286 * We should start assigning threads very small fragments: if there are many
287 * threads the nursery will be full of reserved space that the threads may not
288 * use at all, slowing down allocation speed.
290 typedef struct _Fragment Fragment;
294 char *fragment_start;
295 char *fragment_limit; /* the current soft limit for allocation */
299 /* the runtime can register areas of memory as roots: we keep two lists of roots,
300 * a pinned root set for conservatively scanned roots and a normal one for
301 * precisely scanned roots (currently implemented as a single list).
303 typedef struct _RootRecord RootRecord;
311 /* for use with write barriers */
312 typedef struct _RememberedSet RememberedSet;
313 struct _RememberedSet {
317 mword data [MONO_ZERO_LEN_ARRAY];
320 /* we have 4 possible values in the low 2 bits */
322 REMSET_LOCATION, /* just a pointer to the exact location */
323 REMSET_RANGE, /* range of pointer fields */
324 REMSET_OBJECT, /* mark all the object for scanning */
325 REMSET_TYPE_MASK = 0x3
328 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
329 static pthread_key_t remembered_set_key;
330 static RememberedSet *global_remset;
331 static int store_to_global_remset = 0;
333 /* FIXME: later choose a size that takes into account the RememberedSet struct
334 * and doesn't waste any alloc paddin space.
336 #define DEFAULT_REMSET_SIZE 1024
337 static RememberedSet* alloc_remset (int size, gpointer id);
339 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
340 * no cast from a pointer to an integer
347 /* these bits are set in the object vtable: we could merge them since an object can be
348 * either pinned or forwarded but not both.
349 * We store them in the vtable slot because the bits are used in the sync block for
350 * other purpouses: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
351 * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
352 * would be an invalid combination for the monitor and hash code).
353 * The values are already shifted.
354 * The forwarding address is stored in the sync block.
356 #define FORWARDED_BIT 1
358 #define VTABLE_BITS_MASK 0x3
360 /* returns NULL if not forwarded, or the forwarded address */
361 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
362 /* set the forwarded address fw_addr for object obj */
363 #define forward_object(obj,fw_addr) do { \
364 ((mword*)(obj))[0] |= FORWARDED_BIT; \
365 ((mword*)(obj))[1] = (mword)(fw_addr); \
368 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
369 #define pin_object(obj) do { \
370 ((mword*)(obj))[0] |= PINNED_BIT; \
372 #define unpin_object(obj) do { \
373 ((mword*)(obj))[0] &= ~PINNED_BIT; \
378 * Since we set bits in the vtable, use the macro to load it from the pointer to
379 * an object that is potentially pinned.
381 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
384 safe_name (void* obj)
386 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
387 return vt->klass->name;
391 safe_object_get_size (MonoObject* o)
393 MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
394 if (klass == mono_defaults.string_class) {
395 return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
396 } else if (klass->rank) {
397 MonoArray *array = (MonoArray*)o;
398 size_t size = sizeof (MonoArray) + mono_array_element_size (klass) * mono_array_length (array);
402 size += sizeof (MonoArrayBounds) * klass->rank;
406 /* from a created object: the class must be inited already */
407 return klass->instance_size;
412 * ######################################################################
413 * ######## Global data.
414 * ######################################################################
416 static LOCK_DECLARE (gc_mutex);
417 static int gc_disabled = 0;
418 static int num_minor_gcs = 0;
419 static int num_major_gcs = 0;
421 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
422 //#define DEFAULT_NURSERY_SIZE (1024*512*125+4096*118)
423 #define DEFAULT_NURSERY_SIZE (1024*512*2)
424 #define DEFAULT_MAX_SECTION (DEFAULT_NURSERY_SIZE * 16)
425 #define DEFAULT_LOS_COLLECTION_TARGET (DEFAULT_NURSERY_SIZE * 2)
426 /* to quickly find the heard of an object pinned by a conservative address
427 * we keep track of the objects allocated for each SCAN_START_SIZE memory
428 * chunk in the nursery or other memory sections. Larger values have less
429 * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
431 #define SCAN_START_SIZE (4096*2)
432 /* the minimum size of a fragment that we consider useful for allocation */
433 #define FRAGMENT_MIN_SIZE (512)
434 /* This is a fixed value used for pinned chunks, not the system pagesize */
435 #define FREELIST_PAGESIZE 4096
437 static mword pagesize = 4096;
438 static mword nursery_size = DEFAULT_NURSERY_SIZE;
439 static mword next_section_size = DEFAULT_NURSERY_SIZE * 4;
440 static mword max_section_size = DEFAULT_MAX_SECTION;
441 static int section_size_used = 0;
442 static int degraded_mode = 0;
444 static LOSObject *los_object_list = NULL;
445 static mword los_memory_usage = 0;
446 static mword los_num_objects = 0;
447 static mword next_los_collection = 2*1024*1024; /* 2 MB, need to tune */
448 static mword total_alloc = 0;
449 /* use this to tune when to do a major/minor collection */
450 static mword memory_pressure = 0;
452 static GCMemSection *section_list = NULL;
453 static GCMemSection *nursery_section = NULL;
454 static mword lowest_heap_address = ~(mword)0;
455 static mword highest_heap_address = 0;
457 typedef struct _FinalizeEntry FinalizeEntry;
458 struct _FinalizeEntry {
461 void *data; /* can be a disappearing link or the data for the finalizer */
462 /* Note we could use just one pointer if we don't support multiple callbacks
463 * for finalizers and per-finalizer data and if we store the obj pointers
464 * in the link like libgc does
469 * The finalizable hash has the object as the key, the
470 * disappearing_link hash, has the link address as key.
472 static FinalizeEntry **finalizable_hash = NULL;
473 /* objects that are ready to be finalized */
474 static FinalizeEntry *fin_ready_list = NULL;
475 /* disappearing links use the same structure but a different list */
476 static FinalizeEntry **disappearing_link_hash = NULL;
477 static mword disappearing_link_hash_size = 0;
478 static mword finalizable_hash_size = 0;
480 static mword num_registered_finalizers = 0;
481 static mword num_ready_finalizers = 0;
482 static mword num_disappearing_links = 0;
483 static int no_finalize = 0;
485 /* keep each size a multiple of ALLOC_ALIGN */
486 /* on 64 bit systems 8 is likely completely unused. */
487 static const int freelist_sizes [] = {
488 8, 16, 24, 32, 40, 48, 64, 80,
489 96, 128, 160, 192, 224, 256, 320, 384,
490 448, 512, 584, 680, 816, 1024, 1360, 2048};
491 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
493 static char* max_pinned_chunk_addr = NULL;
494 static char* min_pinned_chunk_addr = (char*)-1;
495 /* pinned_chunk_list is used for allocations of objects that are never moved */
496 static PinnedChunk *pinned_chunk_list = NULL;
497 /* internal_chunk_list is used for allocating structures needed by the GC */
498 static PinnedChunk *internal_chunk_list = NULL;
501 obj_is_from_pinned_alloc (char *p)
503 PinnedChunk *chunk = pinned_chunk_list;
504 for (; chunk; chunk = chunk->next) {
505 if (p >= (char*)chunk->start_data && p < ((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))
511 /* registered roots: the key to the hash is the root start address */
512 static RootRecord **roots_hash = NULL;
513 static int roots_hash_size = 0;
514 static mword roots_size = 0; /* amount of memory in the root set */
515 static mword num_roots_entries = 0;
518 * The current allocation cursors
519 * We allocate objects in the nursery.
520 * The nursery is the area between nursery_start and nursery_real_end.
521 * nursery_next is the pointer to the space where the next object will be allocated.
522 * nursery_temp_end is the pointer to the end of the temporary space reserved for
523 * the allocation: this allows us to allow allocations inside the fragments of the
524 * nursery (the empty holes between pinned objects) and it allows us to set the
525 * scan starts at reasonable intervals.
526 * nursery_next and nursery_temp_end will become per-thread vars to allow lock-free
528 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
529 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
530 * At the next allocation, the area of the nursery where objects can be present is
531 * between MIN(nursery_first_pinned_start, first_fragment_start) and
532 * MAX(nursery_last_pinned_end, nursery_temp_end)
534 static char *nursery_start = NULL;
535 static char *nursery_next = NULL;
536 static char *nursery_temp_end = NULL;
537 static char *nursery_real_end = NULL;
538 static char *nursery_frag_real_end = NULL;
539 static char *nursery_first_pinned_start = NULL;
540 static char *nursery_last_pinned_end = NULL;
542 /* fragments that are free and ready to be used for allocation */
543 static Fragment *nursery_fragments = NULL;
544 /* freeelist of fragment structures */
545 static Fragment *fragment_freelist = NULL;
548 * used when moving the objects
549 * When the nursery is collected, objects are copied to to_space.
550 * The area between to_space and gray_objects is used as a stack
551 * of objects that need their fields checked for more references
553 * We should optimize somehow this mechanism to avoid rescanning
554 * ptr-free objects. The order is also probably not optimal: need to
555 * test cache misses and other graph traversal orders.
557 static char *to_space = NULL;
558 static char *gray_objects = NULL;
559 static char *to_space_end = NULL;
560 static GCMemSection *to_space_section = NULL;
562 /* objects bigger then this go into the large object space */
563 #define MAX_SMALL_OBJ_SIZE 0xffff
566 * ######################################################################
567 * ######## Macros and function declarations.
568 * ######################################################################
571 #define UPDATE_HEAP_BOUNDARIES(low,high) do { \
572 if ((mword)(low) < lowest_heap_address) \
573 lowest_heap_address = (mword)(low); \
574 if ((mword)(high) > highest_heap_address) \
575 highest_heap_address = (mword)(high); \
579 align_pointer (void *ptr)
581 mword p = (mword)ptr;
582 p += sizeof (gpointer) - 1;
583 p &= ~ (sizeof (gpointer) - 1);
587 /* forward declarations */
588 static void* get_internal_mem (size_t size);
589 static void free_internal_mem (void *addr);
590 static void* get_os_memory (size_t size, int activate);
591 static void free_os_memory (void *addr, size_t size);
592 static void report_internal_mem_usage (void);
594 static int stop_world (void);
595 static int restart_world (void);
596 static void pin_thread_data (void *start_nursery, void *end_nursery);
597 static void scan_from_remsets (void *start_nursery, void *end_nursery);
598 static void find_pinning_ref_from_thread (char *obj, size_t size);
599 static void update_current_thread_stack (void *start);
600 static GCMemSection* alloc_section (size_t size);
601 static void finalize_in_range (char *start, char *end);
602 static void null_link_in_range (char *start, char *end);
603 static gboolean search_fragment_for_size (size_t size);
604 static void mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end);
605 static void clear_remsets (void);
606 static void sweep_pinned_objects (void);
607 static void free_large_object (LOSObject *obj);
608 static void free_mem_section (GCMemSection *section);
611 * ######################################################################
612 * ######## GC descriptors
613 * ######################################################################
614 * Used to quickly get the info the GC needs about an object: size and
615 * where the references are held.
617 /* objects are aligned to 8 bytes boundaries
618 * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
619 * The low 3 bits define the type of the descriptor. The other bits
620 * depend on the type.
621 * As a general rule the 13 remaining low bits define the size, either
622 * of the whole object or of the elements in the arrays. While for objects
623 * the size is already in bytes, for arrays we need to shift, because
624 * array elements might be smaller than 8 bytes. In case of arrays, we
625 * use two bits to describe what the additional high bits represents,
626 * so the default behaviour can handle element sizes less than 2048 bytes.
627 * The high 16 bits, if 0 it means the object is pointer-free.
628 * This design should make it easy and fast to skip over ptr-free data.
629 * The first 4 types should cover >95% of the objects.
630 * Note that since the size of objects is limited to 64K, larger objects
631 * will be allocated in the large object heap.
632 * If we want 4-bytes alignment, we need to put vector and small bitmap
636 DESC_TYPE_RUN_LENGTH, /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
637 DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
638 DESC_TYPE_STRING, /* nothing */
639 DESC_TYPE_COMPLEX, /* index for bitmap into complex_descriptors */
640 DESC_TYPE_VECTOR, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
641 DESC_TYPE_ARRAY, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
642 DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
643 DESC_TYPE_COMPLEX_ARR, /* index for bitmap into complex_descriptors */
644 /* subtypes for arrays and vectors */
645 DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value */
646 DESC_TYPE_V_REFS, /* all the array elements are refs */
647 DESC_TYPE_V_RUN_LEN, /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
648 DESC_TYPE_V_BITMAP /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
651 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
652 #define LOW_TYPE_BITS 3
653 #define SMALL_BITMAP_SHIFT 16
654 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
655 #define VECTOR_INFO_SHIFT 14
656 #define VECTOR_ELSIZE_SHIFT 3
657 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
658 #define MAX_SMALL_SIZE ((1 << SMALL_BITMAP_SHIFT) - 1)
659 #define SMALL_SIZE_MASK 0xfff8
660 #define MAX_ELEMENT_SIZE 0x3ff
661 #define ELEMENT_SIZE_MASK (0x3ff << LOW_TYPE_BITS)
662 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
663 #define VECTOR_SUBTYPE_REFS (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
664 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
665 #define VECTOR_SUBTYPE_BITMAP (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
667 #define ALLOC_ALIGN 8
670 /* Root bitmap descriptors are simpler: the lower two bits describe the type
671 * and we either have 30/62 bitmap bits or nibble-based run-length,
672 * or a complex descriptor
675 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
678 ROOT_DESC_LARGE_BITMAP,
679 ROOT_DESC_TYPE_MASK = 0x3,
680 ROOT_DESC_TYPE_SHIFT = 2,
683 static gsize* complex_descriptors = NULL;
684 static int complex_descriptors_size = 0;
685 static int complex_descriptors_next = 0;
688 alloc_complex_descriptor (gsize *bitmap, int numbits)
690 int nwords = numbits/GC_BITS_PER_WORD + 2;
695 res = complex_descriptors_next;
696 /* linear search, so we don't have duplicates with domain load/unload
697 * this should not be performance critical or we'd have bigger issues
698 * (the number and size of complex descriptors should be small).
700 for (i = 0; i < complex_descriptors_next; ) {
701 if (complex_descriptors [i] == nwords) {
703 for (j = 0; j < nwords - 1; ++j) {
704 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
714 i += complex_descriptors [i];
716 if (complex_descriptors_next + nwords > complex_descriptors_size) {
717 int new_size = complex_descriptors_size * 2 + nwords;
718 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
719 complex_descriptors_size = new_size;
721 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
722 complex_descriptors_next += nwords;
723 complex_descriptors [res] = nwords;
724 for (i = 0; i < nwords - 1; ++i) {
725 complex_descriptors [res + 1 + i] = bitmap [i];
726 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
733 * Descriptor builders.
736 mono_gc_make_descr_for_string (void)
738 return (void*) DESC_TYPE_STRING;
742 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
744 int first_set = -1, num_set = 0, last_set = -1, i;
746 size_t stored_size = obj_size;
747 stored_size += ALLOC_ALIGN - 1;
748 stored_size &= ~(ALLOC_ALIGN - 1);
749 for (i = 0; i < numbits; ++i) {
750 if (bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
757 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
758 /* check run-length encoding first: one byte offset, one byte number of pointers
759 * on 64 bit archs, we can have 3 runs, just one on 32.
760 * It may be better to use nibbles.
763 desc = DESC_TYPE_RUN_LENGTH | stored_size;
764 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %d\n", (void*)desc, stored_size));
766 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
767 desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
768 DEBUG (6, fprintf (gc_debug_file, "Runlen descriptor %p, size: %d, first set: %d, num set: %d\n", (void*)desc, stored_size, first_set, num_set));
771 /* we know the 2-word header is ptr-free */
772 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
773 desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
774 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %d, last set: %d\n", (void*)desc, stored_size, last_set));
778 /* we know the 2-word header is ptr-free */
779 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
780 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
781 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %d, last set: %d\n", (void*)desc, stored_size, last_set));
784 /* it's a complex object ... */
785 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
789 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
791 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
793 int first_set = -1, num_set = 0, last_set = -1, i;
794 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
795 for (i = 0; i < numbits; ++i) {
796 if (elem_bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
803 if (elem_size <= MAX_ELEMENT_SIZE) {
804 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
806 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
808 /* Note: we also handle structs with just ref fields */
809 if (num_set * sizeof (gpointer) == elem_size) {
810 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
812 /* FIXME: try run-len first */
813 /* Note: we can't skip the object header here, because it's not present */
814 if (last_set <= SMALL_BITMAP_SIZE) {
815 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
818 /* it's am array of complex structs ... */
819 desc = DESC_TYPE_COMPLEX_ARR;
820 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
824 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
825 #define STRING_SIZE(size,str) do { \
826 (size) = sizeof (MonoString) + 2 * (mono_string_length ((MonoString*)(str)) + 1); \
827 (size) += (ALLOC_ALIGN - 1); \
828 (size) &= ~(ALLOC_ALIGN - 1); \
831 #define OBJ_RUN_LEN_SIZE(size,vt,obj) do { \
832 (size) = (vt)->desc & 0xfff8; \
835 #define OBJ_BITMAP_SIZE(size,vt,obj) do { \
836 (size) = (vt)->desc & 0xfff8; \
839 //#define PREFETCH(addr) __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(addr)))
840 #define PREFETCH(addr)
842 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
843 #define OBJ_RUN_LEN_FOREACH_PTR(vt,obj) do { \
844 if ((vt)->desc & 0xffff0000) { \
845 /* there are pointers */ \
846 void **_objptr_end; \
847 void **_objptr = (void**)(obj); \
848 _objptr += ((vt)->desc >> 16) & 0xff; \
849 _objptr_end = _objptr + (((vt)->desc >> 24) & 0xff); \
850 while (_objptr < _objptr_end) { \
851 HANDLE_PTR (_objptr, (obj)); \
857 /* a bitmap desc means that there are pointer references or we'd have
858 * choosen run-length, instead: add an assert to check.
860 #define OBJ_BITMAP_FOREACH_PTR(vt,obj) do { \
861 /* there are pointers */ \
862 void **_objptr = (void**)(obj); \
863 gsize _bmap = (vt)->desc >> 16; \
864 _objptr += OBJECT_HEADER_WORDS; \
867 HANDLE_PTR (_objptr, (obj)); \
874 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj) do { \
875 /* there are pointers */ \
876 void **_objptr = (void**)(obj); \
877 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS; \
878 _objptr += OBJECT_HEADER_WORDS; \
881 HANDLE_PTR (_objptr, (obj)); \
888 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do { \
889 /* there are pointers */ \
890 void **_objptr = (void**)(obj); \
891 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
892 int bwords = (*bitmap_data) - 1; \
893 void **start_run = _objptr; \
896 MonoObject *myobj = (MonoObject*)obj; \
897 g_print ("found %d at %p (0x%x): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
899 while (bwords-- > 0) { \
900 gsize _bmap = *bitmap_data++; \
901 _objptr = start_run; \
902 /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/ \
905 HANDLE_PTR (_objptr, (obj)); \
910 start_run += GC_BITS_PER_WORD; \
914 /* this one is untested */
915 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj) do { \
916 /* there are pointers */ \
917 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
918 int mbwords = (*mbitmap_data++) - 1; \
919 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass); \
920 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
921 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
923 MonoObject *myobj = (MonoObject*)start; \
924 g_print ("found %d at %p (0x%x): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
926 while (e_start < e_end) { \
927 void **_objptr = (void**)e_start; \
928 gsize *bitmap_data = mbitmap_data; \
929 unsigned int bwords = mbwords; \
930 while (bwords-- > 0) { \
931 gsize _bmap = *bitmap_data++; \
932 void **start_run = _objptr; \
933 /*g_print ("bitmap: 0x%x\n", _bmap);*/ \
936 HANDLE_PTR (_objptr, (obj)); \
941 _objptr = start_run + GC_BITS_PER_WORD; \
943 e_start += el_size; \
947 #define OBJ_VECTOR_FOREACH_PTR(vt,obj) do { \
948 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */ \
949 if ((vt)->desc & 0xffffc000) { \
950 int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE; \
951 /* there are pointers */ \
952 int etype = (vt)->desc & 0xc000; \
953 if (etype == (DESC_TYPE_V_REFS << 14)) { \
954 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector)); \
955 void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
956 /* Note: this code can handle also arrays of struct with only references in them */ \
957 while (p < end_refs) { \
958 HANDLE_PTR (p, (obj)); \
961 } else if (etype == DESC_TYPE_V_RUN_LEN << 14) { \
962 int offset = ((vt)->desc >> 16) & 0xff; \
963 int num_refs = ((vt)->desc >> 24) & 0xff; \
964 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
965 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
966 while (e_start < e_end) { \
967 void **p = (void**)e_start; \
970 for (i = 0; i < num_refs; ++i) { \
971 HANDLE_PTR (p + i, (obj)); \
973 e_start += el_size; \
975 } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
976 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
977 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
978 while (e_start < e_end) { \
979 void **p = (void**)e_start; \
980 gsize _bmap = (vt)->desc >> 16; \
981 /* Note: there is no object header here to skip */ \
984 HANDLE_PTR (p, (obj)); \
989 e_start += el_size; \
995 static mword new_obj_references = 0;
996 static mword obj_references_checked = 0;
999 #define HANDLE_PTR(ptr,obj) do { \
1000 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
1001 new_obj_references++; \
1002 /*printf ("bogus ptr %p found at %p in object %p (%s.%s)\n", *(ptr), (ptr), o, o->vtable->klass->name_space, o->vtable->klass->name);*/ \
1004 obj_references_checked++; \
1009 * ######################################################################
1010 * ######## Detecting and removing garbage.
1011 * ######################################################################
1012 * This section of code deals with detecting the objects no longer in use
1013 * and reclaiming the memory.
1015 static void __attribute__((noinline))
1016 scan_area (char *start, char *end)
1021 int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
1022 new_obj_references = 0;
1023 obj_references_checked = 0;
1024 while (start < end) {
1025 if (!*(void**)start) {
1026 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1029 vt = (GCVTable*)LOAD_VTABLE (start);
1030 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
1032 MonoObject *obj = (MonoObject*)start;
1033 g_print ("found at %p (0x%x): %s.%s\n", start, vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
1035 type = vt->desc & 0x7;
1036 if (type == DESC_TYPE_STRING) {
1037 STRING_SIZE (skip_size, start);
1041 } else if (type == DESC_TYPE_RUN_LENGTH) {
1042 OBJ_RUN_LEN_SIZE (skip_size, vt, start);
1043 g_assert (skip_size);
1044 OBJ_RUN_LEN_FOREACH_PTR (vt,start);
1048 } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1049 skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1050 skip_size *= mono_array_length ((MonoArray*)start);
1051 skip_size += sizeof (MonoArray);
1052 skip_size += (ALLOC_ALIGN - 1);
1053 skip_size &= ~(ALLOC_ALIGN - 1);
1054 OBJ_VECTOR_FOREACH_PTR (vt, start);
1055 if (type == DESC_TYPE_ARRAY) {
1056 /* account for the bounds */
1061 } else if (type == DESC_TYPE_SMALL_BITMAP) {
1062 OBJ_BITMAP_SIZE (skip_size, vt, start);
1063 g_assert (skip_size);
1064 OBJ_BITMAP_FOREACH_PTR (vt,start);
1068 } else if (type == DESC_TYPE_LARGE_BITMAP) {
1069 skip_size = safe_object_get_size ((MonoObject*)start);
1070 skip_size += (ALLOC_ALIGN - 1);
1071 skip_size &= ~(ALLOC_ALIGN - 1);
1072 OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
1076 } else if (type == DESC_TYPE_COMPLEX) {
1077 /* this is a complex object */
1078 skip_size = safe_object_get_size ((MonoObject*)start);
1079 skip_size += (ALLOC_ALIGN - 1);
1080 skip_size &= ~(ALLOC_ALIGN - 1);
1081 OBJ_COMPLEX_FOREACH_PTR (vt, start);
1085 } else if (type == DESC_TYPE_COMPLEX_ARR) {
1086 /* this is an array of complex structs */
1087 skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
1088 skip_size *= mono_array_length ((MonoArray*)start);
1089 skip_size += sizeof (MonoArray);
1090 skip_size += (ALLOC_ALIGN - 1);
1091 skip_size &= ~(ALLOC_ALIGN - 1);
1092 OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
1093 if (type == DESC_TYPE_ARRAY) {
1094 /* account for the bounds */
1103 /*printf ("references to new nursery %p-%p (size: %dk): %d, checked: %d\n", old_start, end, (end-old_start)/1024, new_obj_references, obj_references_checked);
1104 printf ("\tstrings: %d, runl: %d, vector: %d, bitmaps: %d, lbitmaps: %d, complex: %d\n",
1105 type_str, type_rlen, type_vector, type_bitmap, type_lbit, type_complex);*/
1108 static void __attribute__((noinline))
1109 scan_area_for_domain (MonoDomain *domain, char *start, char *end)
1114 while (start < end) {
1115 if (!*(void**)start) {
1116 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1119 vt = (GCVTable*)LOAD_VTABLE (start);
1120 /* handle threads someway (maybe insert the root domain vtable?) */
1121 if (mono_object_domain (start) == domain && vt->klass != mono_defaults.thread_class) {
1122 DEBUG (1, fprintf (gc_debug_file, "Need to cleanup object %p, (%s)\n", start, safe_name (start)));
1127 type = vt->desc & 0x7;
1128 if (type == DESC_TYPE_STRING) {
1129 STRING_SIZE (skip_size, start);
1130 if (remove) memset (start, 0, skip_size);
1133 } else if (type == DESC_TYPE_RUN_LENGTH) {
1134 OBJ_RUN_LEN_SIZE (skip_size, vt, start);
1135 g_assert (skip_size);
1136 if (remove) memset (start, 0, skip_size);
1139 } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1140 skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1141 skip_size *= mono_array_length ((MonoArray*)start);
1142 skip_size += sizeof (MonoArray);
1143 skip_size += (ALLOC_ALIGN - 1);
1144 skip_size &= ~(ALLOC_ALIGN - 1);
1145 if (type == DESC_TYPE_ARRAY) {
1146 /* account for the bounds */
1148 if (remove) memset (start, 0, skip_size);
1151 } else if (type == DESC_TYPE_SMALL_BITMAP) {
1152 OBJ_BITMAP_SIZE (skip_size, vt, start);
1153 g_assert (skip_size);
1154 if (remove) memset (start, 0, skip_size);
1157 } else if (type == DESC_TYPE_LARGE_BITMAP) {
1158 skip_size = safe_object_get_size ((MonoObject*)start);
1159 skip_size += (ALLOC_ALIGN - 1);
1160 skip_size &= ~(ALLOC_ALIGN - 1);
1161 if (remove) memset (start, 0, skip_size);
1164 } else if (type == DESC_TYPE_COMPLEX) {
1165 /* this is a complex object */
1166 skip_size = safe_object_get_size ((MonoObject*)start);
1167 skip_size += (ALLOC_ALIGN - 1);
1168 skip_size &= ~(ALLOC_ALIGN - 1);
1169 if (remove) memset (start, 0, skip_size);
1172 } else if (type == DESC_TYPE_COMPLEX_ARR) {
1173 /* this is an array of complex structs */
1174 skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
1175 skip_size *= mono_array_length ((MonoArray*)start);
1176 skip_size += sizeof (MonoArray);
1177 skip_size += (ALLOC_ALIGN - 1);
1178 skip_size &= ~(ALLOC_ALIGN - 1);
1179 if (type == DESC_TYPE_ARRAY) {
1180 /* account for the bounds */
1182 if (remove) memset (start, 0, skip_size);
1192 * When appdomains are unloaded we can easily remove objects that have finalizers,
1193 * but all the others could still be present in random places on the heap.
1194 * We need a sweep to get rid of them even though it's going to be costly
1196 * The reason we need to remove them is because we access the vtable and class
1197 * structures to know the object size and the reference bitmap: once the domain is
1198 * unloaded the point to random memory.
1201 mono_gc_clear_domain (MonoDomain * domain)
1203 GCMemSection *section;
1205 for (section = section_list; section; section = section->next) {
1206 scan_area_for_domain (domain, section->data, section->end_data);
1208 /* FIXME: handle big and fixed objects (we remove, don't clear in this case) */
1213 add_to_global_remset (gpointer ptr)
1216 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1217 if (global_remset->store_next < global_remset->end_set) {
1218 *(global_remset->store_next++) = (mword)ptr;
1221 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1222 rs->next = global_remset;
1224 *(global_remset->store_next++) = (mword)ptr;
1228 * This is how the copying happens from the nursery to the old generation.
1229 * We assume that at this time all the pinned objects have been identified and
1231 * We run scan_object() for each pinned object so that each referenced
1232 * objects if possible are copied. The new gray objects created can have
1233 * scan_object() run on them right away, too.
1234 * Then we run copy_object() for the precisely tracked roots. At this point
1235 * all the roots are either gray or black. We run scan_object() on the gray
1236 * objects until no more gray objects are created.
1237 * At the end of the process we walk again the pinned list and we unmark
1238 * the pinned flag. As we go we also create the list of free space for use
1239 * in the next allocation runs.
1241 * We need to remember objects from the old generation that point to the new one
1242 * (or just addresses?).
1244 * copy_object could be made into a macro once debugged (use inline for now).
1247 static char* __attribute__((noinline))
1248 copy_object (char *obj, char *from_space_start, char *from_space_end)
1250 if (obj >= from_space_start && obj < from_space_end && (obj < to_space || obj >= to_space_end)) {
1254 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p", obj));
1255 if ((forwarded = object_is_forwarded (obj))) {
1256 g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr);
1257 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
1260 if (object_is_pinned (obj)) {
1261 g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr);
1262 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
1265 objsize = safe_object_get_size ((MonoObject*)obj);
1266 objsize += ALLOC_ALIGN - 1;
1267 objsize &= ~(ALLOC_ALIGN - 1);
1268 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %d)\n", gray_objects, ((MonoObject*)obj)->vtable->klass->name, objsize));
1269 /* FIXME: handle pinned allocs:
1270 * Large objects are simple, at least until we always follow the rule:
1271 * if objsize >= MAX_SMALL_OBJ_SIZE, pin the object and return it.
1272 * At the end of major collections, we walk the los list and if
1273 * the object is pinned, it is marked, otherwise it can be freed.
1275 if (objsize >= MAX_SMALL_OBJ_SIZE || (obj >= min_pinned_chunk_addr && obj < max_pinned_chunk_addr && obj_is_from_pinned_alloc (obj))) {
1276 DEBUG (9, fprintf (gc_debug_file, "Marked LOS/Pinned %p (%s), size: %d\n", obj, safe_name (obj), objsize));
1280 /* ok, the object is not pinned, we can move it */
1281 /* use a optimized memcpy here */
1286 char* edi = gray_objects;
1287 __asm__ __volatile__(
1289 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
1290 : "0" (objsize/4), "1" (edi),"2" (esi)
1295 memcpy (gray_objects, obj, objsize);
1297 /* adjust array->bounds */
1298 vt = ((MonoObject*)obj)->vtable;
1299 g_assert (vt->gc_descr);
1300 if (vt->rank && ((MonoArray*)obj)->bounds) {
1301 MonoArray *array = (MonoArray*)gray_objects;
1302 array->bounds = (MonoArrayBounds*)((char*)gray_objects + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
1303 DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %d, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
1305 /* set the forwarding pointer */
1306 forward_object (obj, gray_objects);
1308 to_space_section->scan_starts [((char*)obj - (char*)to_space_section->data)/SCAN_START_SIZE] = obj;
1309 gray_objects += objsize;
1310 DEBUG (8, g_assert (gray_objects <= to_space_end));
1317 #define HANDLE_PTR(ptr,obj) do { \
1319 void *__old = *(ptr); \
1320 *(ptr) = copy_object (*(ptr), from_start, from_end); \
1321 DEBUG (9, if (__old != *(ptr)) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
1322 if (*(ptr) >= (void*)from_start && *(ptr) < (void*)from_end) \
1323 add_to_global_remset ((ptr)); \
1328 * Scan the object pointed to by @start for references to
1329 * other objects between @from_start and @from_end and copy
1330 * them to the gray_objects area.
1331 * Returns a pointer to the end of the object.
1334 scan_object (char *start, char* from_start, char* from_end)
1339 vt = (GCVTable*)LOAD_VTABLE (start);
1340 //type = vt->desc & 0x7;
1342 /* gcc should be smart enough to remove the bounds check, but it isn't:( */
1343 switch (vt->desc & 0x7) {
1344 //if (type == DESC_TYPE_STRING) {
1345 case DESC_TYPE_STRING:
1346 STRING_SIZE (skip_size, start);
1347 return start + skip_size;
1348 //} else if (type == DESC_TYPE_RUN_LENGTH) {
1349 case DESC_TYPE_RUN_LENGTH:
1350 OBJ_RUN_LEN_FOREACH_PTR (vt,start);
1351 OBJ_RUN_LEN_SIZE (skip_size, vt, start);
1352 g_assert (skip_size);
1353 return start + skip_size;
1354 //} else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1355 case DESC_TYPE_ARRAY:
1356 case DESC_TYPE_VECTOR:
1357 OBJ_VECTOR_FOREACH_PTR (vt, start);
1358 skip_size = safe_object_get_size ((MonoObject*)start);
1360 skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1361 skip_size *= mono_array_length ((MonoArray*)start);
1362 skip_size += sizeof (MonoArray);
1364 skip_size += (ALLOC_ALIGN - 1);
1365 skip_size &= ~(ALLOC_ALIGN - 1);
1366 return start + skip_size;
1367 //} else if (type == DESC_TYPE_SMALL_BITMAP) {
1368 case DESC_TYPE_SMALL_BITMAP:
1369 OBJ_BITMAP_FOREACH_PTR (vt,start);
1370 OBJ_BITMAP_SIZE (skip_size, vt, start);
1371 return start + skip_size;
1372 //} else if (type == DESC_TYPE_LARGE_BITMAP) {
1373 case DESC_TYPE_LARGE_BITMAP:
1374 OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
1375 skip_size = safe_object_get_size ((MonoObject*)start);
1376 skip_size += (ALLOC_ALIGN - 1);
1377 skip_size &= ~(ALLOC_ALIGN - 1);
1378 return start + skip_size;
1379 //} else if (type == DESC_TYPE_COMPLEX) {
1380 case DESC_TYPE_COMPLEX:
1381 OBJ_COMPLEX_FOREACH_PTR (vt, start);
1382 /* this is a complex object */
1383 skip_size = safe_object_get_size ((MonoObject*)start);
1384 skip_size += (ALLOC_ALIGN - 1);
1385 skip_size &= ~(ALLOC_ALIGN - 1);
1386 return start + skip_size;
1387 //} else if (type == DESC_TYPE_COMPLEX_ARR) {
1388 case DESC_TYPE_COMPLEX_ARR:
1389 OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
1390 /* this is an array of complex structs */
1391 skip_size = safe_object_get_size ((MonoObject*)start);
1393 skip_size = mono_array_element_size (((MonoObject*)start)->vtable->klass);
1394 skip_size *= mono_array_length ((MonoArray*)start);
1395 skip_size += sizeof (MonoArray);
1397 skip_size += (ALLOC_ALIGN - 1);
1398 skip_size &= ~(ALLOC_ALIGN - 1);
1399 return start + skip_size;
1401 g_assert_not_reached ();
1406 * Addresses from start to end are already sorted. This function finds the object header
1407 * for each address and pins the object. The addresses must be inside the passed section.
1408 * Return the number of pinned objects.
1411 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
1416 void *last_obj = NULL;
1417 size_t last_obj_size = 0;
1420 void **definitely_pinned = start;
1421 while (start < end) {
1423 /* the range check should be reduntant */
1424 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1425 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1426 /* multiple pointers to the same object */
1427 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1431 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1432 search_start = (void*)section->scan_starts [idx];
1433 if (!search_start || search_start > addr) {
1436 search_start = section->scan_starts [idx];
1437 if (search_start && search_start <= addr)
1440 if (!search_start || search_start > addr)
1441 search_start = start_nursery;
1443 if (search_start < last_obj)
1444 search_start = (char*)last_obj + last_obj_size;
1445 /* now addr should be in an object a short distance from search_start
1446 * Note that search_start must point to zeroed mem or point to an object.
1449 if (!*(void**)search_start) {
1450 mword p = (mword)search_start;
1451 p += sizeof (gpointer);
1452 p += ALLOC_ALIGN - 1;
1453 p &= ~(ALLOC_ALIGN - 1);
1454 search_start = (void*)p;
1457 last_obj = search_start;
1458 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
1459 last_obj_size += ALLOC_ALIGN - 1;
1460 last_obj_size &= ~(ALLOC_ALIGN - 1);
1461 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %d\n", last_obj, safe_name (last_obj), last_obj_size));
1462 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1463 DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
1464 pin_object (search_start);
1465 definitely_pinned [count] = search_start;
1469 /* skip to the next object */
1470 search_start = (void*)((char*)search_start + last_obj_size);
1471 } while (search_start <= addr);
1472 /* we either pinned the correct object or we ignored the addr because
1473 * it points to unused zeroed memory.
1479 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1483 static void** pin_queue;
1484 static int pin_queue_size = 0;
1485 static int next_pin_slot = 0;
1490 gap = (gap * 10) / 13;
1491 if (gap == 9 || gap == 10)
1500 compare_addr (const void *a, const void *b)
1502 return *(const void **)a - *(const void **)b;
1506 /* sort the addresses in array in increasing order */
1508 sort_addresses (void **array, int size)
1511 * qsort is slower as predicted.
1512 * qsort (array, size, sizeof (gpointer), compare_addr);
1519 gap = new_gap (gap);
1522 for (i = 0; i < end; i++) {
1524 if (array [i] > array [j]) {
1525 void* val = array [i];
1526 array [i] = array [j];
1531 if (gap == 1 && !swapped)
1537 print_nursery_gaps (void* start_nursery, void *end_nursery)
1540 gpointer first = start_nursery;
1542 for (i = 0; i < next_pin_slot; ++i) {
1543 next = pin_queue [i];
1544 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %d\n", first, next, (char*)next-(char*)first);
1548 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %d\n", first, next, (char*)next-(char*)first);
1551 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1553 optimize_pin_queue (int start_slot)
1555 void **start, **cur, **end;
1556 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1557 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1558 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1559 if ((next_pin_slot - start_slot) > 1)
1560 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1561 start = cur = pin_queue + start_slot;
1562 end = pin_queue + next_pin_slot;
1565 while (*start == *cur && cur < end)
1569 next_pin_slot = start - pin_queue;
1570 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1571 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1576 realloc_pin_queue (void)
1578 int new_size = pin_queue_size? pin_queue_size + pin_queue_size/2: 1024;
1579 void **new_pin = get_internal_mem (sizeof (void*) * new_size);
1580 memcpy (new_pin, pin_queue, sizeof (void*) * next_pin_slot);
1581 free_internal_mem (pin_queue);
1582 pin_queue = new_pin;
1583 pin_queue_size = new_size;
1584 DEBUG (4, fprintf (gc_debug_file, "Reallocated pin queue to size: %d\n", new_size));
1588 * Scan the memory between start and end and queue values which could be pointers
1589 * to the area between start_nursery and end_nursery for later consideration.
1590 * Typically used for thread stacks.
1593 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery)
1596 while (start < end) {
1597 if (*start >= start_nursery && *start < end_nursery) {
1599 * *start can point to the middle of an object
1600 * note: should we handle pointing at the end of an object?
1601 * pinning in C# code disallows pointing at the end of an object
1602 * but there is some small chance that an optimizing C compiler
1603 * may keep the only reference to an object by pointing
1604 * at the end of it. We ignore this small chance for now.
1605 * Pointers to the end of an object are indistinguishable
1606 * from pointers to the start of the next object in memory
1607 * so if we allow that we'd need to pin two objects...
1608 * We queue the pointer in an array, the
1609 * array will then be sorted and uniqued. This way
1610 * we can coalesce several pinning pointers and it should
1611 * be faster since we'd do a memory scan with increasing
1612 * addresses. Note: we can align the address to the allocation
1613 * alignment, so the unique process is more effective.
1615 mword addr = (mword)*start;
1616 addr &= ~(ALLOC_ALIGN - 1);
1617 if (next_pin_slot >= pin_queue_size)
1618 realloc_pin_queue ();
1619 pin_queue [next_pin_slot++] = (void*)addr;
1620 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
1625 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1629 * If generation is 0, just mark objects in the nursery, the others we don't care,
1630 * since they are not going to move anyway.
1631 * There are different areas that are scanned for pinned pointers:
1632 * *) the thread stacks (when jit support is ready only the unmanaged frames)
1633 * *) the pinned handle table
1634 * *) the pinned roots
1636 * Note: when we'll use a write barrier for old to new gen references, we need to
1637 * keep track of old gen objects that point to pinned new gen objects because in that
1638 * case the referenced object will be moved maybe at the next collection, but there
1639 * is no write in the old generation area where the pinned object is referenced
1640 * and we may not consider it as reachable.
1643 mark_pinned_objects (int generation)
1648 * Debugging function: find in the conservative roots where @obj is being pinned.
1651 find_pinning_reference (char *obj, size_t size)
1655 char *endobj = obj + size;
1656 for (i = 0; i < roots_hash_size; ++i) {
1657 for (root = roots_hash [i]; root; root = root->next) {
1658 /* if desc is non-null it has precise info */
1659 if (!root->root_desc) {
1660 char ** start = (char**)root->start_root;
1661 while (start < (char**)root->end_root) {
1662 if (*start >= obj && *start < endobj) {
1663 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in pinned roots %p-%p (at %p in record %p)\n", obj, root->start_root, root->end_root, start, root));
1670 find_pinning_ref_from_thread (obj, size);
1674 * The first thing we do in a collection is to identify pinned objects.
1675 * This function considers all the areas of memory that need to be
1676 * conservatively scanned.
1679 pin_from_roots (void *start_nursery, void *end_nursery)
1683 DEBUG (3, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d entries)\n", (int)roots_size, num_roots_entries));
1684 /* objects pinned from the API are inside these roots */
1685 for (i = 0; i < roots_hash_size; ++i) {
1686 for (root = roots_hash [i]; root; root = root->next) {
1687 /* if desc is non-null it has precise info */
1688 if (root->root_desc)
1690 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
1691 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery);
1694 /* now deal with the thread stacks
1695 * in the future we should be able to conservatively scan only:
1696 * *) the cpu registers
1697 * *) the unmanaged stack frames
1698 * *) the _last_ managed stack frame
1699 * *) pointers slots in managed frames
1701 pin_thread_data (start_nursery, end_nursery);
1705 * The memory area from start_root to end_root contains pointers to objects.
1706 * Their position is precisely described by @desc (this means that the pointer
1707 * can be either NULL or the pointer to the start of an object).
1708 * This functions copies them to to_space updates them.
1711 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
1713 switch (desc & ROOT_DESC_TYPE_MASK) {
1714 case ROOT_DESC_BITMAP:
1715 desc >>= ROOT_DESC_TYPE_SHIFT;
1717 if ((desc & 1) && *start_root) {
1718 *start_root = copy_object (*start_root, n_start, n_end);
1719 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root)); \
1725 case ROOT_DESC_RUN_LEN:
1726 case ROOT_DESC_LARGE_BITMAP:
1727 g_assert_not_reached ();
1732 alloc_fragment (void)
1734 Fragment *frag = fragment_freelist;
1736 fragment_freelist = frag->next;
1740 frag = get_internal_mem (sizeof (Fragment));
1746 * Allocate and setup the data structures needed to be able to allocate objects
1747 * in the nursery. The nursery is stored in nursery_section.
1750 alloc_nursery (void)
1752 GCMemSection *section;
1757 if (nursery_section)
1759 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %d\n", nursery_size));
1760 /* later we will alloc a larger area for the nursery but only activate
1761 * what we need. The rest will be used as expansion if we have too many pinned
1762 * objects in the existing nursery.
1764 /* FIXME: handle OOM */
1765 section = get_internal_mem (sizeof (GCMemSection));
1766 data = get_os_memory (nursery_size, TRUE);
1767 nursery_start = nursery_next = data;
1768 nursery_real_end = data + nursery_size;
1769 nursery_temp_end = data + SCAN_START_SIZE;
1770 UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
1771 total_alloc += nursery_size;
1772 DEBUG (4, fprintf (gc_debug_file, "Expanding heap size: %d, total: %d\n", nursery_size, total_alloc));
1773 section->data = section->next_data = data;
1774 section->size = nursery_size;
1775 section->end_data = nursery_real_end;
1776 scan_starts = nursery_size / SCAN_START_SIZE;
1777 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts);
1778 section->num_scan_start = scan_starts;
1780 /* add to the section list */
1781 section->next = section_list;
1782 section_list = section;
1784 nursery_section = section;
1786 /* Setup the single first large fragment */
1787 frag = alloc_fragment ();
1788 frag->fragment_start = nursery_start;
1789 frag->fragment_limit = nursery_start;
1790 frag->fragment_end = nursery_real_end;
1791 nursery_frag_real_end = nursery_real_end;
1792 /* FIXME: frag here is lost */
1796 * Update roots in the old generation. Since we currently don't have the
1797 * info from the write barriers, we just scan all the objects.
1800 scan_old_generation (char *start, char* end)
1802 GCMemSection *section;
1804 LOSObject *big_object;
1807 for (section = section_list; section; section = section->next) {
1808 if (section == nursery_section)
1810 DEBUG (2, fprintf (gc_debug_file, "Scan of old section: %p-%p, size: %d\n", section->data, section->next_data, (int)(section->next_data - section->data)));
1811 /* we have to deal with zeroed holes in old generation (truncated strings ...) */
1813 while (p < section->next_data) {
1818 DEBUG (8, fprintf (gc_debug_file, "Precise old object scan of %p (%s)\n", p, safe_name (p)));
1819 p = scan_object (p, start, end);
1822 /* scan the old object space, too */
1823 for (big_object = los_object_list; big_object; big_object = big_object->next) {
1824 DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %d\n", big_object->data, safe_name (big_object->data), big_object->size));
1825 scan_object (big_object->data, start, end);
1827 /* scan the list of objects ready for finalization */
1828 for (fin = fin_ready_list; fin; fin = fin->next) {
1829 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
1830 fin->object = copy_object (fin->object, start, end);
1834 static mword fragment_total = 0;
1836 * We found a fragment of free memory in the nursery: memzero it and if
1837 * it is big enough, add it to the list of fragments that can be used for
1841 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
1844 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %d\n", frag_start, frag_end, frag_size));
1845 /* memsetting just the first chunk start is bound to provide better cache locality */
1846 memset (frag_start, 0, frag_size);
1847 /* Not worth dealing with smaller fragments: need to tune */
1848 if (frag_size >= FRAGMENT_MIN_SIZE) {
1849 fragment = alloc_fragment ();
1850 fragment->fragment_start = frag_start;
1851 fragment->fragment_limit = frag_start;
1852 fragment->fragment_end = frag_end;
1853 fragment->next = nursery_fragments;
1854 nursery_fragments = fragment;
1855 fragment_total += frag_size;
1860 scan_needed_big_objects (char *start_addr, char *end_addr)
1862 LOSObject *big_object;
1864 for (big_object = los_object_list; big_object; big_object = big_object->next) {
1865 if (!big_object->scanned && object_is_pinned (big_object->data)) {
1866 DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %d\n", big_object->data, safe_name (big_object->data), big_object->size));
1867 scan_object (big_object->data, start_addr, end_addr);
1868 big_object->scanned = TRUE;
1876 drain_gray_stack (char *start_addr, char *end_addr)
1880 int fin_ready, bigo_scanned_num;
1884 * We copied all the reachable objects. Now it's the time to copy
1885 * the objects that were not referenced by the roots, but by the copied objects.
1886 * we built a stack of objects pointed to by gray_start: they are
1887 * additional roots and we may add more items as we go.
1888 * We loop until gray_start == gray_objects which means no more objects have
1889 * been added. Note this is iterative: no recursion is involved.
1890 * We need to walk the LO list as well in search of marked big objects
1891 * (use a flag since this is needed only on major collections). We need to loop
1892 * here as well, so keep a counter of marked LO (increasing it in copy_object).
1895 gray_start = to_space;
1896 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area: %p-%p, size: %d\n", gray_start, gray_objects, (int)(gray_objects - gray_start)));
1897 while (gray_start < gray_objects) {
1898 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", gray_start, safe_name (gray_start)));
1899 gray_start = scan_object (gray_start, start_addr, end_addr);
1902 DEBUG (2, fprintf (gc_debug_file, "Gray stack scan: %d usecs\n", TV_ELAPSED (btv, atv)));
1903 //scan_old_generation (start_addr, end_addr);
1904 DEBUG (2, fprintf (gc_debug_file, "Old generation done\n"));
1905 /* walk the finalization queue and move also the objects that need to be
1906 * finalized: use the finalized objects as new roots so the objects they depend
1907 * on are also not reclaimed. As with the roots above, only objects in the nursery
1908 * are marked/copied.
1909 * We need a loop here, since objects ready for finalizers may reference other objects
1910 * that are fin-ready. Speedup with a flag?
1913 fin_ready = num_ready_finalizers;
1914 finalize_in_range (start_addr, end_addr);
1915 bigo_scanned_num = scan_needed_big_objects (start_addr, end_addr);
1917 /* drain the new stack that might have been created */
1918 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin: %p-%p, size: %d\n", gray_start, gray_objects, (int)(gray_objects - gray_start)));
1919 while (gray_start < gray_objects) {
1920 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", gray_start, safe_name (gray_start)));
1921 gray_start = scan_object (gray_start, start_addr, end_addr);
1923 } while (fin_ready != num_ready_finalizers || bigo_scanned_num);
1925 DEBUG (2, fprintf (gc_debug_file, "Copied to old space: %d bytes\n", (int)(gray_objects - to_space)));
1926 to_space = gray_start;
1927 to_space_section->next_data = to_space;
1930 * handle disappearing links
1931 * Note we do this after checking the finalization queue because if an object
1932 * survives (at least long enough to be finalized) we don't clear the link.
1933 * This also deals with a possible issue with the monitor reclamation: with the Boehm
1934 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
1937 null_link_in_range (start_addr, end_addr);
1939 DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan: %d usecs\n", TV_ELAPSED (atv, btv)));
1942 static int last_num_pinned = 0;
1945 build_nursery_fragments (int start_pin, int end_pin)
1947 char *frag_start, *frag_end;
1951 /* FIXME: handle non-NULL fragment_freelist */
1952 fragment_freelist = nursery_fragments;
1953 nursery_fragments = NULL;
1954 frag_start = nursery_start;
1956 /* clear scan starts */
1957 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
1958 for (i = start_pin; i < end_pin; ++i) {
1959 frag_end = pin_queue [i];
1960 /* remove the pin bit from pinned objects */
1961 unpin_object (frag_end);
1962 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
1963 frag_size = frag_end - frag_start;
1965 add_nursery_frag (frag_size, frag_start, frag_end);
1966 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
1967 frag_size += ALLOC_ALIGN - 1;
1968 frag_size &= ~(ALLOC_ALIGN - 1);
1969 frag_start = (char*)pin_queue [i] + frag_size;
1971 nursery_last_pinned_end = frag_start;
1972 frag_end = nursery_real_end;
1973 frag_size = frag_end - frag_start;
1975 add_nursery_frag (frag_size, frag_start, frag_end);
1976 if (!nursery_fragments) {
1977 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
1978 for (i = start_pin; i < end_pin; ++i) {
1979 DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], safe_name (pin_queue [i]), safe_object_get_size (pin_queue [i])));
1985 /* FIXME: later reduce code duplication here with the above
1986 * We don't keep track of section fragments for non-nursery sections yet, so
1990 build_section_fragments (GCMemSection *section)
1993 char *frag_start, *frag_end;
1996 /* clear scan starts */
1997 memset (section->scan_starts, 0, section->num_scan_start * sizeof (gpointer));
1998 frag_start = section->data;
1999 section->next_data = section->data;
2000 for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
2001 frag_end = pin_queue [i];
2002 /* remove the pin bit from pinned objects */
2003 unpin_object (frag_end);
2004 if (frag_end >= section->data + section->size) {
2005 frag_end = section->data + section->size;
2007 section->scan_starts [((char*)frag_end - (char*)section->data)/SCAN_START_SIZE] = frag_end;
2009 frag_size = frag_end - frag_start;
2011 memset (frag_start, 0, frag_size);
2012 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
2013 frag_size += ALLOC_ALIGN - 1;
2014 frag_size &= ~(ALLOC_ALIGN - 1);
2015 frag_start = (char*)pin_queue [i] + frag_size;
2016 section->next_data = MAX (section->next_data, frag_start);
2018 frag_end = section->end_data;
2019 frag_size = frag_end - frag_start;
2021 memset (frag_start, 0, frag_size);
2025 scan_from_registered_roots (char *addr_start, char *addr_end)
2029 for (i = 0; i < roots_hash_size; ++i) {
2030 for (root = roots_hash [i]; root; root = root->next) {
2031 /* if desc is non-null it has precise info */
2032 if (!root->root_desc)
2034 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2035 precisely_scan_objects_from ((void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
2041 * Collect objects in the nursery.
2044 collect_nursery (size_t requested_size)
2046 GCMemSection *section;
2047 size_t max_garbage_amount;
2053 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
2054 /* FIXME: optimize later to use the higher address where an object can be present */
2055 nursery_next = MAX (nursery_next, nursery_real_end);
2057 DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", num_minor_gcs, nursery_start, nursery_next, (int)(nursery_next - nursery_start)));
2058 max_garbage_amount = nursery_next - nursery_start;
2060 * not enough room in the old generation to store all the possible data from
2061 * the nursery in a single continuous space.
2062 * We reset to_space if we allocated objects in degraded mode.
2064 if (to_space_section)
2065 to_space = gray_objects = to_space_section->next_data;
2066 if ((to_space_end - to_space) < max_garbage_amount) {
2067 section = alloc_section (nursery_section->size * 4);
2068 g_assert (nursery_section->size >= max_garbage_amount);
2069 to_space = gray_objects = section->next_data;
2070 to_space_end = section->end_data;
2071 to_space_section = section;
2073 DEBUG (2, fprintf (gc_debug_file, "To space setup: %p-%p in section %p\n", to_space, to_space_end, to_space_section));
2074 nursery_section->next_data = nursery_next;
2077 /* world must be stopped already */
2079 /* pin from pinned handles */
2080 pin_from_roots (nursery_start, nursery_next);
2081 /* identify pinned objects */
2082 optimize_pin_queue (0);
2083 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
2085 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2086 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2089 * walk all the roots and copy the young objects to the old generation,
2090 * starting from to_space
2093 scan_from_remsets (nursery_start, nursery_next);
2094 /* we don't have complete write barrier yet, so we scan all the old generation sections */
2096 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2097 /* FIXME: later scan also alloc_pinned objects */
2099 /* the pinned objects are roots */
2100 for (i = 0; i < next_pin_slot; ++i) {
2101 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
2102 scan_object (pin_queue [i], nursery_start, nursery_next);
2104 /* registered roots, this includes static fields */
2105 scan_from_registered_roots (nursery_start, nursery_next);
2107 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2109 drain_gray_stack (nursery_start, nursery_next);
2111 /* walk the pin_queue, build up the fragment list of free memory, unmark
2112 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2115 build_nursery_fragments (0, next_pin_slot);
2117 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %d bytes available\n", TV_ELAPSED (btv, atv), fragment_total));
2119 /* prepare the pin queue for the next collection */
2120 last_num_pinned = next_pin_slot;
2122 if (fin_ready_list) {
2123 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2124 mono_gc_finalize_notify ();
2129 major_collection (void)
2131 GCMemSection *section, *prev_section;
2132 LOSObject *bigobj, *prevbo;
2139 /* FIXME: only use these values for the precise scan
2140 * note that to_space pointers should be excluded anyway...
2142 char *heap_start = NULL;
2143 char *heap_end = (char*)-1;
2144 size_t copy_space_required = 0;
2147 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
2150 * FIXME: implement Mark/Compact
2151 * Until that is done, we can just apply mostly the same alg as for the nursery:
2152 * this means we need a big section to potentially copy all the other sections, so
2153 * it is not ideal specially with large heaps.
2155 if (g_getenv ("MONO_GC_NO_MAJOR")) {
2156 collect_nursery (0);
2159 /* FIXME: make sure the nursery next_data ptr is updated */
2160 nursery_section->next_data = nursery_real_end;
2161 /* we should also coalesce scanning from sections close to each other
2162 * and deal with pointers outside of the sections later.
2164 /* The remsets are not useful for a major collection */
2166 /* world must be stopped already */
2168 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
2169 for (section = section_list; section; section = section->next) {
2170 section->pin_queue_start = count = section->pin_queue_end = next_pin_slot;
2171 pin_from_roots (section->data, section->next_data);
2172 if (count != next_pin_slot) {
2174 optimize_pin_queue (count);
2175 DEBUG (6, fprintf (gc_debug_file, "Found %d pinning addresses in section %p (%d-%d)\n", next_pin_slot - count, section, count, next_pin_slot));
2176 reduced_to = pin_objects_from_addresses (section, pin_queue + count, pin_queue + next_pin_slot, section->data, section->next_data);
2177 section->pin_queue_end = next_pin_slot = count + reduced_to;
2179 copy_space_required += (char*)section->next_data - (char*)section->data;
2181 /* identify possible pointers to the insize of large objects */
2182 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
2183 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2184 count = next_pin_slot;
2185 pin_from_roots (bigobj->data, (char*)bigobj->data + bigobj->size);
2186 /* FIXME: this is only valid until we don't optimize the pin queue midway */
2187 if (next_pin_slot != count) {
2188 next_pin_slot = count;
2189 pin_object (bigobj->data);
2190 DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %d from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
2193 /* look for pinned addresses for pinned-alloc objects */
2194 DEBUG (6, fprintf (gc_debug_file, "Pinning from pinned-alloc objects\n"));
2195 for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2196 count = next_pin_slot;
2197 pin_from_roots (chunk->start_data, (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE);
2198 /* FIXME: this is only valid until we don't optimize the pin queue midway */
2199 if (next_pin_slot != count) {
2200 mark_pinned_from_addresses (chunk, pin_queue + count, pin_queue + next_pin_slot);
2201 next_pin_slot = count;
2206 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2207 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2209 /* allocate the big to space */
2210 DEBUG (4, fprintf (gc_debug_file, "Allocate tospace for size: %d\n", copy_space_required));
2211 section = alloc_section (copy_space_required);
2212 to_space = gray_objects = section->next_data;
2213 to_space_end = section->end_data;
2214 to_space_section = section;
2216 /* the old generation doesn't need to be scanned (no remembered sets or card
2217 * table needed either): the only objects that must survive are those pinned and
2218 * those referenced by the precise roots.
2219 * mark any section without pinned objects, so we can free it since we will be able to
2220 * move all the objects.
2222 /* the pinned objects are roots (big objects are included in this list, too) */
2223 for (i = 0; i < next_pin_slot; ++i) {
2224 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
2225 scan_object (pin_queue [i], heap_start, heap_end);
2227 /* registered roots, this includes static fields */
2228 scan_from_registered_roots (heap_start, heap_end);
2230 /* scan the list of objects ready for finalization */
2231 for (fin = fin_ready_list; fin; fin = fin->next) {
2232 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2233 fin->object = copy_object (fin->object, heap_start, heap_end);
2236 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2238 /* we need to go over the big object list to see if any was marked and scan it
2239 * And we need to make this in a loop, considering that objects referenced by finalizable
2240 * objects could reference big objects (this happens in drain_gray_stack ())
2242 scan_needed_big_objects (heap_start, heap_end);
2243 /* all the objects in the heap */
2244 drain_gray_stack (heap_start, heap_end);
2246 /* sweep the big objects list */
2248 for (bigobj = los_object_list; bigobj;) {
2249 if (object_is_pinned (bigobj->data)) {
2250 unpin_object (bigobj->data);
2251 bigobj->scanned = FALSE;
2254 /* not referenced anywhere, so we can free it */
2256 prevbo->next = bigobj->next;
2258 los_object_list = bigobj->next;
2260 bigobj = bigobj->next;
2261 free_large_object (to_free);
2265 bigobj = bigobj->next;
2267 /* unpin objects from the pinned chunks and free the unmarked ones */
2268 sweep_pinned_objects ();
2270 /* free the unused sections */
2271 prev_section = NULL;
2272 for (section = section_list; section;) {
2273 /* to_space doesn't need handling here and the nursery is special */
2274 if (section == to_space_section || section == nursery_section) {
2275 prev_section = section;
2276 section = section->next;
2279 /* no pinning object, so the section is free */
2280 if (section->pin_queue_start == section->pin_queue_end) {
2281 GCMemSection *to_free;
2283 prev_section->next = section->next;
2285 section_list = section->next;
2287 section = section->next;
2288 free_mem_section (to_free);
2291 DEBUG (6, fprintf (gc_debug_file, "Section %p has still pinned objects (%d)\n", section, section->pin_queue_end - section->pin_queue_start));
2292 build_section_fragments (section);
2294 prev_section = section;
2295 section = section->next;
2298 /* walk the pin_queue, build up the fragment list of free memory, unmark
2299 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2302 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
2304 /* prepare the pin queue for the next collection */
2306 if (fin_ready_list) {
2307 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2308 mono_gc_finalize_notify ();
2313 * Allocate a new section of memory to be used as old generation.
2315 static GCMemSection*
2316 alloc_section (size_t size)
2318 GCMemSection *section;
2321 size_t new_size = next_section_size;
2323 if (size > next_section_size) {
2325 new_size += pagesize - 1;
2326 new_size &= ~(pagesize - 1);
2328 section_size_used++;
2329 if (section_size_used > 3) {
2330 section_size_used = 0;
2331 next_section_size *= 2;
2332 if (next_section_size > max_section_size)
2333 next_section_size = max_section_size;
2335 section = get_internal_mem (sizeof (GCMemSection));
2336 data = get_os_memory (new_size, TRUE);
2337 section->data = section->next_data = data;
2338 section->size = new_size;
2339 section->end_data = data + new_size;
2340 UPDATE_HEAP_BOUNDARIES (data, section->end_data);
2341 total_alloc += new_size;
2342 DEBUG (2, fprintf (gc_debug_file, "Expanding heap size: %d, total: %d\n", new_size, total_alloc));
2343 section->data = data;
2344 section->size = new_size;
2345 scan_starts = new_size / SCAN_START_SIZE;
2346 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts);
2347 section->num_scan_start = scan_starts;
2349 /* add to the section list */
2350 section->next = section_list;
2351 section_list = section;
2357 free_mem_section (GCMemSection *section)
2359 char *data = section->data;
2360 size_t size = section->size;
2361 DEBUG (2, fprintf (gc_debug_file, "Freed section %p, size %d\n", data, size));
2362 free_os_memory (data, size);
2363 free_internal_mem (section);
2364 total_alloc -= size;
2368 * When deciding if it's better to collect or to expand, keep track
2369 * of how much garbage was reclaimed with the last collection: if it's too
2371 * This is called when we could not allocate a small object.
2373 static void __attribute__((noinline))
2374 minor_collect_or_expand_inner (size_t size)
2376 int do_minor_collection = 1;
2378 if (!nursery_section) {
2382 if (do_minor_collection) {
2384 collect_nursery (size);
2385 DEBUG (2, fprintf (gc_debug_file, "Heap size: %d, LOS size: %d\n", total_alloc, los_memory_usage));
2387 /* this also sets the proper pointers for the next allocation */
2388 if (!search_fragment_for_size (size)) {
2390 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2391 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %d alloc (%d pinned)", size, last_num_pinned));
2392 for (i = 0; i < last_num_pinned; ++i) {
2393 DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], safe_name (pin_queue [i]), safe_object_get_size (pin_queue [i])));
2398 //report_internal_mem_usage ();
2402 * ######################################################################
2403 * ######## Memory allocation from the OS
2404 * ######################################################################
2405 * This section of code deals with getting memory from the OS and
2406 * allocating memory for GC-internal data structures.
2407 * Internal memory can be handled with a freelist for small objects.
2411 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
2412 * This must not require any lock.
2415 get_os_memory (size_t size, int activate)
2418 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
2420 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
2421 size += pagesize - 1;
2422 size &= ~(pagesize - 1);
2423 ptr = mono_valloc (0, size, prot_flags);
2428 * Free the memory returned by get_os_memory (), returning it to the OS.
2431 free_os_memory (void *addr, size_t size)
2433 munmap (addr, size);
2440 report_pinned_chunk (PinnedChunk *chunk, int seq) {
2442 int i, free_pages, num_free, free_mem;
2444 for (i = 0; i < chunk->num_pages; ++i) {
2445 if (!chunk->page_sizes [i])
2448 printf ("Pinned chunk %d at %p, size: %d, pages: %d, free: %d\n", seq, chunk, chunk->num_pages * FREELIST_PAGESIZE, chunk->num_pages, free_pages);
2449 free_mem = FREELIST_PAGESIZE * free_pages;
2450 for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
2451 if (!chunk->free_list [i])
2454 p = chunk->free_list [i];
2459 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
2460 free_mem += freelist_sizes [i] * num_free;
2462 printf ("\tfree memory in chunk: %d\n", free_mem);
2469 report_internal_mem_usage (void) {
2472 printf ("Internal memory usage:\n");
2474 for (chunk = internal_chunk_list; chunk; chunk = chunk->next) {
2475 report_pinned_chunk (chunk, i++);
2477 printf ("Pinned memory usage:\n");
2479 for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2480 report_pinned_chunk (chunk, i++);
2485 * the array of pointers from @start to @end contains conservative
2486 * pointers to objects inside @chunk: mark each referenced object
2490 mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end)
2492 for (; start < end; start++) {
2493 char *addr = *start;
2494 int offset = (char*)addr - (char*)chunk;
2495 int page = offset / FREELIST_PAGESIZE;
2496 int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
2497 int slot_size = chunk->page_sizes [page];
2499 /* the page is not allocated */
2502 /* would be faster if we restrict the sizes to power of two,
2503 * but that's a waste of memory: need to measure. it could reduce
2504 * fragmentation since there are less pages needed, if for example
2505 * someone interns strings of each size we end up with one page per
2506 * interned string (still this is just ~40 KB): with more fine-grained sizes
2507 * this increases the number of used pages.
2510 obj_offset /= slot_size;
2511 obj_offset *= slot_size;
2512 addr = (char*)chunk->start_data + obj_offset;
2514 obj_offset /= slot_size;
2515 obj_offset *= slot_size;
2516 addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
2519 /* if the vtable is inside the chunk it's on the freelist, so skip */
2520 if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))) {
2522 DEBUG (6, fprintf (gc_debug_file, "Marked pinned object %p (%s) from roots\n", addr, safe_name (addr)));
2528 sweep_pinned_objects (void)
2535 for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2536 end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
2537 DEBUG (6, fprintf (gc_debug_file, "Sweeping pinned chunk %p (ranhe: %p-%p)\n", chunk, chunk->start_data, end_chunk));
2538 for (i = 0; i < chunk->num_pages; ++i) {
2539 obj_size = chunk->page_sizes [i];
2542 p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
2543 endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
2544 DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
2545 while (p + obj_size <= endp) {
2547 DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
2548 /* if the first word (the vtable) is outside the chunk we have an object */
2549 if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk)) {
2550 if (object_is_pinned (ptr)) {
2552 DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
2554 /* FIXME: add to freelist */
2555 DEBUG (6, fprintf (gc_debug_file, "Going to free unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
2565 * Find the slot number in the freelist for memory chunks that
2566 * can contain @size objects.
2569 slot_for_size (size_t size)
2572 /* do a binary search or lookup table later. */
2573 for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
2574 if (freelist_sizes [slot] >= size)
2577 g_assert_not_reached ();
2582 * Build a free list for @size memory chunks from the memory area between
2583 * start_page and end_page.
2586 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
2590 /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
2591 p = (void**)start_page;
2592 end = (void**)(end_page - size);
2593 g_assert (!chunk->free_list [slot]);
2594 chunk->free_list [slot] = p;
2595 while ((char*)p + size <= (char*)end) {
2597 *p = (void*)((char*)p + size);
2601 /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
2605 alloc_pinned_chunk (size_t size)
2610 size += pagesize; /* at least one page */
2611 size += pagesize - 1;
2612 size &= ~(pagesize - 1);
2613 if (size < PINNED_CHUNK_MIN_SIZE * 2)
2614 size = PINNED_CHUNK_MIN_SIZE * 2;
2615 chunk = get_os_memory (size, TRUE);
2616 UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
2617 total_alloc += size;
2619 /* setup the bookeeping fields */
2620 chunk->num_pages = size / FREELIST_PAGESIZE;
2621 offset = G_STRUCT_OFFSET (PinnedChunk, data);
2622 chunk->page_sizes = (void*)((char*)chunk + offset);
2623 offset += sizeof (int) * chunk->num_pages;
2624 offset += ALLOC_ALIGN - 1;
2625 offset &= ~(ALLOC_ALIGN - 1);
2626 chunk->free_list = (void*)((char*)chunk + offset);
2627 offset += sizeof (void*) * FREELIST_NUM_SLOTS;
2628 offset += ALLOC_ALIGN - 1;
2629 offset &= ~(ALLOC_ALIGN - 1);
2630 chunk->start_data = (void*)((char*)chunk + offset);
2632 /* allocate the first page to the freelist */
2633 chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
2634 build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
2635 DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
2636 min_pinned_chunk_addr = MIN (min_pinned_chunk_addr, (char*)chunk->start_data);
2637 max_pinned_chunk_addr = MAX (max_pinned_chunk_addr, ((char*)chunk + size));
2641 /* assumes freelist for slot is empty, so try to alloc a new page */
2643 get_chunk_freelist (PinnedChunk *chunk, int slot)
2647 p = chunk->free_list [slot];
2649 chunk->free_list [slot] = *p;
2652 for (i = 0; i < chunk->num_pages; ++i) {
2654 if (chunk->page_sizes [i])
2656 size = freelist_sizes [slot];
2657 chunk->page_sizes [i] = size;
2658 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
2662 p = chunk->free_list [slot];
2664 chunk->free_list [slot] = *p;
2671 alloc_from_freelist (size_t size)
2675 PinnedChunk *pchunk;
2676 slot = slot_for_size (size);
2677 /*g_print ("using slot %d for size %d (slot size: %d)\n", slot, size, freelist_sizes [slot]);*/
2678 g_assert (size <= freelist_sizes [slot]);
2679 for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->next) {
2680 void **p = pchunk->free_list [slot];
2682 /*g_print ("found freelist for slot %d in chunk %p, returning %p, next %p\n", slot, pchunk, p, *p);*/
2683 pchunk->free_list [slot] = *p;
2687 for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->next) {
2688 res = get_chunk_freelist (pchunk, slot);
2692 pchunk = alloc_pinned_chunk (size);
2693 /* FIXME: handle OOM */
2694 pchunk->next = pinned_chunk_list;
2695 pinned_chunk_list = pchunk;
2696 res = get_chunk_freelist (pchunk, slot);
2700 /* used for the GC-internal data structures */
2701 /* FIXME: add support for bigger sizes by allocating more than one page
2705 get_internal_mem (size_t size)
2707 return calloc (1, size);
2710 PinnedChunk *pchunk;
2711 slot = slot_for_size (size);
2712 g_assert (size <= freelist_sizes [slot]);
2713 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
2714 void **p = pchunk->free_list [slot];
2716 pchunk->free_list [slot] = *p;
2720 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
2721 res = get_chunk_freelist (pchunk, slot);
2725 pchunk = alloc_pinned_chunk (size);
2726 /* FIXME: handle OOM */
2727 pchunk->next = internal_chunk_list;
2728 internal_chunk_list = pchunk;
2729 res = get_chunk_freelist (pchunk, slot);
2734 free_internal_mem (void *addr)
2738 PinnedChunk *pchunk;
2739 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
2740 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
2741 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
2742 int offset = (char*)addr - (char*)pchunk;
2743 int page = offset / FREELIST_PAGESIZE;
2744 int slot = slot_for_size (pchunk->page_sizes [page]);
2746 *p = pchunk->free_list [slot];
2747 pchunk->free_list [slot] = p;
2751 printf ("free of %p failed\n", addr);
2752 g_assert_not_reached ();
2756 * ######################################################################
2757 * ######## Object allocation
2758 * ######################################################################
2759 * This section of code deals with allocating memory for objects.
2760 * There are several ways:
2761 * *) allocate large objects
2762 * *) allocate normal objects
2763 * *) fast lock-free allocation
2764 * *) allocation of pinned objects
2768 free_large_object (LOSObject *obj)
2770 size_t size = obj->size;
2771 DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %d\n", obj->data, obj->size));
2773 los_memory_usage -= size;
2774 size += sizeof (LOSObject);
2775 size += pagesize - 1;
2776 size &= ~(pagesize - 1);
2777 total_alloc -= size;
2779 free_os_memory (obj, size);
2783 * Objects with size >= 64KB are allocated in the large object space.
2784 * They are currently kept track of with a linked list.
2785 * They don't move, so there is no need to pin them during collection
2786 * and we avoid the memcpy overhead.
2788 static void* __attribute__((noinline))
2789 alloc_large_inner (MonoVTable *vtable, size_t size)
2794 int just_did_major_gc = FALSE;
2796 if (los_memory_usage > next_los_collection) {
2797 DEBUG (4, fprintf (gc_debug_file, "Should trigger major collection: req size %d (los already: %u, limit: %u)\n", size, los_memory_usage, next_los_collection));
2798 just_did_major_gc = TRUE;
2800 major_collection ();
2802 /* later increase based on a percent of the heap size */
2803 next_los_collection = los_memory_usage + 5*1024*1024;
2806 alloc_size += sizeof (LOSObject);
2807 alloc_size += pagesize - 1;
2808 alloc_size &= ~(pagesize - 1);
2809 /* FIXME: handle OOM */
2810 obj = get_os_memory (alloc_size, TRUE);
2812 vtslot = (void**)obj->data;
2814 total_alloc += alloc_size;
2815 UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
2816 obj->next = los_object_list;
2817 los_object_list = obj;
2818 los_memory_usage += size;
2820 DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %d\n", obj->data, vtable, vtable->klass->name, size));
2824 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
2825 * an object of size @size
2826 * Return FALSE if not found (which means we need a collection)
2829 search_fragment_for_size (size_t size)
2831 Fragment *frag, *prev;
2832 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %d\n", nursery_frag_real_end, size));
2834 for (frag = nursery_fragments; frag; frag = frag->next) {
2835 if (size <= (frag->fragment_end - frag->fragment_start)) {
2836 /* remove from the list */
2838 prev->next = frag->next;
2840 nursery_fragments = frag->next;
2841 nursery_next = frag->fragment_start;
2842 nursery_frag_real_end = frag->fragment_end;
2843 nursery_temp_end = MIN (nursery_frag_real_end, nursery_next + size + SCAN_START_SIZE);
2845 DEBUG (4, fprintf (gc_debug_file, "Using nursery fragment %p-%p, size: %d (req: %d)\n", nursery_next, nursery_frag_real_end, nursery_frag_real_end - nursery_next, size));
2846 frag->next = fragment_freelist;
2847 fragment_freelist = frag;
2856 * size is already rounded up and we hold the GC lock.
2859 alloc_degraded (MonoVTable *vtable, size_t size)
2861 GCMemSection *section;
2863 for (section = section_list; section; section = section->next) {
2864 if (section != nursery_section && (section->end_data - section->next_data) >= size) {
2865 p = (void**)section->next_data;
2870 section = alloc_section (nursery_section->size * 4);
2871 /* FIXME: handle OOM */
2872 p = (void**)section->next_data;
2874 section->next_data += size;
2875 degraded_mode += size;
2876 DEBUG (3, fprintf (gc_debug_file, "Allocated (degraded) object %p, vtable: %p (%s), size: %d in section %p\n", p, vtable, vtable->klass->name, size, section));
2882 * Provide a variant that takes just the vtable for small fixed-size objects.
2883 * The aligned size is already computed and stored in vt->gc_descr.
2884 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
2885 * processing. We can keep track of where objects start, for example,
2886 * so when we scan the thread stacks for pinned objects, we can start
2887 * a search for the pinned object in SCAN_START_SIZE chunks.
2890 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
2892 /* FIXME: handle OOM */
2895 size += ALLOC_ALIGN - 1;
2896 size &= ~(ALLOC_ALIGN - 1);
2898 g_assert (vtable->gc_descr);
2901 p = (void**)nursery_next;
2902 /* FIXME: handle overflow */
2903 nursery_next += size;
2904 if (nursery_next >= nursery_temp_end) {
2905 /* there are two cases: the object is too big or we need to collect */
2906 /* there can be another case (from ORP), if we cooperate with the runtime a bit:
2907 * objects that need finalizers can have the high bit set in their size
2908 * so the above check fails and we can readily add the object to the queue.
2909 * This avoids taking again the GC lock when registering, but this is moot when
2910 * doing thread-local allocation, so it may not be a good idea.
2912 if (size > MAX_SMALL_OBJ_SIZE) {
2913 /* get ready for possible collection */
2914 update_current_thread_stack (&dummy);
2915 nursery_next -= size;
2916 p = alloc_large_inner (vtable, size);
2918 if (nursery_next >= nursery_frag_real_end) {
2919 nursery_next -= size;
2920 /* when running in degraded mode, we continue allocing that way
2921 * for a while, to decrease the number of useless nursery collections.
2923 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
2924 p = alloc_degraded (vtable, size);
2928 if (!search_fragment_for_size (size)) {
2929 /* get ready for possible collection */
2930 update_current_thread_stack (&dummy);
2931 minor_collect_or_expand_inner (size);
2932 if (degraded_mode) {
2933 p = alloc_degraded (vtable, size);
2938 /* nursery_next changed by minor_collect_or_expand_inner () */
2939 p = (void*)nursery_next;
2940 nursery_next += size;
2941 if (nursery_next > nursery_temp_end) {
2946 /* record the scan start so we can find pinned objects more easily */
2947 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
2948 /* we just bump nursery_temp_end as well */
2949 nursery_temp_end = MIN (nursery_frag_real_end, nursery_next + SCAN_START_SIZE);
2950 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", nursery_next, nursery_temp_end));
2954 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %d\n", p, vtable, vtable->klass->name, size));
2963 * To be used for interned strings and possibly MonoThread, reflection handles.
2964 * We may want to explicitly free these objects.
2967 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
2969 /* FIXME: handle OOM */
2971 size += ALLOC_ALIGN - 1;
2972 size &= ~(ALLOC_ALIGN - 1);
2974 if (size > MAX_FREELIST_SIZE) {
2975 update_current_thread_stack (&p);
2976 /* large objects are always pinned anyway */
2977 p = alloc_large_inner (vtable, size);
2979 p = alloc_from_freelist (size);
2980 memset (p, 0, size);
2982 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %d\n", p, vtable, vtable->klass->name, size));
2989 * ######################################################################
2990 * ######## Finalization support
2991 * ######################################################################
2995 * this is valid for the nursery: if the object has been forwarded it means it's
2996 * still refrenced from a root. If it is pinned it's still alive as well.
2997 * Return TRUE if @obj is ready to be finalized.
2999 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3002 finalize_in_range (char *start, char *end)
3004 FinalizeEntry *entry, *prev;
3008 for (i = 0; i < finalizable_hash_size; ++i) {
3010 for (entry = finalizable_hash [i]; entry;) {
3011 if (entry->object >= start && entry->object < end && (entry->object < to_space || entry->object >= to_space_end)) {
3012 if (object_is_fin_ready (entry->object)) {
3014 FinalizeEntry *next;
3015 /* remove and put in fin_ready_list */
3017 prev->next = entry->next;
3019 finalizable_hash [i] = entry->next;
3021 num_ready_finalizers++;
3022 num_registered_finalizers--;
3023 entry->next = fin_ready_list;
3024 fin_ready_list = entry;
3025 /* Make it survive */
3026 from = entry->object;
3027 entry->object = copy_object (entry->object, start, end);
3028 DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", entry->object, safe_name (entry->object), from, num_ready_finalizers, num_registered_finalizers));
3032 /* update pointer */
3033 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s)\n", entry->object, safe_name (entry->object)));
3034 entry->object = copy_object (entry->object, start, end);
3038 entry = entry->next;
3044 null_link_in_range (char *start, char *end)
3046 FinalizeEntry *entry, *prev;
3048 for (i = 0; i < disappearing_link_hash_size; ++i) {
3050 for (entry = disappearing_link_hash [i]; entry;) {
3051 if (entry->object >= start && entry->object < end && (entry->object < to_space || entry->object >= to_space_end)) {
3052 if (object_is_fin_ready (entry->object)) {
3053 void **p = entry->data;
3056 /* remove from list */
3058 prev->next = entry->next;
3060 disappearing_link_hash [i] = entry->next;
3061 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, entry->object));
3063 free_internal_mem (entry);
3065 num_disappearing_links--;
3069 /* update pointer if it's moved
3070 * FIXME: what if an object is moved earlier?
3072 entry->object = copy_object (entry->object, start, end);
3073 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->data, entry->object));
3075 *link = entry->object;
3079 entry = entry->next;
3085 * mono_gc_finalizers_for_domain:
3086 * @domain: the unloading appdomain
3087 * @out_array: output array
3088 * @out_size: size of output array
3090 * Store inside @out_array up to @out_size objects that belong to the unloading
3091 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
3092 * until it returns 0.
3093 * The items are removed from the finalizer data structure, so the caller is supposed
3095 * @out_array should be on the stack to allow the GC to know the objects are still alive.
3098 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
3100 FinalizeEntry *entry, *prev;
3102 if (no_finalize || !out_size || !out_array)
3106 for (i = 0; i < finalizable_hash_size; ++i) {
3108 for (entry = finalizable_hash [i]; entry;) {
3109 if (mono_object_domain (entry->object) == domain) {
3110 FinalizeEntry *next;
3111 /* remove and put in out_array */
3113 prev->next = entry->next;
3115 finalizable_hash [i] = entry->next;
3117 num_registered_finalizers--;
3118 out_array [count ++] = entry->object;
3119 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, num_registered_finalizers));
3121 if (count == out_size) {
3128 entry = entry->next;
3136 rehash_fin_table (void)
3140 FinalizeEntry **new_hash;
3141 FinalizeEntry *entry, *next;
3142 int new_size = g_spaced_primes_closest (num_registered_finalizers);
3144 new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
3145 for (i = 0; i < finalizable_hash_size; ++i) {
3146 for (entry = finalizable_hash [i]; entry; entry = next) {
3147 hash = mono_object_hash (entry->object) % new_size;
3149 entry->next = new_hash [hash];
3150 new_hash [hash] = entry;
3153 free_internal_mem (finalizable_hash);
3154 finalizable_hash = new_hash;
3155 finalizable_hash_size = new_size;
3159 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
3161 FinalizeEntry *entry, *prev;
3165 hash = mono_object_hash (obj);
3167 if (num_registered_finalizers >= finalizable_hash_size * 2)
3168 rehash_fin_table ();
3169 hash %= finalizable_hash_size;
3171 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
3172 if (entry->object == obj) {
3174 entry->data = user_data;
3176 /* remove from the list */
3178 prev->next = entry->next;
3180 finalizable_hash [hash] = entry->next;
3181 num_registered_finalizers--;
3182 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
3183 free_internal_mem (entry);
3191 /* request to deregister, but already out of the list */
3195 entry = get_internal_mem (sizeof (FinalizeEntry));
3196 entry->object = obj;
3197 entry->data = user_data;
3198 entry->next = finalizable_hash [hash];
3199 finalizable_hash [hash] = entry;
3200 num_registered_finalizers++;
3201 DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
3206 rehash_dislink (void)
3210 FinalizeEntry **new_hash;
3211 FinalizeEntry *entry, *next;
3212 int new_size = g_spaced_primes_closest (num_disappearing_links);
3214 new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
3215 for (i = 0; i < disappearing_link_hash_size; ++i) {
3216 for (entry = disappearing_link_hash [i]; entry; entry = next) {
3217 hash = mono_aligned_addr_hash (entry->data) % new_size;
3219 entry->next = new_hash [hash];
3220 new_hash [hash] = entry;
3223 free_internal_mem (disappearing_link_hash);
3224 disappearing_link_hash = new_hash;
3225 disappearing_link_hash_size = new_size;
3229 mono_gc_register_disappearing_link (MonoObject *obj, void *link)
3231 FinalizeEntry *entry, *prev;
3235 if (num_disappearing_links >= disappearing_link_hash_size * 2)
3237 /* FIXME: add check that link is not in the heap */
3238 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
3239 entry = disappearing_link_hash [hash];
3241 for (; entry; entry = entry->next) {
3242 /* link already added */
3243 if (link == entry->data) {
3244 /* NULL obj means remove */
3247 prev->next = entry->next;
3249 disappearing_link_hash [hash] = entry->next;
3250 num_disappearing_links--;
3251 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d)\n", entry, num_disappearing_links));
3252 free_internal_mem (entry);
3254 entry->object = obj; /* we allow the change of object */
3261 entry = get_internal_mem (sizeof (FinalizeEntry));
3262 entry->object = obj;
3264 entry->next = disappearing_link_hash [hash];
3265 disappearing_link_hash [hash] = entry;
3266 num_disappearing_links++;
3267 DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p\n", entry, obj, obj->vtable->klass->name, link));
3272 mono_gc_invoke_finalizers (void)
3274 FinalizeEntry *entry;
3277 /* FIXME: batch to reduce lock contention */
3278 while (fin_ready_list) {
3280 entry = fin_ready_list;
3282 fin_ready_list = entry->next;
3283 num_ready_finalizers--;
3284 obj = entry->object;
3285 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
3289 void (*callback)(void *, void*) = entry->data;
3291 obj = entry->object;
3293 /* the object is on the stack so it is pinned */
3294 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3295 callback (obj, NULL);
3296 free_internal_mem (entry);
3303 mono_gc_pending_finalizers (void)
3305 return fin_ready_list != NULL;
3308 /* Negative value to remove */
3310 mono_gc_add_memory_pressure (gint64 value)
3312 /* FIXME: Use interlocked functions */
3314 memory_pressure += value;
3319 * ######################################################################
3320 * ######## registered roots support
3321 * ######################################################################
3329 RootRecord **new_hash;
3330 RootRecord *entry, *next;
3331 int new_size = g_spaced_primes_closest (num_roots_entries);
3333 new_hash = get_internal_mem (new_size * sizeof (RootRecord*));
3334 for (i = 0; i < roots_hash_size; ++i) {
3335 for (entry = roots_hash [i]; entry; entry = next) {
3336 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
3338 entry->next = new_hash [hash];
3339 new_hash [hash] = entry;
3342 free_internal_mem (roots_hash);
3343 roots_hash = new_hash;
3344 roots_hash_size = new_size;
3348 * We do not coalesce roots.
3351 mono_gc_register_root (char *start, size_t size, void *descr)
3353 RootRecord *new_root;
3354 unsigned int hash = mono_aligned_addr_hash (start);
3356 if (num_roots_entries >= roots_hash_size * 2)
3358 hash %= roots_hash_size;
3359 for (new_root = roots_hash [hash]; new_root; new_root = new_root->next) {
3360 /* we allow changing the size and the descriptor (for thread statics etc) */
3361 if (new_root->start_root == start) {
3362 size_t old_size = new_root->end_root - new_root->start_root;
3363 new_root->end_root = new_root->start_root + size;
3364 new_root->root_desc = (mword)descr;
3366 roots_size -= old_size;
3371 new_root = get_internal_mem (sizeof (RootRecord));
3373 new_root->start_root = start;
3374 new_root->end_root = new_root->start_root + size;
3375 new_root->root_desc = (mword)descr;
3377 num_roots_entries++;
3378 new_root->next = roots_hash [hash];
3379 roots_hash [hash] = new_root;
3380 DEBUG (3, fprintf (gc_debug_file, "Added root %p for range: %p-%p, descr: %p (%d/%d bytes)\n", new_root, new_root->start_root, new_root->end_root, descr, (int)size, (int)roots_size));
3390 mono_gc_deregister_root (char* addr)
3392 RootRecord *tmp, *prev = NULL;
3393 unsigned int hash = mono_aligned_addr_hash (addr);
3395 hash %= roots_hash_size;
3396 tmp = roots_hash [hash];
3398 if (tmp->start_root == (char*)addr) {
3400 prev->next = tmp->next;
3402 roots_hash [hash] = tmp->next;
3403 roots_size -= (tmp->end_root - tmp->start_root);
3404 num_roots_entries--;
3405 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
3406 free_internal_mem (tmp);
3416 * ######################################################################
3417 * ######## Thread handling (stop/start code)
3418 * ######################################################################
3421 /* eventually share with MonoThread? */
3422 typedef struct _SgenThreadInfo SgenThreadInfo;
3424 struct _SgenThreadInfo {
3425 SgenThreadInfo *next;
3426 ARCH_THREAD_TYPE id;
3427 unsigned int stop_count; /* to catch duplicate signals */
3432 RememberedSet *remset;
3435 /* FIXME: handle large/small config */
3436 #define THREAD_HASH_SIZE 11
3437 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
3439 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
3441 #if USE_SIGNAL_BASED_START_STOP_WORLD
3443 static sem_t suspend_ack_semaphore;
3444 static unsigned int global_stop_count = 0;
3445 static int suspend_signal_num = SIGPWR;
3446 static int restart_signal_num = SIGXCPU;
3447 static sigset_t suspend_signal_mask;
3448 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
3450 /* LOCKING: assumes the GC lock is held */
3451 static SgenThreadInfo*
3452 thread_info_lookup (ARCH_THREAD_TYPE id)
3454 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
3455 SgenThreadInfo *info;
3457 info = thread_table [hash];
3458 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
3465 update_current_thread_stack (void *start)
3467 void *ptr = cur_thread_regs;
3468 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
3469 info->stack_start = align_pointer (&ptr);
3470 ARCH_STORE_REGS (ptr);
3474 signal_desc (int signum)
3476 if (signum == suspend_signal_num)
3478 if (signum == restart_signal_num)
3483 /* LOCKING: assumes the GC lock is held */
3485 thread_handshake (int signum)
3487 int count, i, result;
3488 SgenThreadInfo *info;
3489 pthread_t me = pthread_self ();
3492 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3493 for (info = thread_table [i]; info; info = info->next) {
3494 DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
3495 if (ARCH_THREAD_EQUALS (info->id, me)) {
3496 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
3499 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
3501 result = pthread_kill (info->id, signum);
3503 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
3506 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
3512 for (i = 0; i < count; ++i) {
3513 while ((result = sem_wait (&suspend_ack_semaphore)) != 0) {
3514 if (errno != EINTR) {
3515 g_error ("sem_wait ()");
3522 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
3524 suspend_handler (int sig)
3526 SgenThreadInfo *info;
3529 int old_errno = errno;
3531 id = pthread_self ();
3532 info = thread_info_lookup (id);
3533 stop_count = global_stop_count;
3534 /* duplicate signal */
3535 if (0 && info->stop_count == stop_count) {
3539 /* update the remset info in the thread data structure */
3540 info->remset = remembered_set;
3542 * this includes the register values that the kernel put on the stack.
3543 * Write arch-specific code to only push integer regs and a more accurate
3546 info->stack_start = align_pointer (&id);
3548 /* notify the waiting thread */
3549 sem_post (&suspend_ack_semaphore);
3550 info->stop_count = stop_count;
3552 /* wait until we receive the restart signal */
3555 sigsuspend (&suspend_signal_mask);
3556 } while (info->signal != restart_signal_num);
3558 /* notify the waiting thread */
3559 sem_post (&suspend_ack_semaphore);
3565 restart_handler (int sig)
3567 SgenThreadInfo *info;
3568 int old_errno = errno;
3570 info = thread_info_lookup (pthread_self ());
3571 info->signal = restart_signal_num;
3576 static TV_DECLARE (stop_world_time);
3577 static unsigned long max_pause_usec = 0;
3579 /* LOCKING: assumes the GC lock is held */
3585 global_stop_count++;
3586 DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, thread_info_lookup (ARCH_GET_THREAD ()), (gpointer)ARCH_GET_THREAD ()));
3587 TV_GETTIME (stop_world_time);
3588 count = thread_handshake (suspend_signal_num);
3589 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
3593 /* LOCKING: assumes the GC lock is held */
3595 restart_world (void)
3598 TV_DECLARE (end_sw);
3601 count = thread_handshake (restart_signal_num);
3602 TV_GETTIME (end_sw);
3603 usec = TV_ELAPSED (stop_world_time, end_sw);
3604 max_pause_usec = MAX (usec, max_pause_usec);
3605 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
3609 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
3612 * Identify objects pinned in a thread stack and its registers.
3615 pin_thread_data (void *start_nursery, void *end_nursery)
3618 SgenThreadInfo *info;
3620 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3621 for (info = thread_table [i]; info; info = info->next) {
3623 DEBUG (2, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
3626 DEBUG (2, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
3627 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery);
3630 DEBUG (2, fprintf (gc_debug_file, "Scanning current thread registers\n"));
3631 conservatively_pin_objects_from ((void*)cur_thread_regs, (void*)(cur_thread_regs + ARCH_NUM_REGS), start_nursery, end_nursery);
3635 find_pinning_ref_from_thread (char *obj, size_t size)
3638 SgenThreadInfo *info;
3639 char *endobj = obj + size;
3641 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3642 for (info = thread_table [i]; info; info = info->next) {
3643 char **start = (char**)info->stack_start;
3646 while (start < (char**)info->stack_end) {
3647 if (*start >= obj && *start < endobj) {
3648 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, info->id, start, info->stack_start, info->stack_end));
3654 /* FIXME: check register */
3657 /* return TRUE if ptr points inside the managed heap */
3659 ptr_in_heap (void* ptr)
3661 mword p = (mword)ptr;
3662 if (p < lowest_heap_address || p >= highest_heap_address)
3664 /* FIXME: more checks */
3669 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
3674 /* FIXME: exclude stack locations */
3675 switch ((*p) & REMSET_TYPE_MASK) {
3676 case REMSET_LOCATION:
3678 if ((ptr < start_nursery || ptr >= end_nursery) && ptr_in_heap (ptr)) {
3679 *ptr = copy_object (*ptr, start_nursery, end_nursery);
3680 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
3681 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
3682 add_to_global_remset (ptr);
3684 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
3688 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
3689 if ((ptr >= start_nursery && ptr < end_nursery) || !ptr_in_heap (ptr))
3692 while (count-- > 0) {
3693 *ptr = copy_object (*ptr, start_nursery, end_nursery);
3694 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
3695 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
3696 add_to_global_remset (ptr);
3701 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
3702 if ((ptr >= start_nursery && ptr < end_nursery) || !ptr_in_heap (ptr))
3704 scan_object (*ptr, start_nursery, end_nursery);
3707 g_assert_not_reached ();
3713 scan_from_remsets (void *start_nursery, void *end_nursery)
3716 SgenThreadInfo *info;
3717 RememberedSet *remset, *next;
3720 /* the global one */
3721 for (remset = global_remset; remset; remset = remset->next) {
3722 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %d\n", remset->data, remset->store_next, remset->store_next - remset->data));
3723 for (p = remset->data; p < remset->store_next;) {
3724 p = handle_remset (p, start_nursery, end_nursery, TRUE);
3727 /* the per-thread ones */
3728 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3729 for (info = thread_table [i]; info; info = info->next) {
3730 for (remset = info->remset; remset; remset = next) {
3731 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %d\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
3732 for (p = remset->data; p < remset->store_next;) {
3733 p = handle_remset (p, start_nursery, end_nursery, FALSE);
3735 remset->store_next = remset->data;
3736 next = remset->next;
3737 remset->next = NULL;
3738 if (remset != info->remset) {
3739 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
3740 free_internal_mem (remset);
3748 * Clear the info in the remembered sets: we're doing a major collection, so
3749 * the per-thread ones are not needed and the global ones will be reconstructed
3753 clear_remsets (void)
3756 SgenThreadInfo *info;
3757 RememberedSet *remset, *next;
3759 /* the global list */
3760 for (remset = global_remset; remset; remset = next) {
3761 remset->store_next = remset->data;
3762 next = remset->next;
3763 remset->next = NULL;
3764 if (remset != global_remset) {
3765 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
3766 free_internal_mem (remset);
3769 /* the per-thread ones */
3770 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3771 for (info = thread_table [i]; info; info = info->next) {
3772 for (remset = info->remset; remset; remset = next) {
3773 remset->store_next = remset->data;
3774 next = remset->next;
3775 remset->next = NULL;
3776 if (remset != info->remset) {
3777 DEBUG (1, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
3778 free_internal_mem (remset);
3785 /* LOCKING: assumes the GC lock is held */
3786 static SgenThreadInfo*
3787 gc_register_current_thread (void *addr)
3790 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
3793 info->id = ARCH_GET_THREAD ();
3794 info->stop_count = -1;
3797 info->stack_start = NULL;
3799 /* try to get it with attributes first */
3800 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
3804 pthread_attr_t attr;
3805 pthread_getattr_np (pthread_self (), &attr);
3806 pthread_attr_getstack (&attr, &sstart, &size);
3807 info->stack_end = (char*)sstart + size;
3808 pthread_attr_destroy (&attr);
3810 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
3811 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
3814 /* FIXME: we assume the stack grows down */
3815 gsize stack_bottom = (gsize)addr;
3816 stack_bottom += 4095;
3817 stack_bottom &= ~4095;
3818 info->stack_end = (char*)stack_bottom;
3822 /* hash into the table */
3823 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
3824 info->next = thread_table [hash];
3825 thread_table [hash] = info;
3827 remembered_set = info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
3828 pthread_setspecific (remembered_set_key, remembered_set);
3829 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
3834 unregister_current_thread (void)
3837 SgenThreadInfo *prev = NULL;
3839 RememberedSet *rset;
3840 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
3842 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
3843 p = thread_table [hash];
3845 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
3846 while (!ARCH_THREAD_EQUALS (p->id, id)) {
3851 thread_table [hash] = p->next;
3853 prev->next = p->next;
3856 /* FIXME: transfer remsets if any */
3858 RememberedSet *next = rset->next;
3859 free_internal_mem (rset);
3866 unregister_thread (void *k)
3869 unregister_current_thread ();
3874 mono_gc_register_thread (void *baseptr)
3876 SgenThreadInfo *info;
3878 info = thread_info_lookup (ARCH_GET_THREAD ());
3880 info = gc_register_current_thread (baseptr);
3882 return info != NULL;
3885 #if USE_PTHREAD_INTERCEPT
3887 #undef pthread_create
3889 #undef pthread_detach
3892 void *(*start_routine) (void *);
3896 } SgenThreadStartInfo;
3899 gc_start_thread (void *arg)
3901 SgenThreadStartInfo *start_info = arg;
3902 SgenThreadInfo* info;
3903 void *t_arg = start_info->arg;
3904 void *(*start_func) (void*) = start_info->start_routine;
3908 info = gc_register_current_thread (&result);
3910 sem_post (&(start_info->registered));
3911 result = start_func (t_arg);
3913 * this is done by the pthread key dtor
3915 unregister_current_thread ();
3923 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
3925 SgenThreadStartInfo *start_info;
3928 start_info = malloc (sizeof (SgenThreadStartInfo));
3931 sem_init (&(start_info->registered), 0, 0);
3932 start_info->arg = arg;
3933 start_info->start_routine = start_routine;
3935 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
3937 while (sem_wait (&(start_info->registered)) != 0) {
3938 /*if (EINTR != errno) ABORT("sem_wait failed"); */
3941 sem_destroy (&(start_info->registered));
3947 mono_gc_pthread_join (pthread_t thread, void **retval)
3949 return pthread_join (thread, retval);
3953 mono_gc_pthread_detach (pthread_t thread)
3955 return pthread_detach (thread);
3958 #endif /* USE_PTHREAD_INTERCEPT */
3961 * ######################################################################
3962 * ######## Write barriers
3963 * ######################################################################
3966 static RememberedSet*
3967 alloc_remset (int size, gpointer id) {
3968 RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)));
3969 res->store_next = res->data;
3970 res->end_set = res->data + size;
3972 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
3977 * Note: the write barriers first do the needed GC work and then do the actual store:
3978 * this way the value is visible to the conservative GC scan after the write barrier
3979 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
3980 * the conservative scan, otherwise by the remembered set scan. FIXME: figure out what
3981 * happens when we need to record which pointers contain references to the new generation.
3982 * The write barrier will be executed, but the pointer is still not stored.
3985 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
3988 if (field_ptr >= nursery_start && field_ptr < nursery_real_end) {
3989 *(void**)field_ptr = value;
3992 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
3993 rs = remembered_set;
3994 if (rs->store_next < rs->end_set) {
3995 *(rs->store_next++) = (mword)field_ptr;
3996 *(void**)field_ptr = value;
3999 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4000 rs->next = remembered_set;
4001 remembered_set = rs;
4002 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4003 *(rs->store_next++) = (mword)field_ptr;
4004 *(void**)field_ptr = value;
4008 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
4010 RememberedSet *rs = remembered_set;
4011 if (slot_ptr >= nursery_start && slot_ptr < nursery_real_end) {
4012 *(void**)slot_ptr = value;
4015 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
4016 if (rs->store_next < rs->end_set) {
4017 *(rs->store_next++) = (mword)slot_ptr;
4018 *(void**)slot_ptr = value;
4021 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4022 rs->next = remembered_set;
4023 remembered_set = rs;
4024 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4025 *(rs->store_next++) = (mword)slot_ptr;
4026 *(void**)slot_ptr = value;
4030 mono_gc_wbarrier_arrayref_copy (MonoArray *arr, gpointer slot_ptr, int count)
4032 RememberedSet *rs = remembered_set;
4033 if (slot_ptr >= nursery_start && slot_ptr < nursery_real_end)
4035 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", slot_ptr, count));
4036 if (rs->store_next + 1 < rs->end_set) {
4037 *(rs->store_next++) = (mword)slot_ptr | REMSET_RANGE;
4038 *(rs->store_next++) = count;
4041 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4042 rs->next = remembered_set;
4043 remembered_set = rs;
4044 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4045 *(rs->store_next++) = (mword)slot_ptr | REMSET_RANGE;
4046 *(rs->store_next++) = count;
4050 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
4052 RememberedSet *rs = remembered_set;
4053 if (ptr >= nursery_start && ptr < nursery_real_end) {
4054 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
4055 *(void**)ptr = value;
4058 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
4059 /* FIXME: ensure it is on the heap */
4060 if (rs->store_next < rs->end_set) {
4061 *(rs->store_next++) = (mword)ptr;
4062 *(void**)ptr = value;
4065 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4066 rs->next = remembered_set;
4067 remembered_set = rs;
4068 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4069 *(rs->store_next++) = (mword)ptr;
4070 *(void**)ptr = value;
4074 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
4076 if (dest >= nursery_start && dest < nursery_real_end) {
4079 DEBUG (1, fprintf (gc_debug_file, "Adding value remset at %p, count %d for class %s\n", dest, count, klass->name));
4083 * mono_gc_wbarrier_object:
4085 * Write barrier to call when obj is the result of a clone or copy of an object.
4088 mono_gc_wbarrier_object (MonoObject* obj)
4090 RememberedSet *rs = remembered_set;
4091 DEBUG (1, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
4092 if (rs->store_next < rs->end_set) {
4093 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
4096 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4097 rs->next = remembered_set;
4098 remembered_set = rs;
4099 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4100 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
4104 * ######################################################################
4105 * ######## Other mono public interface functions.
4106 * ######################################################################
4110 mono_gc_collect (int generation)
4113 update_current_thread_stack (&generation);
4115 if (generation == 0) {
4116 collect_nursery (0);
4118 major_collection ();
4125 mono_gc_max_generation (void)
4131 mono_gc_collection_count (int generation)
4133 if (generation == 0)
4134 return num_minor_gcs;
4135 return num_major_gcs;
4139 mono_gc_get_used_size (void)
4142 GCMemSection *section;
4144 tot = los_memory_usage;
4145 for (section = section_list; section; section = section->next) {
4146 /* this is approximate... */
4147 tot += section->next_data - section->data;
4149 /* FIXME: account for pinned objects */
4155 mono_gc_get_heap_size (void)
4161 mono_gc_disable (void)
4169 mono_gc_enable (void)
4177 mono_object_is_alive (MonoObject* o)
4183 mono_gc_get_generation (MonoObject *obj)
4185 if ((char*)obj >= nursery_start && (char*)obj < nursery_real_end)
4191 mono_gc_enable_events (void)
4196 mono_gc_weak_link_add (void **link_addr, MonoObject *obj)
4198 mono_gc_register_disappearing_link (obj, link_addr);
4203 mono_gc_weak_link_remove (void **link_addr)
4205 mono_gc_register_disappearing_link (NULL, link_addr);
4210 mono_gc_weak_link_get (void **link_addr)
4216 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
4218 if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
4219 mword desc = ROOT_DESC_BITMAP | (bitmap [0] << ROOT_DESC_TYPE_SHIFT);
4222 /* conservative scanning */
4223 DEBUG (3, fprintf (gc_debug_file, "Conservative root descr for size: %d\n", numbits));
4228 mono_gc_alloc_fixed (size_t size, void *descr)
4230 /* FIXME: do a single allocation */
4231 void *res = calloc (1, size);
4234 if (!mono_gc_register_root (res, size, descr)) {
4242 mono_gc_free_fixed (void* addr)
4244 mono_gc_deregister_root (addr);
4249 mono_gc_is_gc_thread (void)
4253 result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
4259 mono_gc_base_init (void)
4262 struct sigaction sinfo;
4264 LOCK_INIT (gc_mutex);
4266 if (gc_initialized) {
4270 pagesize = mono_pagesize ();
4271 gc_debug_file = stderr;
4272 /* format: MONO_GC_DEBUG=l[,filename] where l is a debug level 0-9 */
4273 if ((env = getenv ("MONO_GC_DEBUG"))) {
4274 if (env [0] >= '0' && env [0] <= '9') {
4275 gc_debug_level = atoi (env);
4281 char *rf = g_strdup_printf ("%s.%d", env, getpid ());
4282 gc_debug_file = fopen (rf, "wb");
4284 gc_debug_file = stderr;
4289 sem_init (&suspend_ack_semaphore, 0, 0);
4291 sigfillset (&sinfo.sa_mask);
4292 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
4293 sinfo.sa_handler = suspend_handler;
4294 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
4295 g_error ("failed sigaction");
4298 sinfo.sa_handler = restart_handler;
4299 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
4300 g_error ("failed sigaction");
4303 sigfillset (&suspend_signal_mask);
4304 sigdelset (&suspend_signal_mask, restart_signal_num);
4306 global_remset = alloc_remset (1024, NULL);
4307 global_remset->next = NULL;
4309 pthread_key_create (&remembered_set_key, unregister_thread);
4310 gc_initialized = TRUE;
4312 mono_gc_register_thread (&sinfo);
4316 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
4322 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
4328 mono_gc_get_managed_allocator_by_type (int atype)
4333 #endif /* HAVE_SGEN_GC */