2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
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.
25 * Copyright 2001-2003 Ximian, Inc
26 * Copyright 2003-2010 Novell, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining
29 * a copy of this software and associated documentation files (the
30 * "Software"), to deal in the Software without restriction, including
31 * without limitation the rights to use, copy, modify, merge, publish,
32 * distribute, sublicense, and/or sell copies of the Software, and to
33 * permit persons to whom the Software is furnished to do so, subject to
34 * the following conditions:
36 * The above copyright notice and this permission notice shall be
37 * included in all copies or substantial portions of the Software.
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 * Important: allocation provides always zeroed memory, having to do
49 * a memset after allocation is deadly for performance.
50 * Memory usage at startup is currently as follows:
52 * 64 KB internal space
54 * We should provide a small memory config with half the sizes
56 * We currently try to make as few mono assumptions as possible:
57 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
59 * 2) gc descriptor is the second word in the vtable (first word in the class)
60 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
61 * 4) there is a function to get an object's size and the number of
62 * elements in an array.
63 * 5) we know the special way bounds are allocated for complex arrays
64 * 6) we know about proxies and how to treat them when domains are unloaded
66 * Always try to keep stack usage to a minimum: no recursive behaviour
67 * and no large stack allocs.
69 * General description.
70 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
71 * When the nursery is full we start a nursery collection: this is performed with a
73 * When the old generation is full we start a copying GC of the old generation as well:
74 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
75 * in the future. Maybe we'll even do both during the same collection like IMMIX.
77 * The things that complicate this description are:
78 * *) pinned objects: we can't move them so we need to keep track of them
79 * *) no precise info of the thread stacks and registers: we need to be able to
80 * quickly find the objects that may be referenced conservatively and pin them
81 * (this makes the first issues more important)
82 * *) large objects are too expensive to be dealt with using copying GC: we handle them
83 * with mark/sweep during major collections
84 * *) some objects need to not move even if they are small (interned strings, Type handles):
85 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
86 * PinnedChunks regions
92 *) we could have a function pointer in MonoClass to implement
93 customized write barriers for value types
95 *) investigate the stuff needed to advance a thread to a GC-safe
96 point (single-stepping, read from unmapped memory etc) and implement it.
97 This would enable us to inline allocations and write barriers, for example,
98 or at least parts of them, like the write barrier checks.
99 We may need this also for handling precise info on stacks, even simple things
100 as having uninitialized data on the stack and having to wait for the prolog
101 to zero it. Not an issue for the last frame that we scan conservatively.
102 We could always not trust the value in the slots anyway.
104 *) modify the jit to save info about references in stack locations:
105 this can be done just for locals as a start, so that at least
106 part of the stack is handled precisely.
108 *) test/fix endianess issues
110 *) Implement a card table as the write barrier instead of remembered
111 sets? Card tables are not easy to implement with our current
112 memory layout. We have several different kinds of major heap
113 objects: Small objects in regular blocks, small objects in pinned
114 chunks and LOS objects. If we just have a pointer we have no way
115 to tell which kind of object it points into, therefore we cannot
116 know where its card table is. The least we have to do to make
117 this happen is to get rid of write barriers for indirect stores.
120 *) Get rid of write barriers for indirect stores. We can do this by
121 telling the GC to wbarrier-register an object once we do an ldloca
122 or ldelema on it, and to unregister it once it's not used anymore
123 (it can only travel downwards on the stack). The problem with
124 unregistering is that it needs to happen eventually no matter
125 what, even if exceptions are thrown, the thread aborts, etc.
126 Rodrigo suggested that we could do only the registering part and
127 let the collector find out (pessimistically) when it's safe to
128 unregister, namely when the stack pointer of the thread that
129 registered the object is higher than it was when the registering
130 happened. This might make for a good first implementation to get
131 some data on performance.
133 *) Some sort of blacklist support? Blacklists is a concept from the
134 Boehm GC: if during a conservative scan we find pointers to an
135 area which we might use as heap, we mark that area as unusable, so
136 pointer retention by random pinning pointers is reduced.
138 *) experiment with max small object size (very small right now - 2kb,
139 because it's tied to the max freelist size)
141 *) add an option to mmap the whole heap in one chunk: it makes for many
142 simplifications in the checks (put the nursery at the top and just use a single
143 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
144 not flexible (too much of the address space may be used by default or we can't
145 increase the heap as needed) and we'd need a race-free mechanism to return memory
146 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
147 was written to, munmap is needed, but the following mmap may not find the same segment
150 *) memzero the major fragments after restarting the world and optionally a smaller
153 *) investigate having fragment zeroing threads
155 *) separate locks for finalization and other minor stuff to reduce
158 *) try a different copying order to improve memory locality
160 *) a thread abort after a store but before the write barrier will
161 prevent the write barrier from executing
163 *) specialized dynamically generated markers/copiers
165 *) Dynamically adjust TLAB size to the number of threads. If we have
166 too many threads that do allocation, we might need smaller TLABs,
167 and we might get better performance with larger TLABs if we only
168 have a handful of threads. We could sum up the space left in all
169 assigned TLABs and if that's more than some percentage of the
170 nursery size, reduce the TLAB size.
179 #include <semaphore.h>
184 #include "metadata/metadata-internals.h"
185 #include "metadata/class-internals.h"
186 #include "metadata/gc-internal.h"
187 #include "metadata/object-internals.h"
188 #include "metadata/threads.h"
189 #include "metadata/sgen-gc.h"
190 #include "metadata/sgen-archdep.h"
191 #include "metadata/mono-gc.h"
192 #include "metadata/method-builder.h"
193 #include "metadata/profiler-private.h"
194 #include "metadata/monitor.h"
195 #include "metadata/threadpool-internals.h"
196 #include "metadata/mempool-internals.h"
197 #include "metadata/marshal.h"
198 #include "utils/mono-mmap.h"
199 #include "utils/mono-time.h"
200 #include "utils/mono-semaphore.h"
201 #include "utils/mono-counters.h"
203 #include <mono/utils/memcheck.h>
205 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
209 #include "mono/cil/opcode.def"
216 * ######################################################################
217 * ######## Types and constants used by the GC.
218 * ######################################################################
220 #if SIZEOF_VOID_P == 4
221 typedef guint32 mword;
223 typedef guint64 mword;
226 static int gc_initialized = 0;
227 static int gc_debug_level = 0;
228 static FILE* gc_debug_file;
229 /* If set, do a minor collection before every allocation */
230 static gboolean collect_before_allocs = FALSE;
231 /* If set, do a heap consistency check before each minor collection */
232 static gboolean consistency_check_at_minor_collection = FALSE;
233 /* If set, check that there are no references to the domain left at domain unload */
234 static gboolean xdomain_checks = FALSE;
235 /* If not null, dump the heap after each collection into this file */
236 static FILE *heap_dump_file = NULL;
237 /* If set, mark stacks conservatively, even if precise marking is possible */
238 static gboolean conservative_stack_mark = FALSE;
239 /* If set, do a plausibility check on the scan_starts before and after
241 static gboolean do_scan_starts_check = FALSE;
244 * Turning on heavy statistics will turn off the managed allocator and
245 * the managed write barrier.
247 //#define HEAVY_STATISTICS
249 #ifdef HEAVY_STATISTICS
250 #define HEAVY_STAT(x) x
252 #define HEAVY_STAT(x)
255 #ifdef HEAVY_STATISTICS
256 static long stat_objects_alloced = 0;
257 static long stat_bytes_alloced = 0;
258 static long stat_objects_alloced_degraded = 0;
259 static long stat_bytes_alloced_degraded = 0;
260 static long stat_bytes_alloced_los = 0;
261 static long stat_copy_object_called_nursery = 0;
262 static long stat_objects_copied_nursery = 0;
263 static long stat_copy_object_called_major = 0;
264 static long stat_objects_copied_major = 0;
266 static long stat_copy_object_failed_from_space = 0;
267 static long stat_copy_object_failed_forwarded = 0;
268 static long stat_copy_object_failed_pinned = 0;
269 static long stat_copy_object_failed_large_pinned = 0;
270 static long stat_copy_object_failed_to_space = 0;
272 static long stat_store_remsets = 0;
273 static long stat_store_remsets_unique = 0;
274 static long stat_saved_remsets_1 = 0;
275 static long stat_saved_remsets_2 = 0;
276 static long stat_global_remsets_added = 0;
277 static long stat_global_remsets_processed = 0;
279 static long num_copy_object_called = 0;
280 static long num_objects_copied = 0;
282 static int stat_wbarrier_set_field = 0;
283 static int stat_wbarrier_set_arrayref = 0;
284 static int stat_wbarrier_arrayref_copy = 0;
285 static int stat_wbarrier_generic_store = 0;
286 static int stat_wbarrier_generic_store_remset = 0;
287 static int stat_wbarrier_set_root = 0;
288 static int stat_wbarrier_value_copy = 0;
289 static int stat_wbarrier_object_copy = 0;
292 static long time_minor_pre_collection_fragment_clear = 0;
293 static long time_minor_pinning = 0;
294 static long time_minor_scan_remsets = 0;
295 static long time_minor_scan_pinned = 0;
296 static long time_minor_scan_registered_roots = 0;
297 static long time_minor_scan_thread_data = 0;
298 static long time_minor_finish_gray_stack = 0;
299 static long time_minor_fragment_creation = 0;
301 static long time_major_pre_collection_fragment_clear = 0;
302 static long time_major_pinning = 0;
303 static long time_major_scan_pinned = 0;
304 static long time_major_scan_registered_roots = 0;
305 static long time_major_scan_thread_data = 0;
306 static long time_major_scan_alloc_pinned = 0;
307 static long time_major_scan_finalized = 0;
308 static long time_major_scan_big_objects = 0;
309 static long time_major_finish_gray_stack = 0;
310 static long time_major_sweep = 0;
311 static long time_major_fragment_creation = 0;
313 static long pinned_chunk_bytes_alloced = 0;
314 static long large_internal_bytes_alloced = 0;
317 INTERNAL_MEM_PIN_QUEUE,
318 INTERNAL_MEM_FRAGMENT,
319 INTERNAL_MEM_SECTION,
320 INTERNAL_MEM_SCAN_STARTS,
321 INTERNAL_MEM_FIN_TABLE,
322 INTERNAL_MEM_FINALIZE_ENTRY,
323 INTERNAL_MEM_DISLINK_TABLE,
324 INTERNAL_MEM_DISLINK,
325 INTERNAL_MEM_ROOTS_TABLE,
326 INTERNAL_MEM_ROOT_RECORD,
327 INTERNAL_MEM_STATISTICS,
329 INTERNAL_MEM_GRAY_QUEUE,
330 INTERNAL_MEM_STORE_REMSET,
334 static long small_internal_mem_bytes [INTERNAL_MEM_MAX];
338 mono_gc_flush_info (void)
340 fflush (gc_debug_file);
344 #define MAX_DEBUG_LEVEL 2
345 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
347 /* Define this to allow the user to change some of the constants by specifying
348 * their values in the MONO_GC_PARAMS environmental variable. See
349 * mono_gc_base_init for details. */
350 #define USER_CONFIG 1
352 #define TV_DECLARE(name) gint64 name
353 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
354 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
355 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
357 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
359 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
367 typedef struct _Block Block;
373 /* each request from the OS ends up in a GCMemSection */
374 typedef struct _GCMemSection GCMemSection;
375 struct _GCMemSection {
379 /* pointer where more data could be allocated if it fits */
383 * scan starts is an array of pointers to objects equally spaced in the allocation area
384 * They let use quickly find pinned objects from pinning pointers.
387 /* in major collections indexes in the pin_queue for objects that pin this section */
390 unsigned short num_scan_start;
391 gboolean is_to_space;
394 #define SIZEOF_GC_MEM_SECTION ((sizeof (GCMemSection) + 7) & ~7)
396 /* large object space struct: 64+ KB */
397 /* we could make this limit much smaller to avoid memcpy copy
398 * and potentially have more room in the GC descriptor: need to measure
399 * This also means that such small OS objects will need to be
400 * allocated in a different way (using pinned chunks).
401 * We may want to put large but smaller than 64k objects in the fixed space
402 * when we move the object from one generation to another (to limit the
403 * pig in the snake effect).
404 * Note: it may be worth to have an optimized copy function, since we can
405 * assume that objects are aligned and have a multiple of 8 size.
406 * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
407 * true if MONO_ZERO_LEN_ARRAY is nonzero.
409 typedef struct _LOSObject LOSObject;
412 mword size; /* this is the object size */
413 int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN and data starting at same alignment */
416 char data [MONO_ZERO_LEN_ARRAY];
419 /* Pinned objects are allocated in the LOS space if bigger than half a page
420 * or from freelists otherwise. We assume that pinned objects are relatively few
421 * and they have a slow dying speed (like interned strings, thread objects).
422 * As such they will be collected only at major collections.
423 * free lists are not global: when we need memory we allocate a PinnedChunk.
424 * Each pinned chunk is made of several pages, the first of wich is used
425 * internally for bookeeping (here think of a page as 4KB). The bookeeping
426 * includes the freelists vectors and info about the object size of each page
427 * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
428 * a size is assigned to it, the page is divided in the proper chunks and each
429 * chunk is added to the freelist. To not waste space, the remaining space in the
430 * first page is used as objects of size 16 or 32 (need to measure which are more
432 * We use this same structure to allocate memory used internally by the GC, so
433 * we never use malloc/free if we need to alloc during collection: the world is stopped
434 * and malloc/free will deadlock.
435 * When we want to iterate over pinned objects, we just scan a page at a time
436 * linearly according to the size of objects in the page: the next pointer used to link
437 * the items in the freelist uses the same word as the vtable. Since we keep freelists
438 * for each pinned chunk, if the word points outside the pinned chunk it means
440 * We could avoid this expensive scanning in creative ways. We could have a policy
441 * of putting in the pinned space only objects we know about that have no struct fields
442 * with references and we can easily use a even expensive write barrier for them,
443 * since pointer writes on such objects should be rare.
444 * The best compromise is to just alloc interned strings and System.MonoType in them.
445 * It would be nice to allocate MonoThread in it, too: must check that we properly
446 * use write barriers so we don't have to do any expensive scanning of the whole pinned
447 * chunk list during minor collections. We can avoid it now because we alloc in it only
448 * reference-free objects.
450 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
451 #define MAX_FREELIST_SIZE 2048
452 #define PINNED_PAGE_SIZE (4096)
453 #define PINNED_CHUNK_MIN_SIZE (4096*8)
454 typedef struct _PinnedChunk PinnedChunk;
455 struct _PinnedChunk {
458 int *page_sizes; /* a 0 means the page is still unused */
461 void *data [1]; /* page sizes and free lists are stored here */
464 /* The method used to clear the nursery */
465 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
466 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
471 CLEAR_AT_TLAB_CREATION
472 } NurseryClearPolicy;
474 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
477 * If this is set, the nursery is aligned to an address aligned to its size, ie.
478 * a 1MB nursery will be aligned to an address divisible by 1MB. This allows us to
479 * speed up ptr_in_nursery () checks which are very frequent. This requires the
480 * nursery size to be a compile time constant.
482 #define ALIGN_NURSERY 1
485 * The young generation is divided into fragments. This is because
486 * we can hand one fragments to a thread for lock-less fast alloc and
487 * because the young generation ends up fragmented anyway by pinned objects.
488 * Once a collection is done, a list of fragments is created. When doing
489 * thread local alloc we use smallish nurseries so we allow new threads to
490 * allocate memory from gen0 without triggering a collection. Threads that
491 * are found to allocate lots of memory are given bigger fragments. This
492 * should make the finalizer thread use little nursery memory after a while.
493 * We should start assigning threads very small fragments: if there are many
494 * threads the nursery will be full of reserved space that the threads may not
495 * use at all, slowing down allocation speed.
496 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
497 * Allocation Buffers (TLABs).
499 typedef struct _Fragment Fragment;
503 char *fragment_start;
504 char *fragment_limit; /* the current soft limit for allocation */
508 /* the runtime can register areas of memory as roots: we keep two lists of roots,
509 * a pinned root set for conservatively scanned roots and a normal one for
510 * precisely scanned roots (currently implemented as a single list).
512 typedef struct _RootRecord RootRecord;
520 /* for use with write barriers */
521 typedef struct _RememberedSet RememberedSet;
522 struct _RememberedSet {
526 mword data [MONO_ZERO_LEN_ARRAY];
530 * We're never actually using the first element. It's always set to
531 * NULL to simplify the elimination of consecutive duplicate
534 #define STORE_REMSET_BUFFER_SIZE 1024
536 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
537 struct _GenericStoreRememberedSet {
538 GenericStoreRememberedSet *next;
539 /* We need one entry less because the first entry of store
540 remset buffers is always a dummy and we don't copy it. */
541 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
544 /* we have 4 possible values in the low 2 bits */
546 REMSET_LOCATION, /* just a pointer to the exact location */
547 REMSET_RANGE, /* range of pointer fields */
548 REMSET_OBJECT, /* mark all the object for scanning */
549 REMSET_OTHER, /* all others */
550 REMSET_TYPE_MASK = 0x3
553 /* Subtypes of REMSET_OTHER */
555 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
556 REMSET_ROOT_LOCATION, /* a location inside a root */
559 #ifdef HAVE_KW_THREAD
560 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
562 static pthread_key_t remembered_set_key;
563 static RememberedSet *global_remset;
564 static RememberedSet *freed_thread_remsets;
565 //static int store_to_global_remset = 0;
566 static GenericStoreRememberedSet *generic_store_remsets = NULL;
568 /* FIXME: later choose a size that takes into account the RememberedSet struct
569 * and doesn't waste any alloc paddin space.
571 #define DEFAULT_REMSET_SIZE 1024
572 static RememberedSet* alloc_remset (int size, gpointer id);
574 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
575 * no cast from a pointer to an integer
582 /* these bits are set in the object vtable: we could merge them since an object can be
583 * either pinned or forwarded but not both.
584 * We store them in the vtable slot because the bits are used in the sync block for
585 * other purposes: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
586 * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
587 * would be an invalid combination for the monitor and hash code).
588 * The values are already shifted.
589 * The forwarding address is stored in the sync block.
591 #define FORWARDED_BIT 1
593 #define VTABLE_BITS_MASK 0x3
595 /* returns NULL if not forwarded, or the forwarded address */
596 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
597 /* set the forwarded address fw_addr for object obj */
598 #define forward_object(obj,fw_addr) do { \
599 ((mword*)(obj))[0] |= FORWARDED_BIT; \
600 ((mword*)(obj))[1] = (mword)(fw_addr); \
603 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
604 #define pin_object(obj) do { \
605 ((mword*)(obj))[0] |= PINNED_BIT; \
607 #define unpin_object(obj) do { \
608 ((mword*)(obj))[0] &= ~PINNED_BIT; \
612 #define ptr_in_nursery(ptr) (((mword)(ptr) & ~((1 << DEFAULT_NURSERY_BITS) - 1)) == (mword)nursery_start)
614 #define ptr_in_nursery(ptr) ((char*)(ptr) >= nursery_start && (char*)(ptr) < nursery_real_end)
618 * Since we set bits in the vtable, use the macro to load it from the pointer to
619 * an object that is potentially pinned.
621 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
624 safe_name (void* obj)
626 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
627 return vt->klass->name;
631 safe_object_get_size (MonoObject* o)
633 MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
634 if (klass == mono_defaults.string_class) {
635 return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
636 } else if (klass->rank) {
637 MonoArray *array = (MonoArray*)o;
638 size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length (array);
639 if (G_UNLIKELY (array->bounds)) {
640 size += sizeof (mono_array_size_t) - 1;
641 size &= ~(sizeof (mono_array_size_t) - 1);
642 size += sizeof (MonoArrayBounds) * klass->rank;
646 /* from a created object: the class must be inited already */
647 return klass->instance_size;
652 * ######################################################################
653 * ######## Global data.
654 * ######################################################################
656 static LOCK_DECLARE (gc_mutex);
657 static int gc_disabled = 0;
658 static int num_minor_gcs = 0;
659 static int num_major_gcs = 0;
663 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
664 //#define DEFAULT_NURSERY_SIZE (1024*512*125+4096*118)
665 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
666 static int default_nursery_size = (1 << 20);
668 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
669 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
670 static int default_nursery_bits = 20;
675 #define DEFAULT_NURSERY_SIZE (1024*512*2)
677 #define DEFAULT_NURSERY_BITS 20
682 #define MAJOR_SECTION_SIZE (128*1024)
683 #define BLOCK_FOR_OBJECT(o) ((Block*)(((mword)(o)) & ~(MAJOR_SECTION_SIZE - 1)))
684 #define MAJOR_SECTION_FOR_OBJECT(o) ((GCMemSection*)BLOCK_FOR_OBJECT ((o)))
685 #define MIN_MINOR_COLLECTION_SECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 3 / MAJOR_SECTION_SIZE)
686 #define MIN_LOS_ALLOWANCE (DEFAULT_NURSERY_SIZE * 2)
687 /* to quickly find the head of an object pinned by a conservative address
688 * we keep track of the objects allocated for each SCAN_START_SIZE memory
689 * chunk in the nursery or other memory sections. Larger values have less
690 * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
692 #define SCAN_START_SIZE (4096*2)
693 /* the minimum size of a fragment that we consider useful for allocation */
694 #define FRAGMENT_MIN_SIZE (512)
695 /* This is a fixed value used for pinned chunks, not the system pagesize */
696 #define FREELIST_PAGESIZE 4096
698 static mword pagesize = 4096;
699 static mword nursery_size;
700 static int degraded_mode = 0;
702 static int minor_collection_section_allowance;
703 static int minor_collection_sections_alloced = 0;
704 static int num_major_sections = 0;
706 static LOSObject *los_object_list = NULL;
707 static mword los_memory_usage = 0;
708 static mword los_num_objects = 0;
709 static mword next_los_collection = 2*1024*1024; /* 2 MB, need to tune */
710 static mword total_alloc = 0;
711 /* use this to tune when to do a major/minor collection */
712 static mword memory_pressure = 0;
714 static GCMemSection *section_list = NULL;
715 static GCMemSection *nursery_section = NULL;
716 static mword lowest_heap_address = ~(mword)0;
717 static mword highest_heap_address = 0;
719 static LOCK_DECLARE (interruption_mutex);
721 typedef struct _FinalizeEntry FinalizeEntry;
722 struct _FinalizeEntry {
727 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
728 struct _FinalizeEntryHashTable {
729 FinalizeEntry **table;
734 typedef struct _DisappearingLink DisappearingLink;
735 struct _DisappearingLink {
736 DisappearingLink *next;
740 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
741 struct _DisappearingLinkHashTable {
742 DisappearingLink **table;
747 #define LARGE_INTERNAL_MEM_HEADER_MAGIC 0x7d289f3a
749 typedef struct _LargeInternalMemHeader LargeInternalMemHeader;
750 struct _LargeInternalMemHeader {
763 * The link pointer is hidden by negating each bit. We use the lowest
764 * bit of the link (before negation) to store whether it needs
765 * resurrection tracking.
767 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
768 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
770 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
771 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
774 * The finalizable hash has the object as the key, the
775 * disappearing_link hash, has the link address as key.
777 static FinalizeEntryHashTable minor_finalizable_hash;
778 static FinalizeEntryHashTable major_finalizable_hash;
779 /* objects that are ready to be finalized */
780 static FinalizeEntry *fin_ready_list = NULL;
781 static FinalizeEntry *critical_fin_list = NULL;
783 static DisappearingLinkHashTable minor_disappearing_link_hash;
784 static DisappearingLinkHashTable major_disappearing_link_hash;
786 static int num_ready_finalizers = 0;
787 static int no_finalize = 0;
789 /* keep each size a multiple of ALLOC_ALIGN */
790 /* on 64 bit systems 8 is likely completely unused. */
791 static const int freelist_sizes [] = {
792 8, 16, 24, 32, 40, 48, 64, 80,
793 96, 128, 160, 192, 224, 256, 320, 384,
794 448, 512, 584, 680, 816, 1024, 1360, 2048};
795 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
797 static char* max_pinned_chunk_addr = NULL;
798 static char* min_pinned_chunk_addr = (char*)-1;
799 /* pinned_chunk_list is used for allocations of objects that are never moved */
800 static PinnedChunk *pinned_chunk_list = NULL;
801 /* internal_chunk_list is used for allocating structures needed by the GC */
802 static PinnedChunk *internal_chunk_list = NULL;
805 obj_is_from_pinned_alloc (char *p)
807 return BLOCK_FOR_OBJECT (p)->role == MEMORY_ROLE_PINNED;
810 static int slot_for_size (size_t size);
813 free_pinned_object (PinnedChunk *chunk, char *obj, size_t size)
815 void **p = (void**)obj;
816 int slot = slot_for_size (size);
818 g_assert (obj >= (char*)chunk->start_data && obj < ((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE));
819 *p = chunk->free_list [slot];
820 chunk->free_list [slot] = p;
824 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
825 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
826 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
830 /* registered roots: the key to the hash is the root start address */
832 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
834 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
835 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
836 static mword roots_size = 0; /* amount of memory in the root set */
837 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
840 * The current allocation cursors
841 * We allocate objects in the nursery.
842 * The nursery is the area between nursery_start and nursery_real_end.
843 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
844 * from nursery fragments.
845 * tlab_next is the pointer to the space inside the TLAB where the next object will
847 * tlab_temp_end is the pointer to the end of the temporary space reserved for
848 * the allocation: it allows us to set the scan starts at reasonable intervals.
849 * tlab_real_end points to the end of the TLAB.
850 * nursery_frag_real_end points to the end of the currently used nursery fragment.
851 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
852 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
853 * At the next allocation, the area of the nursery where objects can be present is
854 * between MIN(nursery_first_pinned_start, first_fragment_start) and
855 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
857 static char *nursery_start = NULL;
859 /* eventually share with MonoThread? */
860 typedef struct _SgenThreadInfo SgenThreadInfo;
862 struct _SgenThreadInfo {
863 SgenThreadInfo *next;
865 unsigned int stop_count; /* to catch duplicate signals */
868 volatile int in_critical_region;
871 void *stack_start_limit;
872 char **tlab_next_addr;
873 char **tlab_start_addr;
874 char **tlab_temp_end_addr;
875 char **tlab_real_end_addr;
876 gpointer **store_remset_buffer_addr;
877 long *store_remset_buffer_index_addr;
878 RememberedSet *remset;
879 gpointer runtime_data;
880 gpointer stopped_ip; /* only valid if the thread is stopped */
881 MonoDomain *stopped_domain; /* ditto */
882 gpointer *stopped_regs; /* ditto */
883 #ifndef HAVE_KW_THREAD
888 gpointer *store_remset_buffer;
889 long store_remset_buffer_index;
893 #ifdef HAVE_KW_THREAD
894 #define TLAB_ACCESS_INIT
895 #define TLAB_START tlab_start
896 #define TLAB_NEXT tlab_next
897 #define TLAB_TEMP_END tlab_temp_end
898 #define TLAB_REAL_END tlab_real_end
899 #define REMEMBERED_SET remembered_set
900 #define STORE_REMSET_BUFFER store_remset_buffer
901 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
902 #define IN_CRITICAL_REGION thread_info->in_critical_region
904 static pthread_key_t thread_info_key;
905 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
906 #define TLAB_START (__thread_info__->tlab_start)
907 #define TLAB_NEXT (__thread_info__->tlab_next)
908 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
909 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
910 #define REMEMBERED_SET (__thread_info__->remset)
911 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
912 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
913 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
916 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
917 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
918 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
921 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
922 * variables for next+temp_end ?
924 #ifdef HAVE_KW_THREAD
925 static __thread SgenThreadInfo *thread_info;
926 static __thread char *tlab_start;
927 static __thread char *tlab_next;
928 static __thread char *tlab_temp_end;
929 static __thread char *tlab_real_end;
930 static __thread gpointer *store_remset_buffer;
931 static __thread long store_remset_buffer_index;
932 /* Used by the managed allocator/wbarrier */
933 static __thread char **tlab_next_addr;
934 static __thread char *stack_end;
935 static __thread long *store_remset_buffer_index_addr;
937 static char *nursery_next = NULL;
938 static char *nursery_frag_real_end = NULL;
939 static char *nursery_real_end = NULL;
940 //static char *nursery_first_pinned_start = NULL;
941 static char *nursery_last_pinned_end = NULL;
943 /* The size of a TLAB */
944 /* The bigger the value, the less often we have to go to the slow path to allocate a new
945 * one, but the more space is wasted by threads not allocating much memory.
947 * FIXME: Make this self-tuning for each thread.
949 static guint32 tlab_size = (1024 * 4);
951 /* fragments that are free and ready to be used for allocation */
952 static Fragment *nursery_fragments = NULL;
953 /* freeelist of fragment structures */
954 static Fragment *fragment_freelist = NULL;
957 * used when moving the objects
959 static char *to_space_bumper = NULL;
960 static char *to_space_top = NULL;
961 static GCMemSection *to_space_section = NULL;
963 /* objects bigger then this go into the large object space */
964 #define MAX_SMALL_OBJ_SIZE MAX_FREELIST_SIZE
966 /* Functions supplied by the runtime to be called by the GC */
967 static MonoGCCallbacks gc_callbacks;
970 * ######################################################################
971 * ######## Macros and function declarations.
972 * ######################################################################
975 #define UPDATE_HEAP_BOUNDARIES(low,high) do { \
976 if ((mword)(low) < lowest_heap_address) \
977 lowest_heap_address = (mword)(low); \
978 if ((mword)(high) > highest_heap_address) \
979 highest_heap_address = (mword)(high); \
981 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
984 align_pointer (void *ptr)
986 mword p = (mword)ptr;
987 p += sizeof (gpointer) - 1;
988 p &= ~ (sizeof (gpointer) - 1);
992 /* forward declarations */
993 static void* get_internal_mem (size_t size, int type);
994 static void free_internal_mem (void *addr, int type);
995 static void* get_os_memory (size_t size, int activate);
996 static void free_os_memory (void *addr, size_t size);
997 static G_GNUC_UNUSED void report_internal_mem_usage (void);
999 static int stop_world (void);
1000 static int restart_world (void);
1001 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
1002 static void scan_from_remsets (void *start_nursery, void *end_nursery);
1003 static void find_pinning_ref_from_thread (char *obj, size_t size);
1004 static void update_current_thread_stack (void *start);
1005 static GCMemSection* alloc_major_section (void);
1006 static void finalize_in_range (char *start, char *end, int generation);
1007 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
1008 static void null_link_in_range (char *start, char *end, int generation);
1009 static void null_links_for_domain (MonoDomain *domain, int generation);
1010 static gboolean search_fragment_for_size (size_t size);
1011 static void mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end);
1012 static void clear_remsets (void);
1013 static void clear_tlabs (void);
1014 typedef void (*ScanPinnedObjectCallbackFunc) (PinnedChunk*, char*, size_t, void*);
1015 static void scan_pinned_objects (ScanPinnedObjectCallbackFunc callback, void *callback_data);
1016 static void sweep_pinned_objects (void);
1017 static void scan_from_pinned_objects (char *addr_start, char *addr_end);
1018 static void free_large_object (LOSObject *obj);
1019 static void free_major_section (GCMemSection *section);
1020 static void to_space_expand (void);
1022 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
1024 void describe_ptr (char *ptr);
1025 void check_consistency (void);
1026 char* check_object (char *start);
1028 void mono_gc_scan_for_specific_ref (MonoObject *key);
1031 * ######################################################################
1032 * ######## GC descriptors
1033 * ######################################################################
1034 * Used to quickly get the info the GC needs about an object: size and
1035 * where the references are held.
1037 /* objects are aligned to 8 bytes boundaries
1038 * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
1039 * The low 3 bits define the type of the descriptor. The other bits
1040 * depend on the type.
1041 * As a general rule the 13 remaining low bits define the size, either
1042 * of the whole object or of the elements in the arrays. While for objects
1043 * the size is already in bytes, for arrays we need to shift, because
1044 * array elements might be smaller than 8 bytes. In case of arrays, we
1045 * use two bits to describe what the additional high bits represents,
1046 * so the default behaviour can handle element sizes less than 2048 bytes.
1047 * The high 16 bits, if 0 it means the object is pointer-free.
1048 * This design should make it easy and fast to skip over ptr-free data.
1049 * The first 4 types should cover >95% of the objects.
1050 * Note that since the size of objects is limited to 64K, larger objects
1051 * will be allocated in the large object heap.
1052 * If we want 4-bytes alignment, we need to put vector and small bitmap
1056 DESC_TYPE_RUN_LENGTH, /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
1057 DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
1058 DESC_TYPE_STRING, /* nothing */
1059 DESC_TYPE_COMPLEX, /* index for bitmap into complex_descriptors */
1060 DESC_TYPE_VECTOR, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1061 DESC_TYPE_ARRAY, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1062 DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
1063 DESC_TYPE_COMPLEX_ARR, /* index for bitmap into complex_descriptors */
1064 /* subtypes for arrays and vectors */
1065 DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value */
1066 DESC_TYPE_V_REFS, /* all the array elements are refs */
1067 DESC_TYPE_V_RUN_LEN, /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
1068 DESC_TYPE_V_BITMAP /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
1071 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
1072 #define LOW_TYPE_BITS 3
1073 #define SMALL_BITMAP_SHIFT 16
1074 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
1075 #define VECTOR_INFO_SHIFT 14
1076 #define VECTOR_ELSIZE_SHIFT 3
1077 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
1078 #define MAX_SMALL_SIZE ((1 << SMALL_BITMAP_SHIFT) - 1)
1079 #define SMALL_SIZE_MASK 0xfff8
1080 #define MAX_ELEMENT_SIZE 0x3ff
1081 #define ELEMENT_SIZE_MASK (0x3ff << LOW_TYPE_BITS)
1082 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
1083 #define VECTOR_SUBTYPE_REFS (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
1084 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
1085 #define VECTOR_SUBTYPE_BITMAP (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
1087 #define ALLOC_ALIGN 8
1090 /* Root bitmap descriptors are simpler: the lower three bits describe the type
1091 * and we either have 30/62 bitmap bits or nibble-based run-length,
1092 * or a complex descriptor, or a user defined marker function.
1095 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
1100 ROOT_DESC_TYPE_MASK = 0x7,
1101 ROOT_DESC_TYPE_SHIFT = 3,
1104 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
1106 #define MAX_USER_DESCRIPTORS 16
1108 static gsize* complex_descriptors = NULL;
1109 static int complex_descriptors_size = 0;
1110 static int complex_descriptors_next = 0;
1111 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
1112 static int user_descriptors_next = 0;
1115 alloc_complex_descriptor (gsize *bitmap, int numbits)
1119 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
1120 nwords = numbits / GC_BITS_PER_WORD + 1;
1123 res = complex_descriptors_next;
1124 /* linear search, so we don't have duplicates with domain load/unload
1125 * this should not be performance critical or we'd have bigger issues
1126 * (the number and size of complex descriptors should be small).
1128 for (i = 0; i < complex_descriptors_next; ) {
1129 if (complex_descriptors [i] == nwords) {
1130 int j, found = TRUE;
1131 for (j = 0; j < nwords - 1; ++j) {
1132 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
1142 i += complex_descriptors [i];
1144 if (complex_descriptors_next + nwords > complex_descriptors_size) {
1145 int new_size = complex_descriptors_size * 2 + nwords;
1146 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
1147 complex_descriptors_size = new_size;
1149 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
1150 complex_descriptors_next += nwords;
1151 complex_descriptors [res] = nwords;
1152 for (i = 0; i < nwords - 1; ++i) {
1153 complex_descriptors [res + 1 + i] = bitmap [i];
1154 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
1161 * Descriptor builders.
1164 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
1166 return (void*) DESC_TYPE_STRING;
1170 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
1172 int first_set = -1, num_set = 0, last_set = -1, i;
1174 size_t stored_size = obj_size;
1175 stored_size += ALLOC_ALIGN - 1;
1176 stored_size &= ~(ALLOC_ALIGN - 1);
1177 for (i = 0; i < numbits; ++i) {
1178 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1185 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
1186 /* check run-length encoding first: one byte offset, one byte number of pointers
1187 * on 64 bit archs, we can have 3 runs, just one on 32.
1188 * It may be better to use nibbles.
1190 if (first_set < 0) {
1191 desc = DESC_TYPE_RUN_LENGTH | stored_size;
1192 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1193 return (void*) desc;
1194 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1195 desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
1196 DEBUG (6, fprintf (gc_debug_file, "Runlen descriptor %p, size: %zd, first set: %d, num set: %d\n", (void*)desc, stored_size, first_set, num_set));
1197 return (void*) desc;
1199 /* we know the 2-word header is ptr-free */
1200 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1201 desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1202 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1203 return (void*) desc;
1206 /* we know the 2-word header is ptr-free */
1207 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1208 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1209 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1210 return (void*) desc;
1212 /* it's a complex object ... */
1213 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1214 return (void*) desc;
1217 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1219 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1221 int first_set = -1, num_set = 0, last_set = -1, i;
1222 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1223 for (i = 0; i < numbits; ++i) {
1224 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1231 if (elem_size <= MAX_ELEMENT_SIZE) {
1232 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1234 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1236 /* Note: we also handle structs with just ref fields */
1237 if (num_set * sizeof (gpointer) == elem_size) {
1238 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1240 /* FIXME: try run-len first */
1241 /* Note: we can't skip the object header here, because it's not present */
1242 if (last_set <= SMALL_BITMAP_SIZE) {
1243 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1246 /* it's am array of complex structs ... */
1247 desc = DESC_TYPE_COMPLEX_ARR;
1248 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1249 return (void*) desc;
1252 /* Return the bitmap encoded by a descriptor */
1254 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1256 mword d = (mword)descr;
1260 case DESC_TYPE_RUN_LENGTH: {
1261 int first_set = (d >> 16) & 0xff;
1262 int num_set = (d >> 24) & 0xff;
1265 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1267 for (i = first_set; i < first_set + num_set; ++i)
1268 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1270 *numbits = first_set + num_set;
1274 case DESC_TYPE_SMALL_BITMAP:
1275 bitmap = g_new0 (gsize, 1);
1277 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1279 *numbits = GC_BITS_PER_WORD;
1283 g_assert_not_reached ();
1287 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
1288 #define STRING_SIZE(size,str) do { \
1289 (size) = sizeof (MonoString) + 2 * mono_string_length ((MonoString*)(str)) + 2; \
1290 (size) += (ALLOC_ALIGN - 1); \
1291 (size) &= ~(ALLOC_ALIGN - 1); \
1294 #define OBJ_RUN_LEN_SIZE(size,desc,obj) do { \
1295 (size) = (desc) & 0xfff8; \
1298 #define OBJ_BITMAP_SIZE(size,desc,obj) do { \
1299 (size) = (desc) & 0xfff8; \
1302 //#define PREFETCH(addr) __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(addr)))
1303 #define PREFETCH(addr)
1305 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
1306 #define OBJ_RUN_LEN_FOREACH_PTR(desc,obj) do { \
1307 if ((desc) & 0xffff0000) { \
1308 /* there are pointers */ \
1309 void **_objptr_end; \
1310 void **_objptr = (void**)(obj); \
1311 _objptr += ((desc) >> 16) & 0xff; \
1312 _objptr_end = _objptr + (((desc) >> 24) & 0xff); \
1313 while (_objptr < _objptr_end) { \
1314 HANDLE_PTR (_objptr, (obj)); \
1320 /* a bitmap desc means that there are pointer references or we'd have
1321 * choosen run-length, instead: add an assert to check.
1323 #define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
1324 /* there are pointers */ \
1325 void **_objptr = (void**)(obj); \
1326 gsize _bmap = (desc) >> 16; \
1327 _objptr += OBJECT_HEADER_WORDS; \
1329 if ((_bmap & 1)) { \
1330 HANDLE_PTR (_objptr, (obj)); \
1337 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj) do { \
1338 /* there are pointers */ \
1339 void **_objptr = (void**)(obj); \
1340 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS; \
1341 _objptr += OBJECT_HEADER_WORDS; \
1343 if ((_bmap & 1)) { \
1344 HANDLE_PTR (_objptr, (obj)); \
1351 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do { \
1352 /* there are pointers */ \
1353 void **_objptr = (void**)(obj); \
1354 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1355 int bwords = (*bitmap_data) - 1; \
1356 void **start_run = _objptr; \
1359 MonoObject *myobj = (MonoObject*)obj; \
1360 g_print ("found %d at %p (0x%zx): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1362 while (bwords-- > 0) { \
1363 gsize _bmap = *bitmap_data++; \
1364 _objptr = start_run; \
1365 /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/ \
1367 if ((_bmap & 1)) { \
1368 HANDLE_PTR (_objptr, (obj)); \
1373 start_run += GC_BITS_PER_WORD; \
1377 /* this one is untested */
1378 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj) do { \
1379 /* there are pointers */ \
1380 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1381 int mbwords = (*mbitmap_data++) - 1; \
1382 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass); \
1383 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1384 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1386 MonoObject *myobj = (MonoObject*)start; \
1387 g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1389 while (e_start < e_end) { \
1390 void **_objptr = (void**)e_start; \
1391 gsize *bitmap_data = mbitmap_data; \
1392 unsigned int bwords = mbwords; \
1393 while (bwords-- > 0) { \
1394 gsize _bmap = *bitmap_data++; \
1395 void **start_run = _objptr; \
1396 /*g_print ("bitmap: 0x%x\n", _bmap);*/ \
1398 if ((_bmap & 1)) { \
1399 HANDLE_PTR (_objptr, (obj)); \
1404 _objptr = start_run + GC_BITS_PER_WORD; \
1406 e_start += el_size; \
1410 #define OBJ_VECTOR_FOREACH_PTR(vt,obj) do { \
1411 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */ \
1412 if ((vt)->desc & 0xffffc000) { \
1413 int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE; \
1414 /* there are pointers */ \
1415 int etype = (vt)->desc & 0xc000; \
1416 if (etype == (DESC_TYPE_V_REFS << 14)) { \
1417 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector)); \
1418 void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
1419 /* Note: this code can handle also arrays of struct with only references in them */ \
1420 while (p < end_refs) { \
1421 HANDLE_PTR (p, (obj)); \
1424 } else if (etype == DESC_TYPE_V_RUN_LEN << 14) { \
1425 int offset = ((vt)->desc >> 16) & 0xff; \
1426 int num_refs = ((vt)->desc >> 24) & 0xff; \
1427 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1428 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1429 while (e_start < e_end) { \
1430 void **p = (void**)e_start; \
1433 for (i = 0; i < num_refs; ++i) { \
1434 HANDLE_PTR (p + i, (obj)); \
1436 e_start += el_size; \
1438 } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
1439 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1440 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1441 while (e_start < e_end) { \
1442 void **p = (void**)e_start; \
1443 gsize _bmap = (vt)->desc >> 16; \
1444 /* Note: there is no object header here to skip */ \
1446 if ((_bmap & 1)) { \
1447 HANDLE_PTR (p, (obj)); \
1452 e_start += el_size; \
1458 #define COUNT_OBJECT_TYPES do { \
1459 switch (desc & 0x7) { \
1460 case DESC_TYPE_STRING: type_str++; break; \
1461 case DESC_TYPE_RUN_LENGTH: type_rlen++; break; \
1462 case DESC_TYPE_ARRAY: case DESC_TYPE_VECTOR: type_vector++; break; \
1463 case DESC_TYPE_SMALL_BITMAP: type_bitmap++; break; \
1464 case DESC_TYPE_LARGE_BITMAP: type_lbit++; break; \
1465 case DESC_TYPE_COMPLEX: type_complex++; break; \
1466 case DESC_TYPE_COMPLEX_ARR: type_complex++; break; \
1467 default: g_assert_not_reached (); \
1473 * ######################################################################
1474 * ######## Detecting and removing garbage.
1475 * ######################################################################
1476 * This section of code deals with detecting the objects no longer in use
1477 * and reclaiming the memory.
1481 static mword new_obj_references = 0;
1482 static mword obj_references_checked = 0;
1485 #define HANDLE_PTR(ptr,obj) do { \
1486 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
1487 new_obj_references++; \
1488 /*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);*/ \
1490 obj_references_checked++; \
1494 static void __attribute__((noinline))
1495 scan_area (char *start, char *end)
1498 int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
1499 new_obj_references = 0;
1500 obj_references_checked = 0;
1501 while (start < end) {
1502 if (!*(void**)start) {
1503 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1506 vt = (GCVTable*)LOAD_VTABLE (start);
1507 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
1509 MonoObject *obj = (MonoObject*)start;
1510 g_print ("found at %p (0x%zx): %s.%s\n", start, vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
1513 #define SCAN_OBJECT_ACTION COUNT_OBJECT_TYPES
1514 #include "sgen-scan-object.h"
1516 /*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);
1517 printf ("\tstrings: %d, runl: %d, vector: %d, bitmaps: %d, lbitmaps: %d, complex: %d\n",
1518 type_str, type_rlen, type_vector, type_bitmap, type_lbit, type_complex);*/
1523 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1525 MonoObject *o = (MonoObject*)(obj);
1526 MonoObject *ref = (MonoObject*)*(ptr);
1527 int offset = (char*)(ptr) - (char*)o;
1529 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1531 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1533 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1534 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1536 /* Thread.cached_culture_info */
1537 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1538 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1539 !strcmp(o->vtable->klass->name_space, "System") &&
1540 !strcmp(o->vtable->klass->name, "Object[]"))
1543 * at System.IO.MemoryStream.InternalConstructor (byte[],int,int,bool,bool) [0x0004d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:121
1544 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1545 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1546 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1547 * at System.Runtime.Remoting.Messaging.MethodCall..ctor (System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/MethodCall.cs:87
1548 * at System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) [0x00018] in /home/schani/Work/novell/trunk/mcs/class/corlib/System/AppDomain.cs:1213
1549 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1550 * at System.Runtime.Remoting.Channels.CrossAppDomainSink.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00008] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Channels/CrossAppDomainChannel.cs:198
1551 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1553 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1554 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1555 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1556 !strcmp (o->vtable->klass->name, "MemoryStream"))
1558 /* append_job() in threadpool.c */
1559 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1560 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1561 !strcmp (o->vtable->klass->name_space, "System") &&
1562 !strcmp (o->vtable->klass->name, "Object[]") &&
1563 mono_thread_pool_is_queue_array ((MonoArray*) o))
1569 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1571 MonoObject *o = (MonoObject*)(obj);
1572 MonoObject *ref = (MonoObject*)*(ptr);
1573 int offset = (char*)(ptr) - (char*)o;
1575 MonoClassField *field;
1578 if (!ref || ref->vtable->domain == domain)
1580 if (is_xdomain_ref_allowed (ptr, obj, domain))
1584 for (class = o->vtable->klass; class; class = class->parent) {
1587 for (i = 0; i < class->field.count; ++i) {
1588 if (class->fields[i].offset == offset) {
1589 field = &class->fields[i];
1597 if (ref->vtable->klass == mono_defaults.string_class)
1598 str = mono_string_to_utf8 ((MonoString*)ref);
1601 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1602 o, o->vtable->klass->name_space, o->vtable->klass->name,
1603 offset, field ? field->name : "",
1604 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1605 mono_gc_scan_for_specific_ref (o);
1611 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1614 scan_object_for_xdomain_refs (char *start)
1616 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1618 #include "sgen-scan-object.h"
1624 scan_area_for_xdomain_refs (char *start, char *end)
1626 while (start < end) {
1627 if (!*(void**)start) {
1628 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1632 start = scan_object_for_xdomain_refs (start);
1637 #define HANDLE_PTR(ptr,obj) do { \
1638 if ((MonoObject*)*(ptr) == key) { \
1639 g_print ("found ref to %p in object %p (%s) at offset %zd\n", \
1640 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1645 scan_object_for_specific_ref (char *start, MonoObject *key)
1647 #include "sgen-scan-object.h"
1653 scan_area_for_specific_ref (char *start, char *end, MonoObject *key)
1655 while (start < end) {
1656 if (!*(void**)start) {
1657 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1661 start = scan_object_for_specific_ref (start, key);
1666 scan_pinned_object_for_specific_ref_callback (PinnedChunk *chunk, char *obj, size_t size, MonoObject *key)
1668 scan_object_for_specific_ref (obj, key);
1672 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1676 g_print ("found ref to %p in root record %p\n", key, root);
1679 static MonoObject *check_key = NULL;
1680 static RootRecord *check_root = NULL;
1683 check_root_obj_specific_ref_from_marker (void **obj)
1685 check_root_obj_specific_ref (check_root, check_key, *obj);
1689 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1694 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1695 for (root = roots_hash [root_type][i]; root; root = root->next) {
1696 void **start_root = (void**)root->start_root;
1697 mword desc = root->root_desc;
1701 switch (desc & ROOT_DESC_TYPE_MASK) {
1702 case ROOT_DESC_BITMAP:
1703 desc >>= ROOT_DESC_TYPE_SHIFT;
1706 check_root_obj_specific_ref (root, key, *start_root);
1711 case ROOT_DESC_COMPLEX: {
1712 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1713 int bwords = (*bitmap_data) - 1;
1714 void **start_run = start_root;
1716 while (bwords-- > 0) {
1717 gsize bmap = *bitmap_data++;
1718 void **objptr = start_run;
1721 check_root_obj_specific_ref (root, key, *objptr);
1725 start_run += GC_BITS_PER_WORD;
1729 case ROOT_DESC_USER: {
1730 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1731 marker (start_root, check_root_obj_specific_ref_from_marker);
1734 case ROOT_DESC_RUN_LEN:
1735 g_assert_not_reached ();
1737 g_assert_not_reached ();
1746 mono_gc_scan_for_specific_ref (MonoObject *key)
1748 GCMemSection *section;
1753 for (section = section_list; section; section = section->block.next)
1754 scan_area_for_specific_ref (section->data, section->end_data, key);
1756 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1757 scan_object_for_specific_ref (bigobj->data, key);
1759 scan_pinned_objects ((ScanPinnedObjectCallbackFunc)scan_pinned_object_for_specific_ref_callback, key);
1761 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1762 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1764 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1765 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1766 void **ptr = (void**)root->start_root;
1768 while (ptr < (void**)root->end_root) {
1769 check_root_obj_specific_ref (root, *ptr, key);
1776 //#define BINARY_PROTOCOL
1777 #include "sgen-protocol.c"
1780 need_remove_object_for_domain (char *start, MonoDomain *domain)
1782 if (mono_object_domain (start) == domain) {
1783 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1784 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1791 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1793 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1794 if (vt->klass == mono_defaults.internal_thread_class)
1795 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1796 /* The object could be a proxy for an object in the domain
1798 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1799 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1801 /* The server could already have been zeroed out, so
1802 we need to check for that, too. */
1803 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1804 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1806 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1811 static void __attribute__((noinline))
1812 scan_area_for_domain (MonoDomain *domain, char *start, char *end)
1817 while (start < end) {
1818 if (!*(void**)start) {
1819 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1822 vt = (GCVTable*)LOAD_VTABLE (start);
1823 process_object_for_domain_clearing (start, domain);
1824 remove = need_remove_object_for_domain (start, domain);
1825 if (remove && ((MonoObject*)start)->synchronisation) {
1826 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)start);
1828 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1831 #define SCAN_OBJECT_NOSCAN
1832 #define SCAN_OBJECT_ACTION do { \
1833 if (remove) memset (start, 0, skip_size); \
1835 #include "sgen-scan-object.h"
1839 static MonoDomain *check_domain = NULL;
1842 check_obj_not_in_domain (void **o)
1844 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1848 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1852 check_domain = domain;
1853 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1854 for (root = roots_hash [root_type][i]; root; root = root->next) {
1855 void **start_root = (void**)root->start_root;
1856 mword desc = root->root_desc;
1858 /* The MonoDomain struct is allowed to hold
1859 references to objects in its own domain. */
1860 if (start_root == (void**)domain)
1863 switch (desc & ROOT_DESC_TYPE_MASK) {
1864 case ROOT_DESC_BITMAP:
1865 desc >>= ROOT_DESC_TYPE_SHIFT;
1867 if ((desc & 1) && *start_root)
1868 check_obj_not_in_domain (*start_root);
1873 case ROOT_DESC_COMPLEX: {
1874 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1875 int bwords = (*bitmap_data) - 1;
1876 void **start_run = start_root;
1878 while (bwords-- > 0) {
1879 gsize bmap = *bitmap_data++;
1880 void **objptr = start_run;
1882 if ((bmap & 1) && *objptr)
1883 check_obj_not_in_domain (*objptr);
1887 start_run += GC_BITS_PER_WORD;
1891 case ROOT_DESC_USER: {
1892 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1893 marker (start_root, check_obj_not_in_domain);
1896 case ROOT_DESC_RUN_LEN:
1897 g_assert_not_reached ();
1899 g_assert_not_reached ();
1903 check_domain = NULL;
1907 clear_domain_process_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t size, MonoDomain *domain)
1909 process_object_for_domain_clearing (obj, domain);
1913 clear_domain_free_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t size, MonoDomain *domain)
1915 if (need_remove_object_for_domain (obj, domain))
1916 free_pinned_object (chunk, obj, size);
1920 scan_pinned_object_for_xdomain_refs_callback (PinnedChunk *chunk, char *obj, size_t size, gpointer dummy)
1922 scan_object_for_xdomain_refs (obj);
1926 check_for_xdomain_refs (void)
1928 GCMemSection *section;
1931 for (section = section_list; section; section = section->block.next)
1932 scan_area_for_xdomain_refs (section->data, section->end_data);
1934 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1935 scan_object_for_xdomain_refs (bigobj->data);
1937 scan_pinned_objects (scan_pinned_object_for_xdomain_refs_callback, NULL);
1941 * When appdomains are unloaded we can easily remove objects that have finalizers,
1942 * but all the others could still be present in random places on the heap.
1943 * We need a sweep to get rid of them even though it's going to be costly
1945 * The reason we need to remove them is because we access the vtable and class
1946 * structures to know the object size and the reference bitmap: once the domain is
1947 * unloaded the point to random memory.
1950 mono_gc_clear_domain (MonoDomain * domain)
1952 GCMemSection *section;
1953 LOSObject *bigobj, *prev;
1958 /* Clear all remaining nursery fragments */
1959 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1960 g_assert (nursery_next <= nursery_frag_real_end);
1961 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
1962 for (frag = nursery_fragments; frag; frag = frag->next) {
1963 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1967 if (xdomain_checks && domain != mono_get_root_domain ()) {
1968 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1969 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1970 check_for_xdomain_refs ();
1973 for (section = section_list; section; section = section->block.next) {
1974 scan_area_for_domain (domain, section->data, section->end_data);
1977 /* We need two passes over pinned and large objects because
1978 freeing such an object gives its memory back to the OS (in
1979 the case of large objects) or obliterates its vtable
1980 (pinned objects), but we might need to dereference a
1981 pointer from an object to another object if the first
1982 object is a proxy. */
1983 scan_pinned_objects ((ScanPinnedObjectCallbackFunc)clear_domain_process_pinned_object_callback, domain);
1984 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1985 process_object_for_domain_clearing (bigobj->data, domain);
1988 for (bigobj = los_object_list; bigobj;) {
1989 if (need_remove_object_for_domain (bigobj->data, domain)) {
1990 LOSObject *to_free = bigobj;
1992 prev->next = bigobj->next;
1994 los_object_list = bigobj->next;
1995 bigobj = bigobj->next;
1996 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1998 free_large_object (to_free);
2002 bigobj = bigobj->next;
2004 scan_pinned_objects ((ScanPinnedObjectCallbackFunc)clear_domain_free_pinned_object_callback, domain);
2006 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
2007 null_links_for_domain (domain, i);
2013 * add_to_global_remset:
2015 * The global remset contains locations which point into newspace after
2016 * a minor collection. This can happen if the objects they point to are pinned.
2019 add_to_global_remset (gpointer ptr, gboolean root)
2023 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
2024 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
2027 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
2029 HEAVY_STAT (++stat_global_remsets_added);
2032 * FIXME: If an object remains pinned, we need to add it at every minor collection.
2033 * To avoid uncontrolled growth of the global remset, only add each pointer once.
2035 if (global_remset->store_next + 3 < global_remset->end_set) {
2037 *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
2038 *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
2040 *(global_remset->store_next++) = (mword)ptr;
2044 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
2045 rs->next = global_remset;
2048 *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
2049 *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
2051 *(global_remset->store_next++) = (mword)ptr;
2055 int global_rs_size = 0;
2057 for (rs = global_remset; rs; rs = rs->next) {
2058 global_rs_size += rs->store_next - rs->data;
2060 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
2064 #define MOVED_OBJECTS_NUM 64
2065 static void *moved_objects [MOVED_OBJECTS_NUM];
2066 static int moved_objects_idx = 0;
2068 #include "sgen-gray.c"
2071 * This is how the copying happens from the nursery to the old generation.
2072 * We assume that at this time all the pinned objects have been identified and
2074 * We run scan_object() for each pinned object so that each referenced
2075 * objects if possible are copied. The new gray objects created can have
2076 * scan_object() run on them right away, too.
2077 * Then we run copy_object() for the precisely tracked roots. At this point
2078 * all the roots are either gray or black. We run scan_object() on the gray
2079 * objects until no more gray objects are created.
2080 * At the end of the process we walk again the pinned list and we unmark
2081 * the pinned flag. As we go we also create the list of free space for use
2082 * in the next allocation runs.
2084 * We need to remember objects from the old generation that point to the new one
2085 * (or just addresses?).
2087 * copy_object could be made into a macro once debugged (use inline for now).
2090 static void __attribute__((noinline))
2091 copy_object (void **obj_slot, char *from_space_start, char *from_space_end)
2093 static const void *copy_labels [] = { &&LAB_0, &&LAB_1, &&LAB_2, &&LAB_3, &&LAB_4, &&LAB_5, &&LAB_6, &&LAB_7, &&LAB_8 };
2096 char *obj = *obj_slot;
2100 HEAVY_STAT (++num_copy_object_called);
2102 if (!(obj >= from_space_start && obj < from_space_end)) {
2103 DEBUG (9, fprintf (gc_debug_file, "Not copying %p because it's not in from space (%p-%p)\n",
2104 obj, from_space_start, from_space_end));
2105 HEAVY_STAT (++stat_copy_object_failed_from_space);
2109 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
2112 * obj must belong to one of:
2117 * 4. a non-to-space section of the major heap
2118 * 5. a to-space section of the major heap
2120 * In addition, objects in 1, 2 and 4 might also be pinned.
2121 * Objects in 1 and 4 might be forwarded.
2123 * Before we can copy the object we must make sure that we are
2124 * allowed to, i.e. that the object not pinned, not already
2125 * forwarded and doesn't belong to the LOS, a pinned chunk, or
2126 * a to-space section.
2128 * We are usually called for to-space objects (5) when we have
2129 * two remset entries for the same reference. The first entry
2130 * copies the object and updates the reference and the second
2131 * calls us with the updated reference that points into
2132 * to-space. There might also be other circumstances where we
2133 * get to-space objects.
2136 if ((forwarded = object_is_forwarded (obj))) {
2137 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2138 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
2139 HEAVY_STAT (++stat_copy_object_failed_forwarded);
2140 *obj_slot = forwarded;
2143 if (object_is_pinned (obj)) {
2144 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2145 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
2146 HEAVY_STAT (++stat_copy_object_failed_pinned);
2150 objsize = safe_object_get_size ((MonoObject*)obj);
2151 objsize += ALLOC_ALIGN - 1;
2152 objsize &= ~(ALLOC_ALIGN - 1);
2154 if (ptr_in_nursery (obj))
2158 * At this point we know obj is not pinned, not forwarded and
2159 * belongs to 2, 3, 4, or 5.
2161 * LOS object (2) are simple, at least until we always follow
2162 * the rule: if objsize > MAX_SMALL_OBJ_SIZE, pin the object
2163 * and return it. At the end of major collections, we walk
2164 * the los list and if the object is pinned, it is marked,
2165 * otherwise it can be freed.
2167 * Pinned chunks (3) and major heap sections (4, 5) both
2168 * reside in blocks, which are always aligned, so once we've
2169 * eliminated LOS objects, we can just access the block and
2170 * see whether it's a pinned chunk or a major heap section.
2172 if (G_UNLIKELY (objsize > MAX_SMALL_OBJ_SIZE || obj_is_from_pinned_alloc (obj))) {
2173 DEBUG (9, fprintf (gc_debug_file, " (marked LOS/Pinned %p (%s), size: %zd)\n", obj, safe_name (obj), objsize));
2174 binary_protocol_pin (obj, (gpointer)LOAD_VTABLE (obj), safe_object_get_size ((MonoObject*)obj));
2176 HEAVY_STAT (++stat_copy_object_failed_large_pinned);
2181 * Now we know the object is in a major heap section. All we
2182 * need to do is check whether it's already in to-space (5) or
2185 if (MAJOR_SECTION_FOR_OBJECT (obj)->is_to_space) {
2186 DEBUG (9, g_assert (objsize <= MAX_SMALL_OBJ_SIZE));
2187 DEBUG (9, fprintf (gc_debug_file, " (already copied)\n"));
2188 HEAVY_STAT (++stat_copy_object_failed_to_space);
2193 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %zd)\n", to_space_bumper, ((MonoObject*)obj)->vtable->klass->name, objsize));
2194 binary_protocol_copy (obj, to_space_bumper, ((MonoObject*)obj)->vtable, objsize);
2196 HEAVY_STAT (++num_objects_copied);
2198 /* Make sure we have enough space available */
2199 if (to_space_bumper + objsize > to_space_top) {
2201 DEBUG (8, g_assert (to_space_bumper + objsize <= to_space_top));
2204 if (objsize <= sizeof (gpointer) * 8) {
2205 mword *dest = (mword*)to_space_bumper;
2206 goto *copy_labels [objsize / sizeof (gpointer)];
2208 (dest) [7] = ((mword*)obj) [7];
2210 (dest) [6] = ((mword*)obj) [6];
2212 (dest) [5] = ((mword*)obj) [5];
2214 (dest) [4] = ((mword*)obj) [4];
2216 (dest) [3] = ((mword*)obj) [3];
2218 (dest) [2] = ((mword*)obj) [2];
2220 (dest) [1] = ((mword*)obj) [1];
2222 (dest) [0] = ((mword*)obj) [0];
2230 char* edi = to_space_bumper;
2231 __asm__ __volatile__(
2233 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
2234 : "0" (objsize/4), "1" (edi),"2" (esi)
2239 memcpy (to_space_bumper, obj, objsize);
2242 /* adjust array->bounds */
2243 vt = ((MonoObject*)obj)->vtable;
2244 DEBUG (9, g_assert (vt->gc_descr));
2245 if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
2246 MonoArray *array = (MonoArray*)to_space_bumper;
2247 array->bounds = (MonoArrayBounds*)((char*)to_space_bumper + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
2248 DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %zd, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
2250 /* set the forwarding pointer */
2251 forward_object (obj, to_space_bumper);
2252 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
2253 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2254 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2255 moved_objects_idx = 0;
2257 moved_objects [moved_objects_idx++] = obj;
2258 moved_objects [moved_objects_idx++] = to_space_bumper;
2260 obj = to_space_bumper;
2261 to_space_section->scan_starts [((char*)obj - (char*)to_space_section->data)/SCAN_START_SIZE] = obj;
2262 to_space_bumper += objsize;
2263 DEBUG (9, fprintf (gc_debug_file, "Enqueuing gray object %p (%s)\n", obj, safe_name (obj)));
2264 GRAY_OBJECT_ENQUEUE (obj);
2265 DEBUG (8, g_assert (to_space_bumper <= to_space_top));
2270 * This is a variant of copy_object() that can be used for object locations
2271 * not in the heap. Ideally all the callers should later be changed to call
2272 * the real copy_object() that takes the location of the pointer.
2275 copy_object_no_heap (char *obj, char *from_space_start, char *from_space_end)
2277 void *obj_ptr = (void*)obj;
2278 copy_object (&obj_ptr, from_space_start, from_space_end);
2283 #define HANDLE_PTR(ptr,obj) do { \
2284 void *__old = *(ptr); \
2287 copy_object ((ptr), from_start, from_end); \
2289 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2290 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2291 add_to_global_remset ((ptr), FALSE); \
2296 * Scan the object pointed to by @start for references to
2297 * other objects between @from_start and @from_end and copy
2298 * them to the gray_objects area.
2299 * Returns a pointer to the end of the object.
2302 scan_object (char *start, char* from_start, char* from_end)
2304 #include "sgen-scan-object.h"
2312 * Scan objects in the gray stack until the stack is empty. This should be called
2313 * frequently after each object is copied, to achieve better locality and cache
2317 drain_gray_stack (char *start_addr, char *end_addr)
2322 GRAY_OBJECT_DEQUEUE (obj);
2325 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2326 scan_object (obj, start_addr, end_addr);
2333 * Scan the valuetype pointed to by START, described by DESC for references to
2334 * other objects between @from_start and @from_end and copy them to the gray_objects area.
2335 * Returns a pointer to the end of the object.
2338 scan_vtype (char *start, mword desc, char* from_start, char* from_end)
2342 /* The descriptors include info about the MonoObject header as well */
2343 start -= sizeof (MonoObject);
2345 switch (desc & 0x7) {
2346 case DESC_TYPE_RUN_LENGTH:
2347 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
2348 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
2349 g_assert (skip_size);
2350 return start + skip_size;
2351 case DESC_TYPE_SMALL_BITMAP:
2352 OBJ_BITMAP_FOREACH_PTR (desc,start);
2353 OBJ_BITMAP_SIZE (skip_size, desc, start);
2354 return start + skip_size;
2355 case DESC_TYPE_LARGE_BITMAP:
2356 case DESC_TYPE_COMPLEX:
2358 g_assert_not_reached ();
2361 // The other descriptors can't happen with vtypes
2362 g_assert_not_reached ();
2368 #include "sgen-pinning-stats.c"
2371 * Addresses from start to end are already sorted. This function finds
2372 * the object header for each address and pins the object. The
2373 * addresses must be inside the passed section. The (start of the)
2374 * address array is overwritten with the addresses of the actually
2375 * pinned objects. Return the number of pinned objects.
2378 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
2383 void *last_obj = NULL;
2384 size_t last_obj_size = 0;
2387 void **definitely_pinned = start;
2388 while (start < end) {
2390 /* the range check should be reduntant */
2391 if (addr != last && addr >= start_nursery && addr < end_nursery) {
2392 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
2393 /* multiple pointers to the same object */
2394 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
2398 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
2399 g_assert (idx < section->num_scan_start);
2400 search_start = (void*)section->scan_starts [idx];
2401 if (!search_start || search_start > addr) {
2404 search_start = section->scan_starts [idx];
2405 if (search_start && search_start <= addr)
2408 if (!search_start || search_start > addr)
2409 search_start = start_nursery;
2411 if (search_start < last_obj)
2412 search_start = (char*)last_obj + last_obj_size;
2413 /* now addr should be in an object a short distance from search_start
2414 * Note that search_start must point to zeroed mem or point to an object.
2417 if (!*(void**)search_start) {
2418 mword p = (mword)search_start;
2419 p += sizeof (gpointer);
2420 p += ALLOC_ALIGN - 1;
2421 p &= ~(ALLOC_ALIGN - 1);
2422 search_start = (void*)p;
2425 last_obj = search_start;
2426 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
2427 last_obj_size += ALLOC_ALIGN - 1;
2428 last_obj_size &= ~(ALLOC_ALIGN - 1);
2429 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
2430 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
2431 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));
2432 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
2433 pin_object (search_start);
2435 pin_stats_register_object (search_start, last_obj_size);
2436 definitely_pinned [count] = search_start;
2440 /* skip to the next object */
2441 search_start = (void*)((char*)search_start + last_obj_size);
2442 } while (search_start <= addr);
2443 /* we either pinned the correct object or we ignored the addr because
2444 * it points to unused zeroed memory.
2450 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
2454 static void** pin_queue;
2455 static int pin_queue_size = 0;
2456 static int next_pin_slot = 0;
2461 gap = (gap * 10) / 13;
2462 if (gap == 9 || gap == 10)
2471 compare_addr (const void *a, const void *b)
2473 return *(const void **)a - *(const void **)b;
2477 /* sort the addresses in array in increasing order */
2479 sort_addresses (void **array, int size)
2482 * qsort is slower as predicted.
2483 * qsort (array, size, sizeof (gpointer), compare_addr);
2490 gap = new_gap (gap);
2493 for (i = 0; i < end; i++) {
2495 if (array [i] > array [j]) {
2496 void* val = array [i];
2497 array [i] = array [j];
2502 if (gap == 1 && !swapped)
2507 static G_GNUC_UNUSED void
2508 print_nursery_gaps (void* start_nursery, void *end_nursery)
2511 gpointer first = start_nursery;
2513 for (i = 0; i < next_pin_slot; ++i) {
2514 next = pin_queue [i];
2515 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2519 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2522 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
2524 optimize_pin_queue (int start_slot)
2526 void **start, **cur, **end;
2527 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
2528 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
2529 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
2530 if ((next_pin_slot - start_slot) > 1)
2531 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
2532 start = cur = pin_queue + start_slot;
2533 end = pin_queue + next_pin_slot;
2536 while (*start == *cur && cur < end)
2540 next_pin_slot = start - pin_queue;
2541 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2542 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2547 optimized_pin_queue_search (void *addr)
2549 int first = 0, last = next_pin_slot;
2550 while (first < last) {
2551 int middle = first + ((last - first) >> 1);
2552 if (addr <= pin_queue [middle])
2557 g_assert (first == last);
2562 find_optimized_pin_queue_area (void *start, void *end, int *first, int *last)
2564 *first = optimized_pin_queue_search (start);
2565 *last = optimized_pin_queue_search (end);
2569 realloc_pin_queue (void)
2571 int new_size = pin_queue_size? pin_queue_size + pin_queue_size/2: 1024;
2572 void **new_pin = get_internal_mem (sizeof (void*) * new_size, INTERNAL_MEM_PIN_QUEUE);
2573 memcpy (new_pin, pin_queue, sizeof (void*) * next_pin_slot);
2574 free_internal_mem (pin_queue, INTERNAL_MEM_PIN_QUEUE);
2575 pin_queue = new_pin;
2576 pin_queue_size = new_size;
2577 DEBUG (4, fprintf (gc_debug_file, "Reallocated pin queue to size: %d\n", new_size));
2580 #include "sgen-pinning.c"
2583 * Scan the memory between start and end and queue values which could be pointers
2584 * to the area between start_nursery and end_nursery for later consideration.
2585 * Typically used for thread stacks.
2588 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2591 while (start < end) {
2592 if (*start >= start_nursery && *start < end_nursery) {
2594 * *start can point to the middle of an object
2595 * note: should we handle pointing at the end of an object?
2596 * pinning in C# code disallows pointing at the end of an object
2597 * but there is some small chance that an optimizing C compiler
2598 * may keep the only reference to an object by pointing
2599 * at the end of it. We ignore this small chance for now.
2600 * Pointers to the end of an object are indistinguishable
2601 * from pointers to the start of the next object in memory
2602 * so if we allow that we'd need to pin two objects...
2603 * We queue the pointer in an array, the
2604 * array will then be sorted and uniqued. This way
2605 * we can coalesce several pinning pointers and it should
2606 * be faster since we'd do a memory scan with increasing
2607 * addresses. Note: we can align the address to the allocation
2608 * alignment, so the unique process is more effective.
2610 mword addr = (mword)*start;
2611 addr &= ~(ALLOC_ALIGN - 1);
2612 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2613 pin_stage_ptr ((void*)addr);
2615 pin_stats_register_address ((char*)addr, pin_type);
2616 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
2621 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2625 * If generation is 0, just mark objects in the nursery, the others we don't care,
2626 * since they are not going to move anyway.
2627 * There are different areas that are scanned for pinned pointers:
2628 * *) the thread stacks (when jit support is ready only the unmanaged frames)
2629 * *) the pinned handle table
2630 * *) the pinned roots
2632 * Note: when we'll use a write barrier for old to new gen references, we need to
2633 * keep track of old gen objects that point to pinned new gen objects because in that
2634 * case the referenced object will be moved maybe at the next collection, but there
2635 * is no write in the old generation area where the pinned object is referenced
2636 * and we may not consider it as reachable.
2638 static G_GNUC_UNUSED void
2639 mark_pinned_objects (int generation)
2644 * Debugging function: find in the conservative roots where @obj is being pinned.
2646 static G_GNUC_UNUSED void
2647 find_pinning_reference (char *obj, size_t size)
2651 char *endobj = obj + size;
2652 for (i = 0; i < roots_hash_size [0]; ++i) {
2653 for (root = roots_hash [0][i]; root; root = root->next) {
2654 /* if desc is non-null it has precise info */
2655 if (!root->root_desc) {
2656 char ** start = (char**)root->start_root;
2657 while (start < (char**)root->end_root) {
2658 if (*start >= obj && *start < endobj) {
2659 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));
2666 find_pinning_ref_from_thread (obj, size);
2670 * The first thing we do in a collection is to identify pinned objects.
2671 * This function considers all the areas of memory that need to be
2672 * conservatively scanned.
2675 pin_from_roots (void *start_nursery, void *end_nursery)
2679 DEBUG (2, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d/%d entries)\n", (int)roots_size, num_roots_entries [ROOT_TYPE_NORMAL], num_roots_entries [ROOT_TYPE_PINNED]));
2680 /* objects pinned from the API are inside these roots */
2681 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2682 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2683 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2684 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2687 /* now deal with the thread stacks
2688 * in the future we should be able to conservatively scan only:
2689 * *) the cpu registers
2690 * *) the unmanaged stack frames
2691 * *) the _last_ managed stack frame
2692 * *) pointers slots in managed frames
2694 scan_thread_data (start_nursery, end_nursery, FALSE);
2696 evacuate_pin_staging_area ();
2699 /* Copy function called from user defined mark functions */
2700 static char *user_copy_n_start;
2701 static char *user_copy_n_end;
2704 user_copy (void **addr)
2706 copy_object (addr, user_copy_n_start, user_copy_n_end);
2710 * The memory area from start_root to end_root contains pointers to objects.
2711 * Their position is precisely described by @desc (this means that the pointer
2712 * can be either NULL or the pointer to the start of an object).
2713 * This functions copies them to to_space updates them.
2716 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
2718 switch (desc & ROOT_DESC_TYPE_MASK) {
2719 case ROOT_DESC_BITMAP:
2720 desc >>= ROOT_DESC_TYPE_SHIFT;
2722 if ((desc & 1) && *start_root) {
2723 copy_object (start_root, n_start, n_end);
2724 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2725 drain_gray_stack (n_start, n_end);
2731 case ROOT_DESC_COMPLEX: {
2732 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2733 int bwords = (*bitmap_data) - 1;
2734 void **start_run = start_root;
2736 while (bwords-- > 0) {
2737 gsize bmap = *bitmap_data++;
2738 void **objptr = start_run;
2740 if ((bmap & 1) && *objptr) {
2741 copy_object (objptr, n_start, n_end);
2742 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2743 drain_gray_stack (n_start, n_end);
2748 start_run += GC_BITS_PER_WORD;
2752 case ROOT_DESC_USER: {
2753 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2755 user_copy_n_start = n_start;
2756 user_copy_n_end = n_end;
2757 marker (start_root, user_copy);
2760 case ROOT_DESC_RUN_LEN:
2761 g_assert_not_reached ();
2763 g_assert_not_reached ();
2768 alloc_fragment (void)
2770 Fragment *frag = fragment_freelist;
2772 fragment_freelist = frag->next;
2776 frag = get_internal_mem (sizeof (Fragment), INTERNAL_MEM_FRAGMENT);
2781 /* size must be a power of 2 */
2783 get_os_memory_aligned (mword size, gboolean activate)
2785 /* Allocate twice the memory to be able to put the block on an aligned address */
2786 char *mem = get_os_memory (size * 2, activate);
2791 aligned = (char*)((mword)(mem + (size - 1)) & ~(size - 1));
2792 g_assert (aligned >= mem && aligned + size <= mem + size * 2 && !((mword)aligned & (size - 1)));
2795 free_os_memory (mem, aligned - mem);
2796 if (aligned + size < mem + size * 2)
2797 free_os_memory (aligned + size, (mem + size * 2) - (aligned + size));
2803 * Allocate and setup the data structures needed to be able to allocate objects
2804 * in the nursery. The nursery is stored in nursery_section.
2807 alloc_nursery (void)
2809 GCMemSection *section;
2815 if (nursery_section)
2817 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %zd\n", nursery_size));
2818 /* later we will alloc a larger area for the nursery but only activate
2819 * what we need. The rest will be used as expansion if we have too many pinned
2820 * objects in the existing nursery.
2822 /* FIXME: handle OOM */
2823 section = get_internal_mem (SIZEOF_GC_MEM_SECTION, INTERNAL_MEM_SECTION);
2825 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2826 alloc_size = nursery_size;
2827 #ifdef ALIGN_NURSERY
2828 data = get_os_memory_aligned (alloc_size, TRUE);
2830 data = get_os_memory (alloc_size, TRUE);
2832 nursery_start = data;
2833 nursery_real_end = nursery_start + nursery_size;
2834 UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
2835 nursery_next = nursery_start;
2836 total_alloc += alloc_size;
2837 DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %zd, total: %zd\n", data, data + alloc_size, nursery_size, total_alloc));
2838 section->data = section->next_data = data;
2839 section->size = alloc_size;
2840 section->end_data = nursery_real_end;
2841 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2842 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2843 section->num_scan_start = scan_starts;
2844 section->block.role = MEMORY_ROLE_GEN0;
2846 /* add to the section list */
2847 section->block.next = section_list;
2848 section_list = section;
2850 nursery_section = section;
2852 /* Setup the single first large fragment */
2853 frag = alloc_fragment ();
2854 frag->fragment_start = nursery_start;
2855 frag->fragment_limit = nursery_start;
2856 frag->fragment_end = nursery_real_end;
2857 nursery_frag_real_end = nursery_real_end;
2858 /* FIXME: frag here is lost */
2862 scan_finalizer_entries (FinalizeEntry *list, char *start, char *end) {
2865 for (fin = list; fin; fin = fin->next) {
2868 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2869 copy_object (&fin->object, start, end);
2874 * Update roots in the old generation. Since we currently don't have the
2875 * info from the write barriers, we just scan all the objects.
2877 static G_GNUC_UNUSED void
2878 scan_old_generation (char *start, char* end)
2880 GCMemSection *section;
2881 LOSObject *big_object;
2884 for (section = section_list; section; section = section->block.next) {
2885 if (section == nursery_section)
2887 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)));
2888 /* we have to deal with zeroed holes in old generation (truncated strings ...) */
2890 while (p < section->next_data) {
2895 DEBUG (8, fprintf (gc_debug_file, "Precise old object scan of %p (%s)\n", p, safe_name (p)));
2896 p = scan_object (p, start, end);
2899 /* scan the old object space, too */
2900 for (big_object = los_object_list; big_object; big_object = big_object->next) {
2901 DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %zd\n", big_object->data, safe_name (big_object->data), big_object->size));
2902 scan_object (big_object->data, start, end);
2904 /* scan the list of objects ready for finalization */
2905 scan_finalizer_entries (fin_ready_list, start, end);
2906 scan_finalizer_entries (critical_fin_list, start, end);
2909 static mword fragment_total = 0;
2911 * We found a fragment of free memory in the nursery: memzero it and if
2912 * it is big enough, add it to the list of fragments that can be used for
2916 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2919 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2920 binary_protocol_empty (frag_start, frag_size);
2921 /* memsetting just the first chunk start is bound to provide better cache locality */
2922 if (nursery_clear_policy == CLEAR_AT_GC)
2923 memset (frag_start, 0, frag_size);
2924 /* Not worth dealing with smaller fragments: need to tune */
2925 if (frag_size >= FRAGMENT_MIN_SIZE) {
2926 fragment = alloc_fragment ();
2927 fragment->fragment_start = frag_start;
2928 fragment->fragment_limit = frag_start;
2929 fragment->fragment_end = frag_end;
2930 fragment->next = nursery_fragments;
2931 nursery_fragments = fragment;
2932 fragment_total += frag_size;
2934 /* Clear unused fragments, pinning depends on this */
2935 memset (frag_start, 0, frag_size);
2940 scan_needed_big_objects (char *start_addr, char *end_addr)
2942 LOSObject *big_object;
2944 for (big_object = los_object_list; big_object; big_object = big_object->next) {
2945 if (!big_object->scanned && object_is_pinned (big_object->data)) {
2946 DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %zd\n", big_object->data, safe_name (big_object->data), big_object->size));
2947 scan_object (big_object->data, start_addr, end_addr);
2948 drain_gray_stack (start_addr, end_addr);
2949 big_object->scanned = TRUE;
2957 generation_name (int generation)
2959 switch (generation) {
2960 case GENERATION_NURSERY: return "nursery";
2961 case GENERATION_OLD: return "old";
2962 default: g_assert_not_reached ();
2966 static DisappearingLinkHashTable*
2967 get_dislink_hash_table (int generation)
2969 switch (generation) {
2970 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2971 case GENERATION_OLD: return &major_disappearing_link_hash;
2972 default: g_assert_not_reached ();
2976 static FinalizeEntryHashTable*
2977 get_finalize_entry_hash_table (int generation)
2979 switch (generation) {
2980 case GENERATION_NURSERY: return &minor_finalizable_hash;
2981 case GENERATION_OLD: return &major_finalizable_hash;
2982 default: g_assert_not_reached ();
2987 new_to_space_section (void)
2989 /* FIXME: if the current to_space_section is empty, we don't
2990 have to allocate a new one */
2992 to_space_section = alloc_major_section ();
2993 to_space_bumper = to_space_section->next_data;
2994 to_space_top = to_space_section->end_data;
2998 to_space_set_next_data (void)
3000 g_assert (to_space_bumper >= to_space_section->next_data && to_space_bumper <= to_space_section->end_data);
3001 to_space_section->next_data = to_space_bumper;
3005 to_space_expand (void)
3007 if (to_space_section) {
3008 g_assert (to_space_top == to_space_section->end_data);
3009 to_space_set_next_data ();
3012 new_to_space_section ();
3016 unset_to_space (void)
3018 /* between collections the to_space_bumper is invalidated
3019 because degraded allocations might occur, so we set it to
3020 NULL, just to make it explicit */
3021 to_space_bumper = NULL;
3023 /* don't unset to_space_section if we implement the FIXME in
3024 new_to_space_section */
3025 to_space_section = NULL;
3029 object_is_in_to_space (char *obj)
3034 if (ptr_in_nursery (obj))
3037 objsize = safe_object_get_size ((MonoObject*)obj);
3038 objsize += ALLOC_ALIGN - 1;
3039 objsize &= ~(ALLOC_ALIGN - 1);
3042 if (objsize > MAX_SMALL_OBJ_SIZE)
3046 if (obj_is_from_pinned_alloc (obj))
3049 /* now we know it's in a major heap section */
3050 return MAJOR_SECTION_FOR_OBJECT (obj)->is_to_space;
3054 finish_gray_stack (char *start_addr, char *end_addr, int generation)
3058 int fin_ready, bigo_scanned_num;
3061 * We copied all the reachable objects. Now it's the time to copy
3062 * the objects that were not referenced by the roots, but by the copied objects.
3063 * we built a stack of objects pointed to by gray_start: they are
3064 * additional roots and we may add more items as we go.
3065 * We loop until gray_start == gray_objects which means no more objects have
3066 * been added. Note this is iterative: no recursion is involved.
3067 * We need to walk the LO list as well in search of marked big objects
3068 * (use a flag since this is needed only on major collections). We need to loop
3069 * here as well, so keep a counter of marked LO (increasing it in copy_object).
3070 * To achieve better cache locality and cache usage, we drain the gray stack
3071 * frequently, after each object is copied, and just finish the work here.
3073 drain_gray_stack (start_addr, end_addr);
3075 //scan_old_generation (start_addr, end_addr);
3076 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
3077 /* walk the finalization queue and move also the objects that need to be
3078 * finalized: use the finalized objects as new roots so the objects they depend
3079 * on are also not reclaimed. As with the roots above, only objects in the nursery
3080 * are marked/copied.
3081 * We need a loop here, since objects ready for finalizers may reference other objects
3082 * that are fin-ready. Speedup with a flag?
3085 fin_ready = num_ready_finalizers;
3086 finalize_in_range (start_addr, end_addr, generation);
3087 if (generation == GENERATION_OLD)
3088 finalize_in_range (nursery_start, nursery_real_end, GENERATION_NURSERY);
3089 bigo_scanned_num = scan_needed_big_objects (start_addr, end_addr);
3091 /* drain the new stack that might have been created */
3092 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
3093 drain_gray_stack (start_addr, end_addr);
3094 } while (fin_ready != num_ready_finalizers || bigo_scanned_num);
3096 DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan for %s generation: %d usecs\n", generation_name (generation), TV_ELAPSED (atv, btv)));
3099 * handle disappearing links
3100 * Note we do this after checking the finalization queue because if an object
3101 * survives (at least long enough to be finalized) we don't clear the link.
3102 * This also deals with a possible issue with the monitor reclamation: with the Boehm
3103 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
3106 g_assert (gray_object_queue_is_empty ());
3108 null_link_in_range (start_addr, end_addr, generation);
3109 if (generation == GENERATION_OLD)
3110 null_link_in_range (start_addr, end_addr, GENERATION_NURSERY);
3111 if (gray_object_queue_is_empty ())
3113 drain_gray_stack (start_addr, end_addr);
3116 g_assert (gray_object_queue_is_empty ());
3117 /* DEBUG (2, fprintf (gc_debug_file, "Copied from %s to old space: %d bytes (%p-%p)\n", generation_name (generation), (int)(to_space_bumper - to_space), to_space, to_space_bumper)); */
3118 to_space_set_next_data ();
3122 check_scan_starts (void)
3124 GCMemSection *section;
3126 if (!do_scan_starts_check)
3128 for (section = section_list; section; section = section->block.next) {
3129 for (i = 0; i < section->num_scan_start; ++i) {
3130 if (section->scan_starts [i]) {
3131 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
3132 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
3138 static int last_num_pinned = 0;
3141 build_nursery_fragments (int start_pin, int end_pin)
3143 char *frag_start, *frag_end;
3147 while (nursery_fragments) {
3148 Fragment *next = nursery_fragments->next;
3149 nursery_fragments->next = fragment_freelist;
3150 fragment_freelist = nursery_fragments;
3151 nursery_fragments = next;
3153 frag_start = nursery_start;
3155 /* clear scan starts */
3156 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
3157 for (i = start_pin; i < end_pin; ++i) {
3158 frag_end = pin_queue [i];
3159 /* remove the pin bit from pinned objects */
3160 unpin_object (frag_end);
3161 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
3162 frag_size = frag_end - frag_start;
3164 add_nursery_frag (frag_size, frag_start, frag_end);
3165 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
3166 frag_size += ALLOC_ALIGN - 1;
3167 frag_size &= ~(ALLOC_ALIGN - 1);
3168 frag_start = (char*)pin_queue [i] + frag_size;
3170 nursery_last_pinned_end = frag_start;
3171 frag_end = nursery_real_end;
3172 frag_size = frag_end - frag_start;
3174 add_nursery_frag (frag_size, frag_start, frag_end);
3175 if (!nursery_fragments) {
3176 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
3177 for (i = start_pin; i < end_pin; ++i) {
3178 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])));
3183 nursery_next = nursery_frag_real_end = NULL;
3185 /* Clear TLABs for all threads */
3189 /* FIXME: later reduce code duplication here with the above
3190 * We don't keep track of section fragments for non-nursery sections yet, so
3194 build_section_fragments (GCMemSection *section)
3197 char *frag_start, *frag_end;
3200 /* clear scan starts */
3201 memset (section->scan_starts, 0, section->num_scan_start * sizeof (gpointer));
3202 frag_start = section->data;
3203 section->next_data = section->data;
3204 for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
3205 frag_end = pin_queue [i];
3206 /* remove the pin bit from pinned objects */
3207 unpin_object (frag_end);
3208 if (frag_end >= section->data + section->size) {
3209 frag_end = section->data + section->size;
3211 section->scan_starts [((char*)frag_end - (char*)section->data)/SCAN_START_SIZE] = frag_end;
3213 frag_size = frag_end - frag_start;
3215 binary_protocol_empty (frag_start, frag_size);
3216 memset (frag_start, 0, frag_size);
3218 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
3219 frag_size += ALLOC_ALIGN - 1;
3220 frag_size &= ~(ALLOC_ALIGN - 1);
3221 frag_start = (char*)pin_queue [i] + frag_size;
3222 section->next_data = MAX (section->next_data, frag_start);
3224 frag_end = section->end_data;
3225 frag_size = frag_end - frag_start;
3227 binary_protocol_empty (frag_start, frag_size);
3228 memset (frag_start, 0, frag_size);
3233 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type)
3237 for (i = 0; i < roots_hash_size [root_type]; ++i) {
3238 for (root = roots_hash [root_type][i]; root; root = root->next) {
3239 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
3240 precisely_scan_objects_from ((void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
3246 dump_occupied (char *start, char *end, char *section_start)
3248 fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
3252 dump_section (GCMemSection *section, const char *type)
3254 char *start = section->data;
3255 char *end = section->data + section->size;
3256 char *occ_start = NULL;
3258 char *old_start = NULL; /* just for debugging */
3260 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", type, section->size);
3262 while (start < end) {
3266 if (!*(void**)start) {
3268 dump_occupied (occ_start, start, section->data);
3271 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
3274 g_assert (start < section->next_data);
3279 vt = (GCVTable*)LOAD_VTABLE (start);
3282 size = safe_object_get_size ((MonoObject*) start);
3283 size += ALLOC_ALIGN - 1;
3284 size &= ~(ALLOC_ALIGN - 1);
3287 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
3288 start - section->data,
3289 vt->klass->name_space, vt->klass->name,
3297 dump_occupied (occ_start, start, section->data);
3299 fprintf (heap_dump_file, "</section>\n");
3303 dump_object (MonoObject *obj, gboolean dump_location)
3305 static char class_name [1024];
3307 MonoClass *class = mono_object_class (obj);
3311 * Python's XML parser is too stupid to parse angle brackets
3312 * in strings, so we just ignore them;
3315 while (class->name [i] && j < sizeof (class_name) - 1) {
3316 if (!strchr ("<>\"", class->name [i]))
3317 class_name [j++] = class->name [i];
3320 g_assert (j < sizeof (class_name));
3323 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
3324 class->name_space, class_name,
3325 safe_object_get_size (obj));
3326 if (dump_location) {
3327 const char *location;
3328 if (ptr_in_nursery (obj))
3329 location = "nursery";
3330 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
3334 fprintf (heap_dump_file, " location=\"%s\"", location);
3336 fprintf (heap_dump_file, "/>\n");
3340 dump_heap (const char *type, int num, const char *reason)
3342 static char const *internal_mem_names [] = { "pin-queue", "fragment", "section", "scan-starts",
3343 "fin-table", "finalize-entry", "dislink-table",
3344 "dislink", "roots-table", "root-record", "statistics",
3345 "remset", "gray-queue", "store-remset" };
3347 GCMemSection *section;
3352 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
3354 fprintf (heap_dump_file, " reason=\"%s\"", reason);
3355 fprintf (heap_dump_file, ">\n");
3356 fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%ld\"/>\n", pinned_chunk_bytes_alloced);
3357 fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%ld\"/>\n", large_internal_bytes_alloced);
3358 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
3359 for (i = 0; i < INTERNAL_MEM_MAX; ++i)
3360 fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n", internal_mem_names [i], small_internal_mem_bytes [i]);
3361 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
3362 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
3363 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
3365 fprintf (heap_dump_file, "<pinned-objects>\n");
3366 for (list = pinned_objects; list; list = list->next)
3367 dump_object (list->obj, TRUE);
3368 fprintf (heap_dump_file, "</pinned-objects>\n");
3370 dump_section (nursery_section, "nursery");
3372 for (section = section_list; section; section = section->block.next) {
3373 if (section != nursery_section)
3374 dump_section (section, "old");
3377 fprintf (heap_dump_file, "<los>\n");
3378 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
3379 dump_object ((MonoObject*)bigobj->data, FALSE);
3380 fprintf (heap_dump_file, "</los>\n");
3382 fprintf (heap_dump_file, "</collection>\n");
3388 static gboolean inited = FALSE;
3390 #ifdef HEAVY_STATISTICS
3391 num_copy_object_called = 0;
3392 num_objects_copied = 0;
3398 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
3399 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
3400 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
3401 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
3402 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
3403 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
3404 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3405 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
3407 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
3408 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
3409 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
3410 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
3411 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
3412 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
3413 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
3414 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
3415 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3416 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
3417 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
3419 #ifdef HEAVY_STATISTICS
3420 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
3421 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
3422 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
3423 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
3424 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
3425 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
3426 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
3427 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
3429 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
3430 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
3431 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
3432 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
3433 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
3434 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
3435 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
3436 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
3437 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
3439 mono_counters_register ("# copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_from_space);
3440 mono_counters_register ("# copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_forwarded);
3441 mono_counters_register ("# copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_pinned);
3442 mono_counters_register ("# copy_object() failed large or pinned chunk", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_large_pinned);
3443 mono_counters_register ("# copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_to_space);
3445 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
3446 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
3447 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
3448 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
3449 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
3450 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
3457 commit_stats (int generation)
3459 #ifdef HEAVY_STATISTICS
3460 if (generation == GENERATION_NURSERY) {
3461 stat_copy_object_called_nursery += num_copy_object_called;
3462 stat_objects_copied_nursery += num_objects_copied;
3464 g_assert (generation == GENERATION_OLD);
3465 stat_copy_object_called_major += num_copy_object_called;
3466 stat_objects_copied_major += num_objects_copied;
3472 * Collect objects in the nursery. Returns whether to trigger a major
3476 collect_nursery (size_t requested_size)
3478 size_t max_garbage_amount;
3480 char *orig_nursery_next;
3482 GCMemSection *section;
3483 int old_num_major_sections = num_major_sections;
3484 int sections_alloced;
3485 TV_DECLARE (all_atv);
3486 TV_DECLARE (all_btv);
3491 binary_protocol_collection (GENERATION_NURSERY);
3492 check_scan_starts ();
3495 orig_nursery_next = nursery_next;
3496 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3497 /* FIXME: optimize later to use the higher address where an object can be present */
3498 nursery_next = MAX (nursery_next, nursery_real_end);
3500 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)));
3501 max_garbage_amount = nursery_next - nursery_start;
3502 g_assert (nursery_section->size >= max_garbage_amount);
3504 /* world must be stopped already */
3505 TV_GETTIME (all_atv);
3508 /* Clear all remaining nursery fragments, pinning depends on this */
3509 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3510 g_assert (orig_nursery_next <= nursery_frag_real_end);
3511 memset (orig_nursery_next, 0, nursery_frag_real_end - orig_nursery_next);
3512 for (frag = nursery_fragments; frag; frag = frag->next) {
3513 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
3518 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3521 check_for_xdomain_refs ();
3523 nursery_section->next_data = nursery_next;
3525 if (!to_space_section) {
3526 new_to_space_section ();
3528 /* we might have done degraded allocation since the
3530 g_assert (to_space_bumper <= to_space_section->next_data);
3531 to_space_bumper = to_space_section->next_data;
3533 to_space_section->is_to_space = TRUE;
3535 gray_object_queue_init ();
3538 mono_stats.minor_gc_count ++;
3539 /* pin from pinned handles */
3541 pin_from_roots (nursery_start, nursery_next);
3542 /* identify pinned objects */
3543 optimize_pin_queue (0);
3544 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
3546 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3547 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3548 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3550 if (consistency_check_at_minor_collection)
3551 check_consistency ();
3554 * walk all the roots and copy the young objects to the old generation,
3555 * starting from to_space
3558 scan_from_remsets (nursery_start, nursery_next);
3559 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3561 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3562 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3564 /* the pinned objects are roots */
3565 for (i = 0; i < next_pin_slot; ++i) {
3566 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
3567 scan_object (pin_queue [i], nursery_start, nursery_next);
3570 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3571 /* registered roots, this includes static fields */
3572 scan_from_registered_roots (nursery_start, nursery_next, ROOT_TYPE_NORMAL);
3573 scan_from_registered_roots (nursery_start, nursery_next, ROOT_TYPE_WBARRIER);
3575 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3577 scan_thread_data (nursery_start, nursery_next, TRUE);
3579 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3582 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY);
3584 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3586 /* walk the pin_queue, build up the fragment list of free memory, unmark
3587 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3590 build_nursery_fragments (0, next_pin_slot);
3592 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3593 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %zd bytes available\n", TV_ELAPSED (atv, btv), fragment_total));
3595 for (section = section_list; section; section = section->block.next) {
3596 if (section->is_to_space)
3597 section->is_to_space = FALSE;
3600 TV_GETTIME (all_btv);
3601 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3604 dump_heap ("minor", num_minor_gcs - 1, NULL);
3606 /* prepare the pin queue for the next collection */
3607 last_num_pinned = next_pin_slot;
3609 if (fin_ready_list || critical_fin_list) {
3610 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3611 mono_gc_finalize_notify ();
3615 g_assert (gray_object_queue_is_empty ());
3617 commit_stats (GENERATION_NURSERY);
3619 check_scan_starts ();
3621 sections_alloced = num_major_sections - old_num_major_sections;
3622 minor_collection_sections_alloced += sections_alloced;
3624 return minor_collection_sections_alloced > minor_collection_section_allowance;
3628 major_collection (const char *reason)
3630 GCMemSection *section, *prev_section;
3631 LOSObject *bigobj, *prevbo;
3635 TV_DECLARE (all_atv);
3636 TV_DECLARE (all_btv);
3639 /* FIXME: only use these values for the precise scan
3640 * note that to_space pointers should be excluded anyway...
3642 char *heap_start = NULL;
3643 char *heap_end = (char*)-1;
3644 size_t copy_space_required = 0;
3645 int old_num_major_sections = num_major_sections;
3646 int num_major_sections_saved, save_target, allowance_target;
3649 binary_protocol_collection (GENERATION_OLD);
3650 check_scan_starts ();
3653 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3655 mono_stats.major_gc_count ++;
3657 /* world must be stopped already */
3658 TV_GETTIME (all_atv);
3661 /* Clear all remaining nursery fragments, pinning depends on this */
3662 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3663 g_assert (nursery_next <= nursery_frag_real_end);
3664 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3665 for (frag = nursery_fragments; frag; frag = frag->next) {
3666 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
3671 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3674 check_for_xdomain_refs ();
3676 if (g_getenv ("MONO_GC_NO_MAJOR")) {
3677 collect_nursery (0);
3680 nursery_section->next_data = nursery_real_end;
3681 /* we should also coalesce scanning from sections close to each other
3682 * and deal with pointers outside of the sections later.
3684 /* The remsets are not useful for a major collection */
3689 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3690 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3691 optimize_pin_queue (0);
3694 * pin_queue now contains all candidate pointers, sorted and
3695 * uniqued. We must do two passes now to figure out which
3696 * objects are pinned.
3698 * The first is to find within the pin_queue the area for each
3699 * section. This requires that the pin_queue be sorted. We
3700 * also process the LOS objects and pinned chunks here.
3702 * The second, destructive, pass is to reduce the section
3703 * areas to pointers to the actually pinned objects.
3705 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3706 /* first pass for the sections */
3707 for (section = section_list; section; section = section->block.next) {
3709 DEBUG (6, fprintf (gc_debug_file, "Pinning from section %p (%p-%p)\n", section, section->data, section->end_data));
3710 find_optimized_pin_queue_area (section->data, section->end_data, &start, &end);
3711 DEBUG (6, fprintf (gc_debug_file, "Found %d pinning addresses in section %p (%d-%d)\n",
3712 end - start, section, start, end));
3713 section->pin_queue_start = start;
3714 section->pin_queue_end = end;
3716 /* identify possible pointers to the insize of large objects */
3717 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3718 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3720 find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &start, &end);
3722 pin_object (bigobj->data);
3724 pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3725 DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
3728 /* look for pinned addresses for pinned-alloc objects */
3729 DEBUG (6, fprintf (gc_debug_file, "Pinning from pinned-alloc objects\n"));
3730 for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
3732 find_optimized_pin_queue_area (chunk->start_data, (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE, &start, &end);
3734 mark_pinned_from_addresses (chunk, pin_queue + start, pin_queue + end);
3736 /* second pass for the sections */
3737 for (section = section_list; section; section = section->block.next) {
3738 int start = section->pin_queue_start;
3739 int end = section->pin_queue_end;
3742 reduced_to = pin_objects_from_addresses (section, pin_queue + start, pin_queue + end,
3743 section->data, section->next_data);
3744 section->pin_queue_start = start;
3745 section->pin_queue_end = start + reduced_to;
3747 copy_space_required += (char*)section->next_data - (char*)section->data;
3751 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3752 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3753 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3755 new_to_space_section ();
3756 gray_object_queue_init ();
3758 /* the old generation doesn't need to be scanned (no remembered sets or card
3759 * table needed either): the only objects that must survive are those pinned and
3760 * those referenced by the precise roots.
3761 * mark any section without pinned objects, so we can free it since we will be able to
3762 * move all the objects.
3764 /* the pinned objects are roots (big objects are included in this list, too) */
3765 for (section = section_list; section; section = section->block.next) {
3766 for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
3767 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n",
3768 i, pin_queue [i], safe_name (pin_queue [i])));
3769 scan_object (pin_queue [i], heap_start, heap_end);
3772 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3773 if (object_is_pinned (bigobj->data)) {
3774 DEBUG (6, fprintf (gc_debug_file, "Precise object scan pinned LOS object %p (%s)\n",
3775 bigobj->data, safe_name (bigobj->data)));
3776 scan_object (bigobj->data, heap_start, heap_end);
3780 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3782 /* registered roots, this includes static fields */
3783 scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_NORMAL);
3784 scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_WBARRIER);
3786 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3789 scan_thread_data (heap_start, heap_end, TRUE);
3791 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3793 /* alloc_pinned objects */
3794 scan_from_pinned_objects (heap_start, heap_end);
3796 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3798 /* scan the list of objects ready for finalization */
3799 scan_finalizer_entries (fin_ready_list, heap_start, heap_end);
3800 scan_finalizer_entries (critical_fin_list, heap_start, heap_end);
3802 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3803 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3805 /* we need to go over the big object list to see if any was marked and scan it
3806 * And we need to make this in a loop, considering that objects referenced by finalizable
3807 * objects could reference big objects (this happens in finish_gray_stack ())
3809 scan_needed_big_objects (heap_start, heap_end);
3811 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3813 /* all the objects in the heap */
3814 finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
3816 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3820 /* sweep the big objects list */
3822 for (bigobj = los_object_list; bigobj;) {
3823 if (object_is_pinned (bigobj->data)) {
3824 unpin_object (bigobj->data);
3825 bigobj->scanned = FALSE;
3828 /* not referenced anywhere, so we can free it */
3830 prevbo->next = bigobj->next;
3832 los_object_list = bigobj->next;
3834 bigobj = bigobj->next;
3835 free_large_object (to_free);
3839 bigobj = bigobj->next;
3841 /* unpin objects from the pinned chunks and free the unmarked ones */
3842 sweep_pinned_objects ();
3845 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3847 /* free the unused sections */
3848 prev_section = NULL;
3849 for (section = section_list; section;) {
3850 /* to_space doesn't need handling here and the nursery is special */
3851 if (section->is_to_space || section == nursery_section) {
3852 if (section->is_to_space)
3853 section->is_to_space = FALSE;
3854 prev_section = section;
3855 section = section->block.next;
3858 /* no pinning object, so the section is free */
3859 if (section->pin_queue_start == section->pin_queue_end) {
3860 GCMemSection *to_free;
3862 prev_section->block.next = section->block.next;
3864 section_list = section->block.next;
3866 section = section->block.next;
3867 free_major_section (to_free);
3870 DEBUG (6, fprintf (gc_debug_file, "Section %p has still pinned objects (%d)\n", section, section->pin_queue_end - section->pin_queue_start));
3871 build_section_fragments (section);
3873 prev_section = section;
3874 section = section->block.next;
3877 /* walk the pin_queue, build up the fragment list of free memory, unmark
3878 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3881 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
3884 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3886 TV_GETTIME (all_btv);
3887 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3890 dump_heap ("major", num_major_gcs - 1, reason);
3892 /* prepare the pin queue for the next collection */
3894 if (fin_ready_list || critical_fin_list) {
3895 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3896 mono_gc_finalize_notify ();
3900 g_assert (gray_object_queue_is_empty ());
3902 commit_stats (GENERATION_OLD);
3904 num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 1);
3906 save_target = num_major_sections / 2;
3908 * We aim to allow the allocation of as many sections as is
3909 * necessary to reclaim save_target sections in the next
3910 * collection. We assume the collection pattern won't change.
3911 * In the last cycle, we had num_major_sections_saved for
3912 * minor_collection_sections_alloced. Assuming things won't
3913 * change, this must be the same ratio as save_target for
3914 * allowance_target, i.e.
3916 * num_major_sections_saved save_target
3917 * --------------------------------- == ----------------
3918 * minor_collection_sections_alloced allowance_target
3922 allowance_target = save_target * minor_collection_sections_alloced / num_major_sections_saved;
3924 minor_collection_section_allowance = MAX (MIN (allowance_target, num_major_sections), MIN_MINOR_COLLECTION_SECTION_ALLOWANCE);
3926 minor_collection_sections_alloced = 0;
3928 check_scan_starts ();
3932 * Allocate a new section of memory to be used as old generation.
3934 static GCMemSection*
3935 alloc_major_section (void)
3937 GCMemSection *section;
3940 section = get_os_memory_aligned (MAJOR_SECTION_SIZE, TRUE);
3941 section->next_data = section->data = (char*)section + SIZEOF_GC_MEM_SECTION;
3942 g_assert (!((mword)section->data & 7));
3943 section->size = MAJOR_SECTION_SIZE - SIZEOF_GC_MEM_SECTION;
3944 section->end_data = section->data + section->size;
3945 UPDATE_HEAP_BOUNDARIES (section->data, section->end_data);
3946 total_alloc += section->size;
3947 DEBUG (3, fprintf (gc_debug_file, "New major heap section: (%p-%p), total: %zd\n", section->data, section->end_data, total_alloc));
3948 scan_starts = (section->size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
3949 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
3950 section->num_scan_start = scan_starts;
3951 section->block.role = MEMORY_ROLE_GEN1;
3952 section->is_to_space = TRUE;
3954 /* add to the section list */
3955 section->block.next = section_list;
3956 section_list = section;
3958 ++num_major_sections;
3964 free_major_section (GCMemSection *section)
3966 DEBUG (3, fprintf (gc_debug_file, "Freed major section %p (%p-%p)\n", section, section->data, section->end_data));
3967 free_internal_mem (section->scan_starts, INTERNAL_MEM_SCAN_STARTS);
3968 free_os_memory (section, MAJOR_SECTION_SIZE);
3969 total_alloc -= MAJOR_SECTION_SIZE - SIZEOF_GC_MEM_SECTION;
3971 --num_major_sections;
3975 * When deciding if it's better to collect or to expand, keep track
3976 * of how much garbage was reclaimed with the last collection: if it's too
3978 * This is called when we could not allocate a small object.
3980 static void __attribute__((noinline))
3981 minor_collect_or_expand_inner (size_t size)
3983 int do_minor_collection = 1;
3985 if (!nursery_section) {
3989 if (do_minor_collection) {
3991 if (collect_nursery (size))
3992 major_collection ("minor overflow");
3993 DEBUG (2, fprintf (gc_debug_file, "Heap size: %zd, LOS size: %zd\n", total_alloc, los_memory_usage));
3995 /* this also sets the proper pointers for the next allocation */
3996 if (!search_fragment_for_size (size)) {
3998 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3999 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
4000 for (i = 0; i < last_num_pinned; ++i) {
4001 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])));
4006 //report_internal_mem_usage ();
4010 * ######################################################################
4011 * ######## Memory allocation from the OS
4012 * ######################################################################
4013 * This section of code deals with getting memory from the OS and
4014 * allocating memory for GC-internal data structures.
4015 * Internal memory can be handled with a freelist for small objects.
4019 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
4020 * This must not require any lock.
4023 get_os_memory (size_t size, int activate)
4026 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
4028 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
4029 size += pagesize - 1;
4030 size &= ~(pagesize - 1);
4031 ptr = mono_valloc (0, size, prot_flags);
4036 * Free the memory returned by get_os_memory (), returning it to the OS.
4039 free_os_memory (void *addr, size_t size)
4041 mono_vfree (addr, size);
4048 report_pinned_chunk (PinnedChunk *chunk, int seq) {
4050 int i, free_pages, num_free, free_mem;
4052 for (i = 0; i < chunk->num_pages; ++i) {
4053 if (!chunk->page_sizes [i])
4056 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);
4057 free_mem = FREELIST_PAGESIZE * free_pages;
4058 for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
4059 if (!chunk->free_list [i])
4062 p = chunk->free_list [i];
4067 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
4068 free_mem += freelist_sizes [i] * num_free;
4070 printf ("\tfree memory in chunk: %d\n", free_mem);
4076 static G_GNUC_UNUSED void
4077 report_internal_mem_usage (void) {
4080 printf ("Internal memory usage:\n");
4082 for (chunk = internal_chunk_list; chunk; chunk = chunk->block.next) {
4083 report_pinned_chunk (chunk, i++);
4085 printf ("Pinned memory usage:\n");
4087 for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
4088 report_pinned_chunk (chunk, i++);
4093 * the array of pointers from @start to @end contains conservative
4094 * pointers to objects inside @chunk: mark each referenced object
4098 mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end)
4100 for (; start < end; start++) {
4101 char *addr = *start;
4102 int offset = (char*)addr - (char*)chunk;
4103 int page = offset / FREELIST_PAGESIZE;
4104 int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
4105 int slot_size = chunk->page_sizes [page];
4107 /* the page is not allocated */
4110 /* would be faster if we restrict the sizes to power of two,
4111 * but that's a waste of memory: need to measure. it could reduce
4112 * fragmentation since there are less pages needed, if for example
4113 * someone interns strings of each size we end up with one page per
4114 * interned string (still this is just ~40 KB): with more fine-grained sizes
4115 * this increases the number of used pages.
4118 obj_offset /= slot_size;
4119 obj_offset *= slot_size;
4120 addr = (char*)chunk->start_data + obj_offset;
4122 obj_offset /= slot_size;
4123 obj_offset *= slot_size;
4124 addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
4127 /* if the vtable is inside the chunk it's on the freelist, so skip */
4128 if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))) {
4129 binary_protocol_pin (addr, (gpointer)LOAD_VTABLE (addr), safe_object_get_size ((MonoObject*)addr));
4130 if (heap_dump_file && !object_is_pinned (addr))
4131 pin_stats_register_object ((char*) addr, safe_object_get_size ((MonoObject*) addr));
4133 DEBUG (6, fprintf (gc_debug_file, "Marked pinned object %p (%s) from roots\n", addr, safe_name (addr)));
4139 scan_pinned_objects (ScanPinnedObjectCallbackFunc callback, void *callback_data)
4146 for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
4147 end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
4148 DEBUG (6, fprintf (gc_debug_file, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
4149 for (i = 0; i < chunk->num_pages; ++i) {
4150 obj_size = chunk->page_sizes [i];
4153 p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
4154 endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
4155 DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
4156 while (p + obj_size <= endp) {
4158 DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
4159 /* if the first word (the vtable) is outside the chunk we have an object */
4160 if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk))
4161 callback (chunk, (char*)ptr, obj_size, callback_data);
4169 sweep_pinned_objects_callback (PinnedChunk *chunk, char *ptr, size_t size, void *data)
4171 if (object_is_pinned (ptr)) {
4173 DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
4175 DEBUG (6, fprintf (gc_debug_file, "Freeing unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
4176 free_pinned_object (chunk, ptr, size);
4181 sweep_pinned_objects (void)
4183 scan_pinned_objects (sweep_pinned_objects_callback, NULL);
4187 scan_object_callback (PinnedChunk *chunk, char *ptr, size_t size, char **data)
4189 DEBUG (6, fprintf (gc_debug_file, "Precise object scan of alloc_pinned %p (%s)\n", ptr, safe_name (ptr)));
4190 /* FIXME: Put objects without references into separate chunks
4191 which do not need to be scanned */
4192 scan_object (ptr, data [0], data [1]);
4196 scan_from_pinned_objects (char *addr_start, char *addr_end)
4198 char *data [2] = { addr_start, addr_end };
4199 scan_pinned_objects ((ScanPinnedObjectCallbackFunc)scan_object_callback, data);
4203 * Find the slot number in the freelist for memory chunks that
4204 * can contain @size objects.
4207 slot_for_size (size_t size)
4210 /* do a binary search or lookup table later. */
4211 for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
4212 if (freelist_sizes [slot] >= size)
4215 g_assert_not_reached ();
4220 * Build a free list for @size memory chunks from the memory area between
4221 * start_page and end_page.
4224 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
4228 /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
4229 p = (void**)start_page;
4230 end = (void**)(end_page - size);
4231 g_assert (!chunk->free_list [slot]);
4232 chunk->free_list [slot] = p;
4233 while ((char*)p + size <= (char*)end) {
4235 *p = (void*)((char*)p + size);
4239 /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
4243 alloc_pinned_chunk (void)
4247 int size = MAJOR_SECTION_SIZE;
4249 chunk = get_os_memory_aligned (size, TRUE);
4250 chunk->block.role = MEMORY_ROLE_PINNED;
4252 UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
4253 total_alloc += size;
4254 pinned_chunk_bytes_alloced += size;
4256 /* setup the bookeeping fields */
4257 chunk->num_pages = size / FREELIST_PAGESIZE;
4258 offset = G_STRUCT_OFFSET (PinnedChunk, data);
4259 chunk->page_sizes = (void*)((char*)chunk + offset);
4260 offset += sizeof (int) * chunk->num_pages;
4261 offset += ALLOC_ALIGN - 1;
4262 offset &= ~(ALLOC_ALIGN - 1);
4263 chunk->free_list = (void*)((char*)chunk + offset);
4264 offset += sizeof (void*) * FREELIST_NUM_SLOTS;
4265 offset += ALLOC_ALIGN - 1;
4266 offset &= ~(ALLOC_ALIGN - 1);
4267 chunk->start_data = (void*)((char*)chunk + offset);
4269 /* allocate the first page to the freelist */
4270 chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
4271 build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
4272 DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
4273 min_pinned_chunk_addr = MIN (min_pinned_chunk_addr, (char*)chunk->start_data);
4274 max_pinned_chunk_addr = MAX (max_pinned_chunk_addr, ((char*)chunk + size));
4278 /* assumes freelist for slot is empty, so try to alloc a new page */
4280 get_chunk_freelist (PinnedChunk *chunk, int slot)
4284 p = chunk->free_list [slot];
4286 chunk->free_list [slot] = *p;
4289 for (i = 0; i < chunk->num_pages; ++i) {
4291 if (chunk->page_sizes [i])
4293 size = freelist_sizes [slot];
4294 chunk->page_sizes [i] = size;
4295 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
4299 p = chunk->free_list [slot];
4301 chunk->free_list [slot] = *p;
4308 alloc_from_freelist (size_t size)
4312 PinnedChunk *pchunk;
4313 slot = slot_for_size (size);
4314 /*g_print ("using slot %d for size %d (slot size: %d)\n", slot, size, freelist_sizes [slot]);*/
4315 g_assert (size <= freelist_sizes [slot]);
4316 for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->block.next) {
4317 void **p = pchunk->free_list [slot];
4319 /*g_print ("found freelist for slot %d in chunk %p, returning %p, next %p\n", slot, pchunk, p, *p);*/
4320 pchunk->free_list [slot] = *p;
4324 for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->block.next) {
4325 res = get_chunk_freelist (pchunk, slot);
4329 pchunk = alloc_pinned_chunk ();
4330 /* FIXME: handle OOM */
4331 pchunk->block.next = pinned_chunk_list;
4332 pinned_chunk_list = pchunk;
4333 res = get_chunk_freelist (pchunk, slot);
4337 /* used for the GC-internal data structures */
4339 get_internal_mem (size_t size, int type)
4343 PinnedChunk *pchunk;
4345 if (size > freelist_sizes [FREELIST_NUM_SLOTS - 1]) {
4346 LargeInternalMemHeader *mh;
4348 size += sizeof (LargeInternalMemHeader);
4349 mh = get_os_memory (size, TRUE);
4350 mh->magic = LARGE_INTERNAL_MEM_HEADER_MAGIC;
4353 large_internal_bytes_alloced += size;
4358 slot = slot_for_size (size);
4359 g_assert (size <= freelist_sizes [slot]);
4361 small_internal_mem_bytes [type] += freelist_sizes [slot];
4363 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
4364 void **p = pchunk->free_list [slot];
4366 pchunk->free_list [slot] = *p;
4367 memset (p, 0, size);
4371 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
4372 res = get_chunk_freelist (pchunk, slot);
4374 memset (res, 0, size);
4378 pchunk = alloc_pinned_chunk ();
4379 /* FIXME: handle OOM */
4380 pchunk->block.next = internal_chunk_list;
4381 internal_chunk_list = pchunk;
4382 res = get_chunk_freelist (pchunk, slot);
4383 memset (res, 0, size);
4388 free_internal_mem (void *addr, int type)
4390 PinnedChunk *pchunk;
4391 LargeInternalMemHeader *mh;
4394 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
4395 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
4396 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
4397 int offset = (char*)addr - (char*)pchunk;
4398 int page = offset / FREELIST_PAGESIZE;
4399 int slot = slot_for_size (pchunk->page_sizes [page]);
4401 *p = pchunk->free_list [slot];
4402 pchunk->free_list [slot] = p;
4404 small_internal_mem_bytes [type] -= freelist_sizes [slot];
4409 mh = (LargeInternalMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargeInternalMemHeader, data));
4410 g_assert (mh->magic == LARGE_INTERNAL_MEM_HEADER_MAGIC);
4411 large_internal_bytes_alloced -= mh->size;
4412 free_os_memory (mh, mh->size);
4416 * ######################################################################
4417 * ######## Object allocation
4418 * ######################################################################
4419 * This section of code deals with allocating memory for objects.
4420 * There are several ways:
4421 * *) allocate large objects
4422 * *) allocate normal objects
4423 * *) fast lock-free allocation
4424 * *) allocation of pinned objects
4428 free_large_object (LOSObject *obj)
4430 size_t size = obj->size;
4431 DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %zd\n", obj->data, obj->size));
4432 binary_protocol_empty (obj->data, obj->size);
4434 los_memory_usage -= size;
4435 size += sizeof (LOSObject);
4436 size += pagesize - 1;
4437 size &= ~(pagesize - 1);
4438 total_alloc -= size;
4440 free_os_memory (obj, size);
4444 * Objects with size >= 64KB are allocated in the large object space.
4445 * They are currently kept track of with a linked list.
4446 * They don't move, so there is no need to pin them during collection
4447 * and we avoid the memcpy overhead.
4449 static void* __attribute__((noinline))
4450 alloc_large_inner (MonoVTable *vtable, size_t size)
4456 g_assert (size > MAX_SMALL_OBJ_SIZE);
4458 if (los_memory_usage > next_los_collection) {
4459 static mword last_los_memory_usage = 0;
4461 mword los_memory_alloced;
4462 mword old_los_memory_usage;
4463 mword los_memory_saved;
4465 mword allowance_target;
4468 DEBUG (4, fprintf (gc_debug_file, "Should trigger major collection: req size %zd (los already: %zu, limit: %zu)\n", size, los_memory_usage, next_los_collection));
4471 g_assert (los_memory_usage >= last_los_memory_usage);
4472 los_memory_alloced = los_memory_usage - last_los_memory_usage;
4473 old_los_memory_usage = los_memory_usage;
4475 major_collection ("LOS overflow");
4477 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
4478 save_target = los_memory_usage / 2;
4480 * see the comment at the end of major_collection()
4481 * for the explanation for this calculation.
4483 allowance_target = (mword)((double)save_target * (double)los_memory_alloced / (double)los_memory_saved);
4484 allowance = MAX (MIN (allowance_target, los_memory_usage), MIN_LOS_ALLOWANCE);
4485 next_los_collection = los_memory_usage + allowance;
4487 last_los_memory_usage = los_memory_usage;
4492 alloc_size += sizeof (LOSObject);
4493 alloc_size += pagesize - 1;
4494 alloc_size &= ~(pagesize - 1);
4495 /* FIXME: handle OOM */
4496 obj = get_os_memory (alloc_size, TRUE);
4498 vtslot = (void**)obj->data;
4500 total_alloc += alloc_size;
4501 UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
4502 obj->next = los_object_list;
4503 los_object_list = obj;
4504 los_memory_usage += size;
4506 DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %zd\n", obj->data, vtable, vtable->klass->name, size));
4507 binary_protocol_alloc (obj->data, vtable, size);
4511 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
4512 * an object of size @size
4513 * Return FALSE if not found (which means we need a collection)
4516 search_fragment_for_size (size_t size)
4518 Fragment *frag, *prev;
4519 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
4521 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4522 /* Clear the remaining space, pinning depends on this */
4523 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
4526 for (frag = nursery_fragments; frag; frag = frag->next) {
4527 if (size <= (frag->fragment_end - frag->fragment_start)) {
4528 /* remove from the list */
4530 prev->next = frag->next;
4532 nursery_fragments = frag->next;
4533 nursery_next = frag->fragment_start;
4534 nursery_frag_real_end = frag->fragment_end;
4536 DEBUG (4, fprintf (gc_debug_file, "Using nursery fragment %p-%p, size: %zd (req: %zd)\n", nursery_next, nursery_frag_real_end, nursery_frag_real_end - nursery_next, size));
4537 frag->next = fragment_freelist;
4538 fragment_freelist = frag;
4547 * size is already rounded up and we hold the GC lock.
4550 alloc_degraded (MonoVTable *vtable, size_t size)
4552 GCMemSection *section;
4554 g_assert (size <= MAX_SMALL_OBJ_SIZE);
4555 HEAVY_STAT (++stat_objects_alloced_degraded);
4556 HEAVY_STAT (stat_bytes_alloced_degraded += size);
4557 for (section = section_list; section; section = section->block.next) {
4558 if (section != nursery_section && (section->end_data - section->next_data) >= size) {
4559 p = (void**)section->next_data;
4564 section = alloc_major_section ();
4565 section->is_to_space = FALSE;
4566 /* FIXME: handle OOM */
4567 p = (void**)section->next_data;
4569 section->next_data += size;
4570 degraded_mode += size;
4571 DEBUG (3, fprintf (gc_debug_file, "Allocated (degraded) object %p, vtable: %p (%s), size: %zd in section %p\n", p, vtable, vtable->klass->name, size, section));
4577 * Provide a variant that takes just the vtable for small fixed-size objects.
4578 * The aligned size is already computed and stored in vt->gc_descr.
4579 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
4580 * processing. We can keep track of where objects start, for example,
4581 * so when we scan the thread stacks for pinned objects, we can start
4582 * a search for the pinned object in SCAN_START_SIZE chunks.
4585 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4587 /* FIXME: handle OOM */
4593 HEAVY_STAT (++stat_objects_alloced);
4594 if (size <= MAX_SMALL_OBJ_SIZE)
4595 HEAVY_STAT (stat_bytes_alloced += size);
4597 HEAVY_STAT (stat_bytes_alloced_los += size);
4599 size += ALLOC_ALIGN - 1;
4600 size &= ~(ALLOC_ALIGN - 1);
4602 g_assert (vtable->gc_descr);
4604 if (G_UNLIKELY (collect_before_allocs)) {
4605 if (nursery_section) {
4607 collect_nursery (0);
4609 if (!degraded_mode && !search_fragment_for_size (size)) {
4611 g_assert_not_reached ();
4617 * We must already have the lock here instead of after the
4618 * fast path because we might be interrupted in the fast path
4619 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
4620 * and we'll end up allocating an object in a fragment which
4621 * no longer belongs to us.
4623 * The managed allocator does not do this, but it's treated
4624 * specially by the world-stopping code.
4627 if (size > MAX_SMALL_OBJ_SIZE) {
4628 p = alloc_large_inner (vtable, size);
4630 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4632 p = (void**)TLAB_NEXT;
4633 /* FIXME: handle overflow */
4634 new_next = (char*)p + size;
4635 TLAB_NEXT = new_next;
4637 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4641 * FIXME: We might need a memory barrier here so the change to tlab_next is
4642 * visible before the vtable store.
4645 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4646 binary_protocol_alloc (p , vtable, size);
4647 g_assert (*p == NULL);
4650 g_assert (TLAB_NEXT == new_next);
4657 /* there are two cases: the object is too big or we run out of space in the TLAB */
4658 /* we also reach here when the thread does its first allocation after a minor
4659 * collection, since the tlab_ variables are initialized to NULL.
4660 * there can be another case (from ORP), if we cooperate with the runtime a bit:
4661 * objects that need finalizers can have the high bit set in their size
4662 * so the above check fails and we can readily add the object to the queue.
4663 * This avoids taking again the GC lock when registering, but this is moot when
4664 * doing thread-local allocation, so it may not be a good idea.
4666 g_assert (TLAB_NEXT == new_next);
4667 if (TLAB_NEXT >= TLAB_REAL_END) {
4669 * Run out of space in the TLAB. When this happens, some amount of space
4670 * remains in the TLAB, but not enough to satisfy the current allocation
4671 * request. Currently, we retire the TLAB in all cases, later we could
4672 * keep it if the remaining space is above a treshold, and satisfy the
4673 * allocation directly from the nursery.
4676 /* when running in degraded mode, we continue allocing that way
4677 * for a while, to decrease the number of useless nursery collections.
4679 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
4680 p = alloc_degraded (vtable, size);
4684 if (size > tlab_size) {
4685 /* Allocate directly from the nursery */
4686 if (nursery_next + size >= nursery_frag_real_end) {
4687 if (!search_fragment_for_size (size)) {
4688 minor_collect_or_expand_inner (size);
4689 if (degraded_mode) {
4690 p = alloc_degraded (vtable, size);
4696 p = (void*)nursery_next;
4697 nursery_next += size;
4698 if (nursery_next > nursery_frag_real_end) {
4703 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4704 memset (p, 0, size);
4707 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
4709 if (nursery_next + tlab_size >= nursery_frag_real_end) {
4710 res = search_fragment_for_size (tlab_size);
4712 minor_collect_or_expand_inner (tlab_size);
4713 if (degraded_mode) {
4714 p = alloc_degraded (vtable, size);
4720 /* Allocate a new TLAB from the current nursery fragment */
4721 TLAB_START = nursery_next;
4722 nursery_next += tlab_size;
4723 TLAB_NEXT = TLAB_START;
4724 TLAB_REAL_END = TLAB_START + tlab_size;
4725 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, tlab_size);
4727 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4728 memset (TLAB_START, 0, tlab_size);
4730 /* Allocate from the TLAB */
4731 p = (void*)TLAB_NEXT;
4733 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4735 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4738 /* Reached tlab_temp_end */
4740 /* record the scan start so we can find pinned objects more easily */
4741 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4742 /* we just bump tlab_temp_end as well */
4743 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4744 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4748 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4749 binary_protocol_alloc (p, vtable, size);
4756 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4762 size += ALLOC_ALIGN - 1;
4763 size &= ~(ALLOC_ALIGN - 1);
4765 g_assert (vtable->gc_descr);
4766 if (size <= MAX_SMALL_OBJ_SIZE) {
4767 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4769 p = (void**)TLAB_NEXT;
4770 /* FIXME: handle overflow */
4771 new_next = (char*)p + size;
4772 TLAB_NEXT = new_next;
4774 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4778 * FIXME: We might need a memory barrier here so the change to tlab_next is
4779 * visible before the vtable store.
4782 HEAVY_STAT (++stat_objects_alloced);
4783 HEAVY_STAT (stat_bytes_alloced += size);
4785 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4786 binary_protocol_alloc (p, vtable, size);
4787 g_assert (*p == NULL);
4790 g_assert (TLAB_NEXT == new_next);
4799 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4802 #ifndef DISABLE_CRITICAL_REGION
4804 ENTER_CRITICAL_REGION;
4805 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4807 EXIT_CRITICAL_REGION;
4810 EXIT_CRITICAL_REGION;
4813 res = mono_gc_alloc_obj_nolock (vtable, size);
4819 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4822 #ifndef DISABLE_CRITICAL_REGION
4824 ENTER_CRITICAL_REGION;
4825 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4827 arr->max_length = max_length;
4828 EXIT_CRITICAL_REGION;
4831 EXIT_CRITICAL_REGION;
4836 arr = mono_gc_alloc_obj_nolock (vtable, size);
4837 arr->max_length = max_length;
4845 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4848 MonoArrayBounds *bounds;
4852 arr = mono_gc_alloc_obj_nolock (vtable, size);
4853 arr->max_length = max_length;
4855 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4856 arr->bounds = bounds;
4864 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4867 #ifndef DISABLE_CRITICAL_REGION
4869 ENTER_CRITICAL_REGION;
4870 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4873 EXIT_CRITICAL_REGION;
4876 EXIT_CRITICAL_REGION;
4881 str = mono_gc_alloc_obj_nolock (vtable, size);
4890 * To be used for interned strings and possibly MonoThread, reflection handles.
4891 * We may want to explicitly free these objects.
4894 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4896 /* FIXME: handle OOM */
4898 size += ALLOC_ALIGN - 1;
4899 size &= ~(ALLOC_ALIGN - 1);
4901 if (size > MAX_FREELIST_SIZE) {
4902 /* large objects are always pinned anyway */
4903 p = alloc_large_inner (vtable, size);
4905 p = alloc_from_freelist (size);
4906 memset (p, 0, size);
4908 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4909 binary_protocol_alloc (p, vtable, size);
4916 * ######################################################################
4917 * ######## Finalization support
4918 * ######################################################################
4922 * this is valid for the nursery: if the object has been forwarded it means it's
4923 * still refrenced from a root. If it is pinned it's still alive as well.
4924 * Return TRUE if @obj is ready to be finalized.
4926 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4929 is_critical_finalizer (FinalizeEntry *entry)
4934 if (!mono_defaults.critical_finalizer_object)
4937 obj = entry->object;
4938 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4940 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4944 queue_finalization_entry (FinalizeEntry *entry) {
4945 if (is_critical_finalizer (entry)) {
4946 entry->next = critical_fin_list;
4947 critical_fin_list = entry;
4949 entry->next = fin_ready_list;
4950 fin_ready_list = entry;
4954 /* LOCKING: requires that the GC lock is held */
4956 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4958 FinalizeEntry **finalizable_hash = hash_table->table;
4959 mword finalizable_hash_size = hash_table->size;
4962 FinalizeEntry **new_hash;
4963 FinalizeEntry *entry, *next;
4964 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4966 new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4967 for (i = 0; i < finalizable_hash_size; ++i) {
4968 for (entry = finalizable_hash [i]; entry; entry = next) {
4969 hash = mono_object_hash (entry->object) % new_size;
4971 entry->next = new_hash [hash];
4972 new_hash [hash] = entry;
4975 free_internal_mem (finalizable_hash, INTERNAL_MEM_FIN_TABLE);
4976 hash_table->table = new_hash;
4977 hash_table->size = new_size;
4980 /* LOCKING: requires that the GC lock is held */
4982 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4984 if (hash_table->num_registered >= hash_table->size * 2)
4985 rehash_fin_table (hash_table);
4988 /* LOCKING: requires that the GC lock is held */
4990 finalize_in_range (char *start, char *end, int generation)
4992 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4993 FinalizeEntry *entry, *prev;
4995 FinalizeEntry **finalizable_hash = hash_table->table;
4996 mword finalizable_hash_size = hash_table->size;
5000 for (i = 0; i < finalizable_hash_size; ++i) {
5002 for (entry = finalizable_hash [i]; entry;) {
5003 if ((char*)entry->object >= start && (char*)entry->object < end && !object_is_in_to_space (entry->object)) {
5004 gboolean is_fin_ready = object_is_fin_ready (entry->object);
5005 char *copy = copy_object_no_heap (entry->object, start, end);
5008 FinalizeEntry *next;
5009 /* remove and put in fin_ready_list */
5011 prev->next = entry->next;
5013 finalizable_hash [i] = entry->next;
5015 num_ready_finalizers++;
5016 hash_table->num_registered--;
5017 queue_finalization_entry (entry);
5018 /* Make it survive */
5019 from = entry->object;
5020 entry->object = copy;
5021 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, hash_table->num_registered));
5025 char *from = entry->object;
5026 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
5027 FinalizeEntry *next = entry->next;
5028 unsigned int major_hash;
5029 /* remove from the list */
5031 prev->next = entry->next;
5033 finalizable_hash [i] = entry->next;
5034 hash_table->num_registered--;
5036 entry->object = copy;
5038 /* insert it into the major hash */
5039 rehash_fin_table_if_necessary (&major_finalizable_hash);
5040 major_hash = mono_object_hash ((MonoObject*) copy) %
5041 major_finalizable_hash.size;
5042 entry->next = major_finalizable_hash.table [major_hash];
5043 major_finalizable_hash.table [major_hash] = entry;
5044 major_finalizable_hash.num_registered++;
5046 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
5051 /* update pointer */
5052 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
5053 entry->object = copy;
5058 entry = entry->next;
5063 /* LOCKING: requires that the GC lock is held */
5065 null_link_in_range (char *start, char *end, int generation)
5067 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
5068 DisappearingLink **disappearing_link_hash = hash->table;
5069 int disappearing_link_hash_size = hash->size;
5070 DisappearingLink *entry, *prev;
5072 if (!hash->num_links)
5074 for (i = 0; i < disappearing_link_hash_size; ++i) {
5076 for (entry = disappearing_link_hash [i]; entry;) {
5077 char *object = DISLINK_OBJECT (entry);
5078 if (object >= start && object < end && !object_is_in_to_space (object)) {
5079 gboolean track = DISLINK_TRACK (entry);
5080 if (!track && object_is_fin_ready (object)) {
5081 void **p = entry->link;
5082 DisappearingLink *old;
5084 /* remove from list */
5086 prev->next = entry->next;
5088 disappearing_link_hash [i] = entry->next;
5089 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
5091 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5096 char *copy = copy_object_no_heap (object, start, end);
5098 /* Update pointer if it's moved. If the object
5099 * has been moved out of the nursery, we need to
5100 * remove the link from the minor hash table to
5103 * FIXME: what if an object is moved earlier?
5106 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
5107 void **link = entry->link;
5108 DisappearingLink *old;
5109 /* remove from list */
5111 prev->next = entry->next;
5113 disappearing_link_hash [i] = entry->next;
5115 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5119 add_or_remove_disappearing_link ((MonoObject*)copy, link,
5120 track, GENERATION_OLD);
5122 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
5126 /* We set the track resurrection bit to
5127 * FALSE if the object is to be finalized
5128 * so that the object can be collected in
5129 * the next cycle (i.e. after it was
5132 *entry->link = HIDE_POINTER (copy,
5133 object_is_fin_ready (object) ? FALSE : track);
5134 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
5139 entry = entry->next;
5144 /* LOCKING: requires that the GC lock is held */
5146 null_links_for_domain (MonoDomain *domain, int generation)
5148 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
5149 DisappearingLink **disappearing_link_hash = hash->table;
5150 int disappearing_link_hash_size = hash->size;
5151 DisappearingLink *entry, *prev;
5153 for (i = 0; i < disappearing_link_hash_size; ++i) {
5155 for (entry = disappearing_link_hash [i]; entry; ) {
5156 char *object = DISLINK_OBJECT (entry);
5157 /* FIXME: actually there should be no object
5158 left in the domain with a non-null vtable
5159 (provided we remove the Thread special
5161 if (object && (!((MonoObject*)object)->vtable || mono_object_domain (object) == domain)) {
5162 DisappearingLink *next = entry->next;
5167 disappearing_link_hash [i] = next;
5169 if (*(entry->link)) {
5170 *(entry->link) = NULL;
5171 g_warning ("Disappearing link %p not freed", entry->link);
5173 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5180 entry = entry->next;
5185 /* LOCKING: requires that the GC lock is held */
5187 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
5188 FinalizeEntryHashTable *hash_table)
5190 FinalizeEntry **finalizable_hash = hash_table->table;
5191 mword finalizable_hash_size = hash_table->size;
5192 FinalizeEntry *entry, *prev;
5195 if (no_finalize || !out_size || !out_array)
5198 for (i = 0; i < finalizable_hash_size; ++i) {
5200 for (entry = finalizable_hash [i]; entry;) {
5201 if (mono_object_domain (entry->object) == domain) {
5202 FinalizeEntry *next;
5203 /* remove and put in out_array */
5205 prev->next = entry->next;
5207 finalizable_hash [i] = entry->next;
5209 hash_table->num_registered--;
5210 out_array [count ++] = entry->object;
5211 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, hash_table->num_registered));
5213 if (count == out_size)
5218 entry = entry->next;
5225 * mono_gc_finalizers_for_domain:
5226 * @domain: the unloading appdomain
5227 * @out_array: output array
5228 * @out_size: size of output array
5230 * Store inside @out_array up to @out_size objects that belong to the unloading
5231 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
5232 * until it returns 0.
5233 * The items are removed from the finalizer data structure, so the caller is supposed
5235 * @out_array should be on the stack to allow the GC to know the objects are still alive.
5238 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
5243 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
5244 if (result < out_size) {
5245 result += finalizers_for_domain (domain, out_array + result, out_size - result,
5246 &major_finalizable_hash);
5254 register_for_finalization (MonoObject *obj, void *user_data, int generation)
5256 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
5257 FinalizeEntry **finalizable_hash;
5258 mword finalizable_hash_size;
5259 FinalizeEntry *entry, *prev;
5263 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
5264 hash = mono_object_hash (obj);
5266 rehash_fin_table_if_necessary (hash_table);
5267 finalizable_hash = hash_table->table;
5268 finalizable_hash_size = hash_table->size;
5269 hash %= finalizable_hash_size;
5271 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
5272 if (entry->object == obj) {
5274 /* remove from the list */
5276 prev->next = entry->next;
5278 finalizable_hash [hash] = entry->next;
5279 hash_table->num_registered--;
5280 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered));
5281 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
5289 /* request to deregister, but already out of the list */
5293 entry = get_internal_mem (sizeof (FinalizeEntry), INTERNAL_MEM_FINALIZE_ENTRY);
5294 entry->object = obj;
5295 entry->next = finalizable_hash [hash];
5296 finalizable_hash [hash] = entry;
5297 hash_table->num_registered++;
5298 DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d) to %s table\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered, generation_name (generation)));
5303 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
5305 if (ptr_in_nursery (obj))
5306 register_for_finalization (obj, user_data, GENERATION_NURSERY);
5308 register_for_finalization (obj, user_data, GENERATION_OLD);
5312 rehash_dislink (DisappearingLinkHashTable *hash_table)
5314 DisappearingLink **disappearing_link_hash = hash_table->table;
5315 int disappearing_link_hash_size = hash_table->size;
5318 DisappearingLink **new_hash;
5319 DisappearingLink *entry, *next;
5320 int new_size = g_spaced_primes_closest (hash_table->num_links);
5322 new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
5323 for (i = 0; i < disappearing_link_hash_size; ++i) {
5324 for (entry = disappearing_link_hash [i]; entry; entry = next) {
5325 hash = mono_aligned_addr_hash (entry->link) % new_size;
5327 entry->next = new_hash [hash];
5328 new_hash [hash] = entry;
5331 free_internal_mem (disappearing_link_hash, INTERNAL_MEM_DISLINK_TABLE);
5332 hash_table->table = new_hash;
5333 hash_table->size = new_size;
5336 /* LOCKING: assumes the GC lock is held */
5338 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
5340 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
5341 DisappearingLink *entry, *prev;
5343 DisappearingLink **disappearing_link_hash = hash_table->table;
5344 int disappearing_link_hash_size = hash_table->size;
5346 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
5347 rehash_dislink (hash_table);
5348 disappearing_link_hash = hash_table->table;
5349 disappearing_link_hash_size = hash_table->size;
5351 /* FIXME: add check that link is not in the heap */
5352 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
5353 entry = disappearing_link_hash [hash];
5355 for (; entry; entry = entry->next) {
5356 /* link already added */
5357 if (link == entry->link) {
5358 /* NULL obj means remove */
5361 prev->next = entry->next;
5363 disappearing_link_hash [hash] = entry->next;
5364 hash_table->num_links--;
5365 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
5366 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5369 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
5377 entry = get_internal_mem (sizeof (DisappearingLink), INTERNAL_MEM_DISLINK);
5378 *link = HIDE_POINTER (obj, track);
5380 entry->next = disappearing_link_hash [hash];
5381 disappearing_link_hash [hash] = entry;
5382 hash_table->num_links++;
5383 DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p to %s table\n", entry, obj, obj->vtable->klass->name, link, generation_name (generation)));
5386 /* LOCKING: assumes the GC lock is held */
5388 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
5390 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
5391 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
5393 if (ptr_in_nursery (obj))
5394 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
5396 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
5401 mono_gc_invoke_finalizers (void)
5403 FinalizeEntry *entry = NULL;
5404 gboolean entry_is_critical = FALSE;
5407 /* FIXME: batch to reduce lock contention */
5408 while (fin_ready_list || critical_fin_list) {
5412 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
5414 /* We have finalized entry in the last
5415 interation, now we need to remove it from
5418 *list = entry->next;
5420 FinalizeEntry *e = *list;
5421 while (e->next != entry)
5423 e->next = entry->next;
5425 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
5429 /* Now look for the first non-null entry. */
5430 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
5433 entry_is_critical = FALSE;
5435 entry_is_critical = TRUE;
5436 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
5441 g_assert (entry->object);
5442 num_ready_finalizers--;
5443 obj = entry->object;
5444 entry->object = NULL;
5445 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
5453 g_assert (entry->object == NULL);
5455 /* the object is on the stack so it is pinned */
5456 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
5457 mono_gc_run_finalize (obj, NULL);
5464 mono_gc_pending_finalizers (void)
5466 return fin_ready_list || critical_fin_list;
5469 /* Negative value to remove */
5471 mono_gc_add_memory_pressure (gint64 value)
5473 /* FIXME: Use interlocked functions */
5475 memory_pressure += value;
5480 * ######################################################################
5481 * ######## registered roots support
5482 * ######################################################################
5486 rehash_roots (gboolean pinned)
5490 RootRecord **new_hash;
5491 RootRecord *entry, *next;
5494 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
5495 new_hash = get_internal_mem (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5496 for (i = 0; i < roots_hash_size [pinned]; ++i) {
5497 for (entry = roots_hash [pinned][i]; entry; entry = next) {
5498 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5500 entry->next = new_hash [hash];
5501 new_hash [hash] = entry;
5504 free_internal_mem (roots_hash [pinned], INTERNAL_MEM_ROOTS_TABLE);
5505 roots_hash [pinned] = new_hash;
5506 roots_hash_size [pinned] = new_size;
5510 find_root (int root_type, char *start, guint32 addr_hash)
5512 RootRecord *new_root;
5514 guint32 hash = addr_hash % roots_hash_size [root_type];
5515 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5516 /* we allow changing the size and the descriptor (for thread statics etc) */
5517 if (new_root->start_root == start) {
5526 * We do not coalesce roots.
5529 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5531 RootRecord *new_root;
5532 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5535 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5536 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5539 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5540 new_root = find_root (i, start, addr_hash);
5541 /* we allow changing the size and the descriptor (for thread statics etc) */
5543 size_t old_size = new_root->end_root - new_root->start_root;
5544 new_root->end_root = new_root->start_root + size;
5545 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5546 ((new_root->root_desc == 0) && (descr == NULL)));
5547 new_root->root_desc = (mword)descr;
5549 roots_size -= old_size;
5554 new_root = get_internal_mem (sizeof (RootRecord), INTERNAL_MEM_ROOT_RECORD);
5556 new_root->start_root = start;
5557 new_root->end_root = new_root->start_root + size;
5558 new_root->root_desc = (mword)descr;
5560 hash = addr_hash % roots_hash_size [root_type];
5561 num_roots_entries [root_type]++;
5562 new_root->next = roots_hash [root_type] [hash];
5563 roots_hash [root_type][hash] = new_root;
5564 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));
5574 mono_gc_register_root (char *start, size_t size, void *descr)
5576 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5580 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5582 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5586 mono_gc_deregister_root (char* addr)
5588 RootRecord *tmp, *prev;
5589 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5593 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5594 hash = addr_hash % roots_hash_size [root_type];
5595 tmp = roots_hash [root_type][hash];
5598 if (tmp->start_root == (char*)addr) {
5600 prev->next = tmp->next;
5602 roots_hash [root_type][hash] = tmp->next;
5603 roots_size -= (tmp->end_root - tmp->start_root);
5604 num_roots_entries [root_type]--;
5605 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5606 free_internal_mem (tmp, INTERNAL_MEM_ROOT_RECORD);
5617 * ######################################################################
5618 * ######## Thread handling (stop/start code)
5619 * ######################################################################
5622 /* FIXME: handle large/small config */
5623 #define THREAD_HASH_SIZE 11
5624 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5626 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5628 #if USE_SIGNAL_BASED_START_STOP_WORLD
5630 static MonoSemType suspend_ack_semaphore;
5631 static MonoSemType *suspend_ack_semaphore_ptr;
5632 static unsigned int global_stop_count = 0;
5634 static int suspend_signal_num = SIGXFSZ;
5636 static int suspend_signal_num = SIGPWR;
5638 static int restart_signal_num = SIGXCPU;
5639 static sigset_t suspend_signal_mask;
5640 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5642 /* LOCKING: assumes the GC lock is held */
5643 static SgenThreadInfo*
5644 thread_info_lookup (ARCH_THREAD_TYPE id)
5646 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5647 SgenThreadInfo *info;
5649 info = thread_table [hash];
5650 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5657 update_current_thread_stack (void *start)
5659 void *ptr = cur_thread_regs;
5660 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5662 info->stack_start = align_pointer (&ptr);
5663 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5664 ARCH_STORE_REGS (ptr);
5665 info->stopped_regs = ptr;
5666 if (gc_callbacks.thread_suspend_func)
5667 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5671 signal_desc (int signum)
5673 if (signum == suspend_signal_num)
5675 if (signum == restart_signal_num)
5681 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5682 * have cross-domain checks in the write barrier.
5684 //#define XDOMAIN_CHECKS_IN_WBARRIER
5686 #ifndef HEAVY_STATISTICS
5687 #define MANAGED_ALLOCATION
5688 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5689 #define MANAGED_WBARRIER
5694 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5697 wait_for_suspend_ack (int count)
5701 for (i = 0; i < count; ++i) {
5702 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5703 if (errno != EINTR) {
5704 g_error ("sem_wait ()");
5710 /* LOCKING: assumes the GC lock is held */
5712 thread_handshake (int signum)
5714 int count, i, result;
5715 SgenThreadInfo *info;
5716 pthread_t me = pthread_self ();
5719 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5720 for (info = thread_table [i]; info; info = info->next) {
5721 DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
5722 if (ARCH_THREAD_EQUALS (info->id, me)) {
5723 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
5726 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
5728 result = pthread_kill (info->id, signum);
5730 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
5733 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
5739 wait_for_suspend_ack (count);
5745 restart_threads_until_none_in_managed_allocator (void)
5747 SgenThreadInfo *info;
5748 int i, result, num_threads_died = 0;
5749 int sleep_duration = -1;
5752 int restart_count = 0, restarted_count = 0;
5753 /* restart all threads that stopped in the
5755 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5756 for (info = thread_table [i]; info; info = info->next) {
5759 if (!info->stack_start || info->in_critical_region ||
5760 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5761 binary_protocol_thread_restart ((gpointer)info->id);
5762 result = pthread_kill (info->id, restart_signal_num);
5769 /* we set the stopped_ip to
5770 NULL for threads which
5771 we're not restarting so
5772 that we can easily identify
5774 info->stopped_ip = NULL;
5775 info->stopped_domain = NULL;
5779 /* if no threads were restarted, we're done */
5780 if (restart_count == 0)
5783 /* wait for the threads to signal their restart */
5784 wait_for_suspend_ack (restart_count);
5786 if (sleep_duration < 0) {
5790 g_usleep (sleep_duration);
5791 sleep_duration += 10;
5794 /* stop them again */
5795 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5796 for (info = thread_table [i]; info; info = info->next) {
5797 if (info->skip || info->stopped_ip == NULL)
5799 result = pthread_kill (info->id, suspend_signal_num);
5807 /* some threads might have died */
5808 num_threads_died += restart_count - restarted_count;
5809 /* wait for the threads to signal their suspension
5811 wait_for_suspend_ack (restart_count);
5814 return num_threads_died;
5817 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5819 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5821 SgenThreadInfo *info;
5824 int old_errno = errno;
5825 gpointer regs [ARCH_NUM_REGS];
5826 gpointer stack_start;
5828 id = pthread_self ();
5829 info = thread_info_lookup (id);
5830 info->stopped_domain = mono_domain_get ();
5831 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5832 stop_count = global_stop_count;
5833 /* duplicate signal */
5834 if (0 && info->stop_count == stop_count) {
5838 #ifdef HAVE_KW_THREAD
5839 /* update the remset info in the thread data structure */
5840 info->remset = remembered_set;
5842 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5843 /* If stack_start is not within the limits, then don't set it
5844 in info and we will be restarted. */
5845 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5846 info->stack_start = stack_start;
5848 ARCH_COPY_SIGCTX_REGS (regs, context);
5849 info->stopped_regs = regs;
5851 g_assert (!info->stack_start);
5854 /* Notify the JIT */
5855 if (gc_callbacks.thread_suspend_func)
5856 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5858 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5859 /* notify the waiting thread */
5860 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5861 info->stop_count = stop_count;
5863 /* wait until we receive the restart signal */
5866 sigsuspend (&suspend_signal_mask);
5867 } while (info->signal != restart_signal_num);
5869 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5870 /* notify the waiting thread */
5871 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5877 restart_handler (int sig)
5879 SgenThreadInfo *info;
5880 int old_errno = errno;
5882 info = thread_info_lookup (pthread_self ());
5883 info->signal = restart_signal_num;
5884 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5890 acquire_gc_locks (void)
5896 release_gc_locks (void)
5898 UNLOCK_INTERRUPTION;
5901 static TV_DECLARE (stop_world_time);
5902 static unsigned long max_pause_usec = 0;
5904 /* LOCKING: assumes the GC lock is held */
5910 acquire_gc_locks ();
5912 update_current_thread_stack (&count);
5914 global_stop_count++;
5915 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 ()));
5916 TV_GETTIME (stop_world_time);
5917 count = thread_handshake (suspend_signal_num);
5918 count -= restart_threads_until_none_in_managed_allocator ();
5919 g_assert (count >= 0);
5920 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5924 /* LOCKING: assumes the GC lock is held */
5926 restart_world (void)
5929 SgenThreadInfo *info;
5930 TV_DECLARE (end_sw);
5933 /* notify the profiler of the leftovers */
5934 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5935 if (moved_objects_idx) {
5936 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5937 moved_objects_idx = 0;
5940 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5941 for (info = thread_table [i]; info; info = info->next) {
5942 info->stack_start = NULL;
5943 info->stopped_regs = NULL;
5947 release_gc_locks ();
5949 count = thread_handshake (restart_signal_num);
5950 TV_GETTIME (end_sw);
5951 usec = TV_ELAPSED (stop_world_time, end_sw);
5952 max_pause_usec = MAX (usec, max_pause_usec);
5953 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5957 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5960 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5962 gc_callbacks = *callbacks;
5965 /* Variables holding start/end nursery so it won't have to be passed at every call */
5966 static void *scan_area_arg_start, *scan_area_arg_end;
5969 mono_gc_conservatively_scan_area (void *start, void *end)
5971 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5975 mono_gc_scan_object (void *obj)
5977 return copy_object_no_heap (obj, scan_area_arg_start, scan_area_arg_end);
5981 * Mark from thread stacks and registers.
5984 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5987 SgenThreadInfo *info;
5989 scan_area_arg_start = start_nursery;
5990 scan_area_arg_end = end_nursery;
5992 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5993 for (info = thread_table [i]; info; info = info->next) {
5995 DEBUG (3, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %zd\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
5998 DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
5999 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
6000 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
6002 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
6005 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
6006 start_nursery, end_nursery, PIN_TYPE_STACK);
6012 find_pinning_ref_from_thread (char *obj, size_t size)
6015 SgenThreadInfo *info;
6016 char *endobj = obj + size;
6018 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6019 for (info = thread_table [i]; info; info = info->next) {
6020 char **start = (char**)info->stack_start;
6023 while (start < (char**)info->stack_end) {
6024 if (*start >= obj && *start < endobj) {
6025 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)info->id, start, info->stack_start, info->stack_end));
6030 /* FIXME: check info->stopped_regs */
6036 ptr_on_stack (void *ptr)
6038 gpointer stack_start = &stack_start;
6039 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
6041 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
6047 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
6054 HEAVY_STAT (++stat_global_remsets_processed);
6056 /* FIXME: exclude stack locations */
6057 switch ((*p) & REMSET_TYPE_MASK) {
6058 case REMSET_LOCATION:
6060 //__builtin_prefetch (ptr);
6061 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
6062 gpointer old = *ptr;
6063 copy_object (ptr, start_nursery, end_nursery);
6064 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
6066 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
6067 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
6069 * If the object is pinned, each reference to it from nonpinned objects
6070 * becomes part of the global remset, which can grow very large.
6072 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
6073 add_to_global_remset (ptr, FALSE);
6076 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
6080 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6081 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
6084 while (count-- > 0) {
6085 copy_object (ptr, start_nursery, end_nursery);
6086 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
6087 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
6088 add_to_global_remset (ptr, FALSE);
6093 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6094 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
6096 scan_object ((char*)ptr, start_nursery, end_nursery);
6098 case REMSET_OTHER: {
6099 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6103 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
6108 ptr = (void**) scan_vtype ((char*)ptr, desc, start_nursery, end_nursery);
6110 case REMSET_ROOT_LOCATION:
6111 /* Same as REMSET_LOCATION, but the address is not required to be in the heap */
6112 copy_object (ptr, start_nursery, end_nursery);
6113 DEBUG (9, fprintf (gc_debug_file, "Overwrote root location remset at %p with %p\n", ptr, *ptr));
6114 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
6116 * If the object is pinned, each reference to it from nonpinned objects
6117 * becomes part of the global remset, which can grow very large.
6119 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
6120 add_to_global_remset (ptr, TRUE);
6124 g_assert_not_reached ();
6129 g_assert_not_reached ();
6134 #ifdef HEAVY_STATISTICS
6136 collect_store_remsets (RememberedSet *remset, mword *bumper)
6138 mword *p = remset->data;
6143 while (p < remset->store_next) {
6144 switch ((*p) & REMSET_TYPE_MASK) {
6145 case REMSET_LOCATION:
6148 ++stat_saved_remsets_1;
6150 if (*p == last1 || *p == last2) {
6151 ++stat_saved_remsets_2;
6169 case REMSET_ROOT_LOCATION:
6173 g_assert_not_reached ();
6177 g_assert_not_reached ();
6187 RememberedSet *remset;
6189 SgenThreadInfo *info;
6191 mword *addresses, *bumper, *p, *r;
6193 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6194 for (info = thread_table [i]; info; info = info->next) {
6195 for (remset = info->remset; remset; remset = remset->next)
6196 size += remset->store_next - remset->data;
6199 for (remset = freed_thread_remsets; remset; remset = remset->next)
6200 size += remset->store_next - remset->data;
6201 for (remset = global_remset; remset; remset = remset->next)
6202 size += remset->store_next - remset->data;
6204 bumper = addresses = get_internal_mem (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
6206 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6207 for (info = thread_table [i]; info; info = info->next) {
6208 for (remset = info->remset; remset; remset = remset->next)
6209 bumper = collect_store_remsets (remset, bumper);
6212 for (remset = global_remset; remset; remset = remset->next)
6213 bumper = collect_store_remsets (remset, bumper);
6214 for (remset = freed_thread_remsets; remset; remset = remset->next)
6215 bumper = collect_store_remsets (remset, bumper);
6217 g_assert (bumper <= addresses + size);
6219 stat_store_remsets += bumper - addresses;
6221 sort_addresses ((void**)addresses, bumper - addresses);
6224 while (r < bumper) {
6230 stat_store_remsets_unique += p - addresses;
6232 free_internal_mem (addresses, INTERNAL_MEM_STATISTICS);
6237 clear_thread_store_remset_buffer (SgenThreadInfo *info)
6239 *info->store_remset_buffer_index_addr = 0;
6240 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6244 scan_from_remsets (void *start_nursery, void *end_nursery)
6247 SgenThreadInfo *info;
6248 RememberedSet *remset;
6249 GenericStoreRememberedSet *store_remset;
6250 mword *p, *next_p, *store_pos;
6252 #ifdef HEAVY_STATISTICS
6256 /* the global one */
6257 for (remset = global_remset; remset; remset = remset->next) {
6258 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
6259 store_pos = remset->data;
6260 for (p = remset->data; p < remset->store_next; p = next_p) {
6263 next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
6266 * Clear global remsets of locations which no longer point to the
6267 * nursery. Otherwise, they could grow indefinitely between major
6270 ptr = (p [0] & ~REMSET_TYPE_MASK);
6271 if ((p [0] & REMSET_TYPE_MASK) == REMSET_LOCATION) {
6272 if (ptr_in_nursery (*(void**)ptr))
6273 *store_pos ++ = p [0];
6275 g_assert ((p [0] & REMSET_TYPE_MASK) == REMSET_OTHER);
6276 g_assert (p [1] == REMSET_ROOT_LOCATION);
6277 if (ptr_in_nursery (*(void**)ptr)) {
6278 *store_pos ++ = p [0];
6279 *store_pos ++ = p [1];
6284 /* Truncate the remset */
6285 remset->store_next = store_pos;
6288 /* the generic store ones */
6289 store_remset = generic_store_remsets;
6290 while (store_remset) {
6291 GenericStoreRememberedSet *next = store_remset->next;
6293 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6294 gpointer addr = store_remset->data [i];
6296 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE);
6299 free_internal_mem (store_remset, INTERNAL_MEM_STORE_REMSET);
6301 store_remset = next;
6303 generic_store_remsets = NULL;
6305 /* the per-thread ones */
6306 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6307 for (info = thread_table [i]; info; info = info->next) {
6308 RememberedSet *next;
6310 for (remset = info->remset; remset; remset = next) {
6311 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %zd\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
6312 for (p = remset->data; p < remset->store_next;) {
6313 p = handle_remset (p, start_nursery, end_nursery, FALSE);
6315 remset->store_next = remset->data;
6316 next = remset->next;
6317 remset->next = NULL;
6318 if (remset != info->remset) {
6319 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6320 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6323 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
6324 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE);
6325 clear_thread_store_remset_buffer (info);
6329 /* the freed thread ones */
6330 while (freed_thread_remsets) {
6331 RememberedSet *next;
6332 remset = freed_thread_remsets;
6333 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
6334 for (p = remset->data; p < remset->store_next;) {
6335 p = handle_remset (p, start_nursery, end_nursery, FALSE);
6337 next = remset->next;
6338 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6339 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6340 freed_thread_remsets = next;
6345 * Clear the info in the remembered sets: we're doing a major collection, so
6346 * the per-thread ones are not needed and the global ones will be reconstructed
6350 clear_remsets (void)
6353 SgenThreadInfo *info;
6354 RememberedSet *remset, *next;
6356 /* the global list */
6357 for (remset = global_remset; remset; remset = next) {
6358 remset->store_next = remset->data;
6359 next = remset->next;
6360 remset->next = NULL;
6361 if (remset != global_remset) {
6362 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6363 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6366 /* the generic store ones */
6367 while (generic_store_remsets) {
6368 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
6369 free_internal_mem (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
6370 generic_store_remsets = gs_next;
6372 /* the per-thread ones */
6373 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6374 for (info = thread_table [i]; info; info = info->next) {
6375 for (remset = info->remset; remset; remset = next) {
6376 remset->store_next = remset->data;
6377 next = remset->next;
6378 remset->next = NULL;
6379 if (remset != info->remset) {
6380 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6381 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6384 clear_thread_store_remset_buffer (info);
6388 /* the freed thread ones */
6389 while (freed_thread_remsets) {
6390 next = freed_thread_remsets->next;
6391 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
6392 free_internal_mem (freed_thread_remsets, INTERNAL_MEM_REMSET);
6393 freed_thread_remsets = next;
6398 * Clear the thread local TLAB variables for all threads.
6403 SgenThreadInfo *info;
6406 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6407 for (info = thread_table [i]; info; info = info->next) {
6408 /* A new TLAB will be allocated when the thread does its first allocation */
6409 *info->tlab_start_addr = NULL;
6410 *info->tlab_next_addr = NULL;
6411 *info->tlab_temp_end_addr = NULL;
6412 *info->tlab_real_end_addr = NULL;
6417 /* LOCKING: assumes the GC lock is held */
6418 static SgenThreadInfo*
6419 gc_register_current_thread (void *addr)
6422 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
6423 #ifndef HAVE_KW_THREAD
6424 SgenThreadInfo *__thread_info__ = info;
6430 memset (info, 0, sizeof (SgenThreadInfo));
6431 #ifndef HAVE_KW_THREAD
6432 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
6434 g_assert (!pthread_getspecific (thread_info_key));
6435 pthread_setspecific (thread_info_key, info);
6440 info->id = ARCH_GET_THREAD ();
6441 info->stop_count = -1;
6444 info->stack_start = NULL;
6445 info->tlab_start_addr = &TLAB_START;
6446 info->tlab_next_addr = &TLAB_NEXT;
6447 info->tlab_temp_end_addr = &TLAB_TEMP_END;
6448 info->tlab_real_end_addr = &TLAB_REAL_END;
6449 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
6450 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
6451 info->stopped_ip = NULL;
6452 info->stopped_domain = NULL;
6453 info->stopped_regs = NULL;
6455 binary_protocol_thread_register ((gpointer)info->id);
6457 #ifdef HAVE_KW_THREAD
6458 tlab_next_addr = &tlab_next;
6459 store_remset_buffer_index_addr = &store_remset_buffer_index;
6462 /* try to get it with attributes first */
6463 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
6467 pthread_attr_t attr;
6468 pthread_getattr_np (pthread_self (), &attr);
6469 pthread_attr_getstack (&attr, &sstart, &size);
6470 info->stack_start_limit = sstart;
6471 info->stack_end = (char*)sstart + size;
6472 pthread_attr_destroy (&attr);
6474 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
6475 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
6476 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
6479 /* FIXME: we assume the stack grows down */
6480 gsize stack_bottom = (gsize)addr;
6481 stack_bottom += 4095;
6482 stack_bottom &= ~4095;
6483 info->stack_end = (char*)stack_bottom;
6487 #ifdef HAVE_KW_THREAD
6488 stack_end = info->stack_end;
6491 /* hash into the table */
6492 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
6493 info->next = thread_table [hash];
6494 thread_table [hash] = info;
6496 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
6497 pthread_setspecific (remembered_set_key, info->remset);
6498 #ifdef HAVE_KW_THREAD
6499 remembered_set = info->remset;
6502 STORE_REMSET_BUFFER = get_internal_mem (sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE, INTERNAL_MEM_STORE_REMSET);
6503 STORE_REMSET_BUFFER_INDEX = 0;
6505 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
6507 if (gc_callbacks.thread_attach_func)
6508 info->runtime_data = gc_callbacks.thread_attach_func ();
6514 add_generic_store_remset_from_buffer (gpointer *buffer)
6516 GenericStoreRememberedSet *remset = get_internal_mem (sizeof (GenericStoreRememberedSet), INTERNAL_MEM_STORE_REMSET);
6517 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6518 remset->next = generic_store_remsets;
6519 generic_store_remsets = remset;
6523 unregister_current_thread (void)
6526 SgenThreadInfo *prev = NULL;
6528 RememberedSet *rset;
6529 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6531 binary_protocol_thread_unregister ((gpointer)id);
6533 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6534 p = thread_table [hash];
6536 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6537 while (!ARCH_THREAD_EQUALS (p->id, id)) {
6542 thread_table [hash] = p->next;
6544 prev->next = p->next;
6547 if (freed_thread_remsets) {
6548 for (rset = p->remset; rset->next; rset = rset->next)
6550 rset->next = freed_thread_remsets;
6551 freed_thread_remsets = p->remset;
6553 freed_thread_remsets = p->remset;
6556 if (*p->store_remset_buffer_index_addr)
6557 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6558 free_internal_mem (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6563 unregister_thread (void *k)
6565 g_assert (!mono_domain_get ());
6567 unregister_current_thread ();
6572 mono_gc_register_thread (void *baseptr)
6574 SgenThreadInfo *info;
6578 info = thread_info_lookup (ARCH_GET_THREAD ());
6580 info = gc_register_current_thread (baseptr);
6582 return info != NULL;
6585 #if USE_PTHREAD_INTERCEPT
6587 #undef pthread_create
6589 #undef pthread_detach
6592 void *(*start_routine) (void *);
6595 MonoSemType registered;
6596 } SgenThreadStartInfo;
6599 gc_start_thread (void *arg)
6601 SgenThreadStartInfo *start_info = arg;
6602 SgenThreadInfo* info;
6603 void *t_arg = start_info->arg;
6604 void *(*start_func) (void*) = start_info->start_routine;
6609 info = gc_register_current_thread (&result);
6611 post_result = MONO_SEM_POST (&(start_info->registered));
6612 g_assert (!post_result);
6613 result = start_func (t_arg);
6614 g_assert (!mono_domain_get ());
6616 * this is done by the pthread key dtor
6618 unregister_current_thread ();
6626 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6628 SgenThreadStartInfo *start_info;
6631 start_info = malloc (sizeof (SgenThreadStartInfo));
6634 result = MONO_SEM_INIT (&(start_info->registered), 0);
6636 start_info->arg = arg;
6637 start_info->start_routine = start_routine;
6639 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6641 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6642 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6645 MONO_SEM_DESTROY (&(start_info->registered));
6651 mono_gc_pthread_join (pthread_t thread, void **retval)
6653 return pthread_join (thread, retval);
6657 mono_gc_pthread_detach (pthread_t thread)
6659 return pthread_detach (thread);
6662 #endif /* USE_PTHREAD_INTERCEPT */
6665 * ######################################################################
6666 * ######## Write barriers
6667 * ######################################################################
6670 static RememberedSet*
6671 alloc_remset (int size, gpointer id) {
6672 RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6673 res->store_next = res->data;
6674 res->end_set = res->data + size;
6676 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6681 * Note: the write barriers first do the needed GC work and then do the actual store:
6682 * this way the value is visible to the conservative GC scan after the write barrier
6683 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6684 * the conservative scan, otherwise by the remembered set scan.
6687 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6691 HEAVY_STAT (++stat_wbarrier_set_field);
6692 if (ptr_in_nursery (field_ptr)) {
6693 *(void**)field_ptr = value;
6696 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6698 rs = REMEMBERED_SET;
6699 if (rs->store_next < rs->end_set) {
6700 *(rs->store_next++) = (mword)field_ptr;
6701 *(void**)field_ptr = value;
6705 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6706 rs->next = REMEMBERED_SET;
6707 REMEMBERED_SET = rs;
6708 #ifdef HAVE_KW_THREAD
6709 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6711 *(rs->store_next++) = (mword)field_ptr;
6712 *(void**)field_ptr = value;
6717 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6721 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6722 if (ptr_in_nursery (slot_ptr)) {
6723 *(void**)slot_ptr = value;
6726 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6728 rs = REMEMBERED_SET;
6729 if (rs->store_next < rs->end_set) {
6730 *(rs->store_next++) = (mword)slot_ptr;
6731 *(void**)slot_ptr = value;
6735 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6736 rs->next = REMEMBERED_SET;
6737 REMEMBERED_SET = rs;
6738 #ifdef HAVE_KW_THREAD
6739 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6741 *(rs->store_next++) = (mword)slot_ptr;
6742 *(void**)slot_ptr = value;
6747 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6751 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6753 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6754 if (ptr_in_nursery (dest_ptr)) {
6758 rs = REMEMBERED_SET;
6759 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6760 if (rs->store_next + 1 < rs->end_set) {
6761 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6762 *(rs->store_next++) = count;
6766 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6767 rs->next = REMEMBERED_SET;
6768 REMEMBERED_SET = rs;
6769 #ifdef HAVE_KW_THREAD
6770 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6772 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6773 *(rs->store_next++) = count;
6778 find_object_for_ptr_in_area (char *ptr, char *start, char *end)
6780 while (start < end) {
6783 if (!*(void**)start) {
6784 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
6790 #define SCAN_OBJECT_NOSCAN
6791 #include "sgen-scan-object.h"
6793 if (ptr >= old_start && ptr < start)
6800 static char *found_obj;
6803 find_object_for_ptr_in_pinned_chunk_callback (PinnedChunk *chunk, char *obj, size_t size, char *ptr)
6805 if (ptr >= obj && ptr < obj + size) {
6806 g_assert (!found_obj);
6811 /* for use in the debugger */
6812 char* find_object_for_ptr (char *ptr);
6814 find_object_for_ptr (char *ptr)
6816 GCMemSection *section;
6819 for (section = section_list; section; section = section->block.next) {
6820 if (ptr >= section->data && ptr < section->end_data)
6821 return find_object_for_ptr_in_area (ptr, section->data, section->end_data);
6824 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
6825 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
6826 return bigobj->data;
6830 scan_pinned_objects ((ScanPinnedObjectCallbackFunc)find_object_for_ptr_in_pinned_chunk_callback, ptr);
6835 evacuate_remset_buffer (void)
6840 buffer = STORE_REMSET_BUFFER;
6842 add_generic_store_remset_from_buffer (buffer);
6843 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6845 STORE_REMSET_BUFFER_INDEX = 0;
6849 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6855 HEAVY_STAT (++stat_wbarrier_generic_store);
6857 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6858 /* FIXME: ptr_in_heap must be called with the GC lock held */
6859 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6860 char *start = find_object_for_ptr (ptr);
6861 MonoObject *value = *(MonoObject**)ptr;
6865 MonoObject *obj = (MonoObject*)start;
6866 if (obj->vtable->domain != value->vtable->domain)
6867 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6875 if (*(gpointer*)ptr)
6876 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6878 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6879 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6884 buffer = STORE_REMSET_BUFFER;
6885 index = STORE_REMSET_BUFFER_INDEX;
6886 /* This simple optimization eliminates a sizable portion of
6887 entries. Comparing it to the last but one entry as well
6888 doesn't eliminate significantly more entries. */
6889 if (buffer [index] == ptr) {
6894 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6895 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6898 if (index >= STORE_REMSET_BUFFER_SIZE) {
6899 evacuate_remset_buffer ();
6900 index = STORE_REMSET_BUFFER_INDEX;
6901 g_assert (index == 0);
6904 buffer [index] = ptr;
6905 STORE_REMSET_BUFFER_INDEX = index;
6911 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6913 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6914 *(void**)ptr = value;
6915 if (ptr_in_nursery (value))
6916 mono_gc_wbarrier_generic_nostore (ptr);
6920 mono_gc_wbarrier_set_root (gpointer ptr, MonoObject *value)
6924 HEAVY_STAT (++stat_wbarrier_set_root);
6925 if (ptr_in_nursery (ptr))
6927 DEBUG (8, fprintf (gc_debug_file, "Adding root remset at %p (%s)\n", ptr, value ? safe_name (value) : "null"));
6929 rs = REMEMBERED_SET;
6930 if (rs->store_next + 2 < rs->end_set) {
6931 *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
6932 *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
6933 *(void**)ptr = value;
6936 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6937 rs->next = REMEMBERED_SET;
6938 REMEMBERED_SET = rs;
6939 #ifdef HAVE_KW_THREAD
6940 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6942 *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
6943 *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
6945 *(void**)ptr = value;
6949 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6953 HEAVY_STAT (++stat_wbarrier_value_copy);
6954 g_assert (klass->valuetype);
6956 memmove (dest, src, count * mono_class_value_size (klass, NULL));
6957 rs = REMEMBERED_SET;
6958 if (ptr_in_nursery (dest) || ptr_on_stack (dest)) {
6962 g_assert (klass->gc_descr_inited);
6963 DEBUG (8, fprintf (gc_debug_file, "Adding value remset at %p, count %d, descr %p for class %s (%p)\n", dest, count, klass->gc_descr, klass->name, klass));
6965 if (rs->store_next + 3 < rs->end_set) {
6966 *(rs->store_next++) = (mword)dest | REMSET_OTHER;
6967 *(rs->store_next++) = (mword)REMSET_VTYPE;
6968 *(rs->store_next++) = (mword)klass->gc_descr;
6969 *(rs->store_next++) = (mword)count;
6973 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6974 rs->next = REMEMBERED_SET;
6975 REMEMBERED_SET = rs;
6976 #ifdef HAVE_KW_THREAD
6977 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6979 *(rs->store_next++) = (mword)dest | REMSET_OTHER;
6980 *(rs->store_next++) = (mword)REMSET_VTYPE;
6981 *(rs->store_next++) = (mword)klass->gc_descr;
6982 *(rs->store_next++) = (mword)count;
6987 * mono_gc_wbarrier_object_copy:
6989 * Write barrier to call when obj is the result of a clone or copy of an object.
6992 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6998 HEAVY_STAT (++stat_wbarrier_object_copy);
6999 rs = REMEMBERED_SET;
7000 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
7001 size = mono_object_class (obj)->instance_size;
7003 /* do not copy the sync state */
7004 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
7005 size - sizeof (MonoObject));
7006 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
7010 if (rs->store_next < rs->end_set) {
7011 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
7015 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
7016 rs->next = REMEMBERED_SET;
7017 REMEMBERED_SET = rs;
7018 #ifdef HAVE_KW_THREAD
7019 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
7021 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
7026 * ######################################################################
7027 * ######## Collector debugging
7028 * ######################################################################
7031 const char*descriptor_types [] = {
7043 describe_ptr (char *ptr)
7045 GCMemSection *section;
7050 if (ptr_in_nursery (ptr)) {
7051 printf ("Pointer inside nursery.\n");
7053 for (section = section_list; section;) {
7054 if (ptr >= section->data && ptr < section->data + section->size)
7056 section = section->block.next;
7060 printf ("Pointer inside oldspace.\n");
7061 } else if (obj_is_from_pinned_alloc (ptr)) {
7062 printf ("Pointer is inside a pinned chunk.\n");
7064 printf ("Pointer unknown.\n");
7069 if (object_is_pinned (ptr))
7070 printf ("Object is pinned.\n");
7072 if (object_is_forwarded (ptr))
7073 printf ("Object is forwared.\n");
7075 // FIXME: Handle pointers to the inside of objects
7076 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
7078 printf ("VTable: %p\n", vtable);
7079 if (vtable == NULL) {
7080 printf ("VTable is invalid (empty).\n");
7083 if (ptr_in_nursery (vtable)) {
7084 printf ("VTable is invalid (points inside nursery).\n");
7087 printf ("Class: %s\n", vtable->klass->name);
7089 desc = ((GCVTable*)vtable)->desc;
7090 printf ("Descriptor: %lx\n", (long)desc);
7093 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
7097 find_in_remset_loc (mword *p, char *addr, gboolean *found)
7103 switch ((*p) & REMSET_TYPE_MASK) {
7104 case REMSET_LOCATION:
7105 if (*p == (mword)addr)
7109 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
7111 if ((void**)addr >= ptr && (void**)addr < ptr + count)
7115 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
7116 count = safe_object_get_size ((MonoObject*)ptr);
7117 count += (ALLOC_ALIGN - 1);
7118 count &= (ALLOC_ALIGN - 1);
7119 count /= sizeof (mword);
7120 if ((void**)addr >= ptr && (void**)addr < ptr + count)
7123 case REMSET_OTHER: {
7126 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
7130 switch (desc & 0x7) {
7131 case DESC_TYPE_RUN_LENGTH:
7132 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
7134 case DESC_TYPE_SMALL_BITMAP:
7135 OBJ_BITMAP_SIZE (skip_size, desc, start);
7139 g_assert_not_reached ();
7142 /* The descriptor includes the size of MonoObject */
7143 skip_size -= sizeof (MonoObject);
7145 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
7149 case REMSET_ROOT_LOCATION:
7152 g_assert_not_reached ();
7157 g_assert_not_reached ();
7163 * Return whenever ADDR occurs in the remembered sets
7166 find_in_remsets (char *addr)
7169 SgenThreadInfo *info;
7170 RememberedSet *remset;
7171 GenericStoreRememberedSet *store_remset;
7173 gboolean found = FALSE;
7175 /* the global one */
7176 for (remset = global_remset; remset; remset = remset->next) {
7177 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
7178 for (p = remset->data; p < remset->store_next;) {
7179 p = find_in_remset_loc (p, addr, &found);
7185 /* the generic store ones */
7186 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
7187 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
7188 if (store_remset->data [i] == addr)
7193 /* the per-thread ones */
7194 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
7195 for (info = thread_table [i]; info; info = info->next) {
7197 for (remset = info->remset; remset; remset = remset->next) {
7198 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %zd\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
7199 for (p = remset->data; p < remset->store_next;) {
7200 p = find_in_remset_loc (p, addr, &found);
7205 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
7206 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
7212 /* the freed thread ones */
7213 for (remset = freed_thread_remsets; remset; remset = remset->next) {
7214 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
7215 for (p = remset->data; p < remset->store_next;) {
7216 p = find_in_remset_loc (p, addr, &found);
7225 static gboolean missing_remsets;
7228 * We let a missing remset slide if the target object is pinned,
7229 * because the store might have happened but the remset not yet added,
7230 * but in that case the target must be pinned. We might theoretically
7231 * miss some missing remsets this way, but it's very unlikely.
7234 #define HANDLE_PTR(ptr,obj) do { \
7235 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
7236 if (!find_in_remsets ((char*)(ptr))) { \
7237 fprintf (gc_debug_file, "Oldspace->newspace reference %p at offset %zd in object %p (%s.%s) not found in remsets.\n", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
7238 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
7239 if (!object_is_pinned (*(ptr))) \
7240 missing_remsets = TRUE; \
7246 * Check that each object reference inside the area which points into the nursery
7247 * can be found in the remembered sets.
7249 static void __attribute__((noinline))
7250 check_remsets_for_area (char *start, char *end)
7253 int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
7254 while (start < end) {
7255 if (!*(void**)start) {
7256 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
7259 vt = (GCVTable*)LOAD_VTABLE (start);
7260 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
7262 MonoObject *obj = (MonoObject*)start;
7263 g_print ("found at %p (0x%lx): %s.%s\n", start, (long)vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
7266 #define SCAN_OBJECT_ACTION COUNT_OBJECT_TYPES
7267 #include "sgen-scan-object.h"
7272 * Perform consistency check of the heap.
7274 * Assumes the world is stopped.
7277 check_consistency (void)
7279 GCMemSection *section;
7281 // Need to add more checks
7282 // FIXME: Create a general heap enumeration function and use that
7284 missing_remsets = FALSE;
7286 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
7288 // Check that oldspace->newspace pointers are registered with the collector
7289 for (section = section_list; section; section = section->block.next) {
7290 if (section->block.role == MEMORY_ROLE_GEN0)
7292 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)));
7293 check_remsets_for_area (section->data, section->next_data);
7296 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
7298 #ifdef BINARY_PROTOCOL
7299 if (!binary_protocol_file)
7301 g_assert (!missing_remsets);
7304 /* Check that the reference is valid */
7306 #define HANDLE_PTR(ptr,obj) do { \
7308 g_assert (safe_name (*(ptr)) != NULL); \
7315 * Perform consistency check on an object. Currently we only check that the
7316 * reference fields are valid.
7319 check_object (char *start)
7324 #include "sgen-scan-object.h"
7330 * ######################################################################
7331 * ######## Other mono public interface functions.
7332 * ######################################################################
7336 mono_gc_collect (int generation)
7340 if (generation == 0) {
7341 collect_nursery (0);
7343 major_collection ("user request");
7350 mono_gc_max_generation (void)
7356 mono_gc_collection_count (int generation)
7358 if (generation == 0)
7359 return num_minor_gcs;
7360 return num_major_gcs;
7364 mono_gc_get_used_size (void)
7367 GCMemSection *section;
7369 tot = los_memory_usage;
7370 for (section = section_list; section; section = section->block.next) {
7371 /* this is approximate... */
7372 tot += section->next_data - section->data;
7374 /* FIXME: account for pinned objects */
7380 mono_gc_get_heap_size (void)
7386 mono_gc_disable (void)
7394 mono_gc_enable (void)
7402 mono_gc_get_los_limit (void)
7404 return MAX_SMALL_OBJ_SIZE;
7408 mono_object_is_alive (MonoObject* o)
7414 mono_gc_get_generation (MonoObject *obj)
7416 if (ptr_in_nursery (obj))
7422 mono_gc_enable_events (void)
7427 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7430 mono_gc_register_disappearing_link (obj, link_addr, track);
7435 mono_gc_weak_link_remove (void **link_addr)
7438 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7443 mono_gc_weak_link_get (void **link_addr)
7447 return (MonoObject*) REVEAL_POINTER (*link_addr);
7451 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7453 if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7454 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7456 mword complex = alloc_complex_descriptor (bitmap, numbits);
7457 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7462 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7466 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7467 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7468 user_descriptors [user_descriptors_next ++] = marker;
7474 mono_gc_alloc_fixed (size_t size, void *descr)
7476 /* FIXME: do a single allocation */
7477 void *res = calloc (1, size);
7480 if (!mono_gc_register_root (res, size, descr)) {
7488 mono_gc_free_fixed (void* addr)
7490 mono_gc_deregister_root (addr);
7495 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7499 result = func (data);
7500 UNLOCK_INTERRUPTION;
7505 mono_gc_is_gc_thread (void)
7509 result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
7516 /* Tries to extract a number from the passed string, taking in to account m, k
7519 parse_environment_string_extract_number (gchar *str, glong *out)
7522 int len = strlen (str), shift = 0;
7524 gboolean is_suffix = FALSE;
7527 switch (str [len - 1]) {
7538 suffix = str [len - 1];
7543 val = strtol (str, &endptr, 10);
7545 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
7546 || (errno != 0 && val == 0) || (endptr == str))
7550 if (*(endptr + 1)) /* Invalid string. */
7562 mono_gc_base_init (void)
7566 struct sigaction sinfo;
7568 LOCK_INIT (gc_mutex);
7570 if (gc_initialized) {
7574 pagesize = mono_pagesize ();
7575 gc_debug_file = stderr;
7579 if ((env = getenv ("MONO_GC_PARAMS"))) {
7580 if (g_str_has_prefix (env, "nursery-size")) {
7583 while (env [index] && env [index++] != '=')
7585 if (env [index] && parse_environment_string_extract_number (env
7587 default_nursery_size = val;
7588 #ifdef ALIGN_NURSERY
7589 if ((val & (val - 1))) {
7590 fprintf (stderr, "The nursery size must be a power of two.\n");
7594 default_nursery_bits = 0;
7595 while (1 << (++ default_nursery_bits) != default_nursery_size)
7599 fprintf (stderr, "nursery-size must be an integer.\n");
7603 fprintf (stderr, "MONO_GC_PARAMS must be of the form 'nursery-size=N' (where N is an integer, possibly with a k, m or a g suffix).\n");
7610 nursery_size = DEFAULT_NURSERY_SIZE;
7612 minor_collection_section_allowance = MIN_MINOR_COLLECTION_SECTION_ALLOWANCE;
7614 if ((env = getenv ("MONO_GC_DEBUG"))) {
7615 opts = g_strsplit (env, ",", -1);
7616 for (ptr = opts; ptr && *ptr; ptr ++) {
7618 if (opt [0] >= '0' && opt [0] <= '9') {
7619 gc_debug_level = atoi (opt);
7624 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7625 gc_debug_file = fopen (rf, "wb");
7627 gc_debug_file = stderr;
7630 } else if (!strcmp (opt, "collect-before-allocs")) {
7631 collect_before_allocs = TRUE;
7632 } else if (!strcmp (opt, "check-at-minor-collections")) {
7633 consistency_check_at_minor_collection = TRUE;
7634 } else if (!strcmp (opt, "xdomain-checks")) {
7635 xdomain_checks = TRUE;
7636 } else if (!strcmp (opt, "clear-at-gc")) {
7637 nursery_clear_policy = CLEAR_AT_GC;
7638 } else if (!strcmp (opt, "conservative-stack-mark")) {
7639 conservative_stack_mark = TRUE;
7640 } else if (!strcmp (opt, "check-scan-starts")) {
7641 do_scan_starts_check = TRUE;
7642 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7643 char *filename = strchr (opt, '=') + 1;
7644 nursery_clear_policy = CLEAR_AT_GC;
7645 heap_dump_file = fopen (filename, "w");
7647 fprintf (heap_dump_file, "<sgen-dump>\n");
7648 #ifdef BINARY_PROTOCOL
7649 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7650 char *filename = strchr (opt, '=') + 1;
7651 binary_protocol_file = fopen (filename, "w");
7654 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7655 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7656 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
7663 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7664 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7666 sigfillset (&sinfo.sa_mask);
7667 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7668 sinfo.sa_sigaction = suspend_handler;
7669 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7670 g_error ("failed sigaction");
7673 sinfo.sa_handler = restart_handler;
7674 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7675 g_error ("failed sigaction");
7678 sigfillset (&suspend_signal_mask);
7679 sigdelset (&suspend_signal_mask, restart_signal_num);
7681 global_remset = alloc_remset (1024, NULL);
7682 global_remset->next = NULL;
7684 pthread_key_create (&remembered_set_key, unregister_thread);
7686 #ifndef HAVE_KW_THREAD
7687 pthread_key_create (&thread_info_key, NULL);
7690 gc_initialized = TRUE;
7692 mono_gc_register_thread (&sinfo);
7696 mono_gc_get_suspend_signal (void)
7698 return suspend_signal_num;
7708 #ifdef HAVE_KW_THREAD
7709 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7710 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7711 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7712 mono_mb_emit_i4 ((mb), (offset)); \
7715 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7716 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7717 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7718 mono_mb_emit_i4 ((mb), thread_info_key); \
7719 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7720 mono_mb_emit_byte ((mb), CEE_ADD); \
7721 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7725 #ifdef MANAGED_ALLOCATION
7726 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7727 * for each class. This is currently not easy to do, as it is hard to generate basic
7728 * blocks + branches, but it is easy with the linear IL codebase.
7730 * For this to work we'd need to solve the TLAB race, first. Now we
7731 * require the allocator to be in a few known methods to make sure
7732 * that they are executed atomically via the restart mechanism.
7735 create_allocator (int atype)
7737 int p_var, size_var;
7738 guint32 slowpath_branch, max_size_branch;
7739 MonoMethodBuilder *mb;
7741 MonoMethodSignature *csig;
7742 static gboolean registered = FALSE;
7743 int tlab_next_addr_var, new_next_var;
7745 const char *name = NULL;
7746 AllocatorWrapperInfo *info;
7748 #ifdef HAVE_KW_THREAD
7749 int tlab_next_addr_offset = -1;
7750 int tlab_temp_end_offset = -1;
7752 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7753 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7755 g_assert (tlab_next_addr_offset != -1);
7756 g_assert (tlab_temp_end_offset != -1);
7760 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7761 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7765 if (atype == ATYPE_SMALL) {
7767 name = "AllocSmall";
7768 } else if (atype == ATYPE_NORMAL) {
7771 } else if (atype == ATYPE_VECTOR) {
7773 name = "AllocVector";
7775 g_assert_not_reached ();
7778 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7779 csig->ret = &mono_defaults.object_class->byval_arg;
7780 for (i = 0; i < num_params; ++i)
7781 csig->params [i] = &mono_defaults.int_class->byval_arg;
7783 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7784 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7785 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7786 /* size = vtable->klass->instance_size; */
7787 mono_mb_emit_ldarg (mb, 0);
7788 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7789 mono_mb_emit_byte (mb, CEE_ADD);
7790 mono_mb_emit_byte (mb, CEE_LDIND_I);
7791 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7792 mono_mb_emit_byte (mb, CEE_ADD);
7793 /* FIXME: assert instance_size stays a 4 byte integer */
7794 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7795 mono_mb_emit_stloc (mb, size_var);
7796 } else if (atype == ATYPE_VECTOR) {
7797 MonoExceptionClause *clause;
7799 MonoClass *oom_exc_class;
7802 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7803 mono_mb_emit_ldarg (mb, 1);
7804 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7805 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7806 mono_mb_emit_exception (mb, "OverflowException", NULL);
7807 mono_mb_patch_short_branch (mb, pos);
7809 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7810 clause->try_offset = mono_mb_get_label (mb);
7812 /* vtable->klass->sizes.element_size */
7813 mono_mb_emit_ldarg (mb, 0);
7814 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7815 mono_mb_emit_byte (mb, CEE_ADD);
7816 mono_mb_emit_byte (mb, CEE_LDIND_I);
7817 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7818 mono_mb_emit_byte (mb, CEE_ADD);
7819 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7822 mono_mb_emit_ldarg (mb, 1);
7823 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7824 /* + sizeof (MonoArray) */
7825 mono_mb_emit_icon (mb, sizeof (MonoArray));
7826 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7827 mono_mb_emit_stloc (mb, size_var);
7829 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7832 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7833 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7834 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7835 "System", "OverflowException");
7836 g_assert (clause->data.catch_class);
7837 clause->handler_offset = mono_mb_get_label (mb);
7839 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7840 "System", "OutOfMemoryException");
7841 g_assert (oom_exc_class);
7842 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7845 mono_mb_emit_byte (mb, CEE_POP);
7846 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7847 mono_mb_emit_byte (mb, CEE_THROW);
7849 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7850 mono_mb_set_clauses (mb, 1, clause);
7851 mono_mb_patch_branch (mb, pos_leave);
7854 g_assert_not_reached ();
7857 /* size += ALLOC_ALIGN - 1; */
7858 mono_mb_emit_ldloc (mb, size_var);
7859 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7860 mono_mb_emit_byte (mb, CEE_ADD);
7861 /* size &= ~(ALLOC_ALIGN - 1); */
7862 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7863 mono_mb_emit_byte (mb, CEE_AND);
7864 mono_mb_emit_stloc (mb, size_var);
7866 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7867 if (atype != ATYPE_SMALL) {
7868 mono_mb_emit_ldloc (mb, size_var);
7869 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7870 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7874 * We need to modify tlab_next, but the JIT only supports reading, so we read
7875 * another tls var holding its address instead.
7878 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7879 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7880 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7881 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7883 /* p = (void**)tlab_next; */
7884 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7885 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7886 mono_mb_emit_byte (mb, CEE_LDIND_I);
7887 mono_mb_emit_stloc (mb, p_var);
7889 /* new_next = (char*)p + size; */
7890 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7891 mono_mb_emit_ldloc (mb, p_var);
7892 mono_mb_emit_ldloc (mb, size_var);
7893 mono_mb_emit_byte (mb, CEE_CONV_I);
7894 mono_mb_emit_byte (mb, CEE_ADD);
7895 mono_mb_emit_stloc (mb, new_next_var);
7897 /* tlab_next = new_next */
7898 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7899 mono_mb_emit_ldloc (mb, new_next_var);
7900 mono_mb_emit_byte (mb, CEE_STIND_I);
7902 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7903 mono_mb_emit_ldloc (mb, new_next_var);
7904 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7905 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7908 if (atype != ATYPE_SMALL)
7909 mono_mb_patch_short_branch (mb, max_size_branch);
7911 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7912 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7914 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7915 mono_mb_emit_ldarg (mb, 0);
7916 mono_mb_emit_ldloc (mb, size_var);
7917 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7918 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7919 } else if (atype == ATYPE_VECTOR) {
7920 mono_mb_emit_ldarg (mb, 1);
7921 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7923 g_assert_not_reached ();
7925 mono_mb_emit_byte (mb, CEE_RET);
7928 mono_mb_patch_short_branch (mb, slowpath_branch);
7930 /* FIXME: Memory barrier */
7933 mono_mb_emit_ldloc (mb, p_var);
7934 mono_mb_emit_ldarg (mb, 0);
7935 mono_mb_emit_byte (mb, CEE_STIND_I);
7937 if (atype == ATYPE_VECTOR) {
7938 /* arr->max_length = max_length; */
7939 mono_mb_emit_ldloc (mb, p_var);
7940 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7941 mono_mb_emit_ldarg (mb, 1);
7942 mono_mb_emit_byte (mb, CEE_STIND_I);
7946 mono_mb_emit_ldloc (mb, p_var);
7947 mono_mb_emit_byte (mb, CEE_RET);
7949 res = mono_mb_create_method (mb, csig, 8);
7951 mono_method_get_header (res)->init_locals = FALSE;
7953 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7954 info->alloc_type = atype;
7955 mono_marshal_set_wrapper_info (res, info);
7961 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7962 static MonoMethod *write_barrier_method;
7965 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7973 ji = mono_jit_info_table_find (domain, ip);
7976 method = ji->method;
7978 if (method == write_barrier_method)
7980 for (i = 0; i < ATYPE_NUM; ++i)
7981 if (method == alloc_method_cache [i])
7987 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7988 * The signature of the called method is:
7989 * object allocate (MonoVTable *vtable)
7992 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7994 #ifdef MANAGED_ALLOCATION
7995 MonoClass *klass = vtable->klass;
7997 #ifdef HAVE_KW_THREAD
7998 int tlab_next_offset = -1;
7999 int tlab_temp_end_offset = -1;
8000 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
8001 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
8003 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
8007 if (!mono_runtime_has_tls_get ())
8009 if (klass->instance_size > tlab_size)
8011 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
8015 if (klass->byval_arg.type == MONO_TYPE_STRING)
8017 if (collect_before_allocs)
8020 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
8021 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
8023 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
8030 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
8032 #ifdef MANAGED_ALLOCATION
8033 MonoClass *klass = vtable->klass;
8035 #ifdef HAVE_KW_THREAD
8036 int tlab_next_offset = -1;
8037 int tlab_temp_end_offset = -1;
8038 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
8039 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
8041 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
8047 if (!mono_runtime_has_tls_get ())
8049 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
8051 if (collect_before_allocs)
8053 g_assert (!klass->has_finalize && !klass->marshalbyref);
8055 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
8062 mono_gc_get_managed_allocator_by_type (int atype)
8064 #ifdef MANAGED_ALLOCATION
8067 if (!mono_runtime_has_tls_get ())
8070 mono_loader_lock ();
8071 res = alloc_method_cache [atype];
8073 res = alloc_method_cache [atype] = create_allocator (atype);
8074 mono_loader_unlock ();
8082 mono_gc_get_managed_allocator_types (void)
8089 mono_gc_get_write_barrier (void)
8092 MonoMethodBuilder *mb;
8093 MonoMethodSignature *sig;
8094 #ifdef MANAGED_WBARRIER
8095 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
8096 #ifndef ALIGN_NURSERY
8097 int label_continue_1, label_continue_2, label_no_wb_5;
8098 int dereferenced_var;
8100 int buffer_var, buffer_index_var, dummy_var;
8102 #ifdef HAVE_KW_THREAD
8103 int stack_end_offset = -1, store_remset_buffer_offset = -1;
8104 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
8106 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
8107 g_assert (stack_end_offset != -1);
8108 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
8109 g_assert (store_remset_buffer_offset != -1);
8110 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
8111 g_assert (store_remset_buffer_index_offset != -1);
8112 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8113 g_assert (store_remset_buffer_index_addr_offset != -1);
8117 // FIXME: Maybe create a separate version for ctors (the branch would be
8118 // correctly predicted more times)
8119 if (write_barrier_method)
8120 return write_barrier_method;
8122 /* Create the IL version of mono_gc_barrier_generic_store () */
8123 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
8124 sig->ret = &mono_defaults.void_class->byval_arg;
8125 sig->params [0] = &mono_defaults.int_class->byval_arg;
8127 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
8129 #ifdef MANAGED_WBARRIER
8130 if (mono_runtime_has_tls_get ()) {
8131 #ifdef ALIGN_NURSERY
8132 // if (ptr_in_nursery (ptr)) return;
8134 * Masking out the bits might be faster, but we would have to use 64 bit
8135 * immediates, which might be slower.
8137 mono_mb_emit_ldarg (mb, 0);
8138 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8139 mono_mb_emit_byte (mb, CEE_SHR_UN);
8140 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8141 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
8143 // if (!ptr_in_nursery (*ptr)) return;
8144 mono_mb_emit_ldarg (mb, 0);
8145 mono_mb_emit_byte (mb, CEE_LDIND_I);
8146 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8147 mono_mb_emit_byte (mb, CEE_SHR_UN);
8148 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8149 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
8152 // if (ptr < (nursery_start)) goto continue;
8153 mono_mb_emit_ldarg (mb, 0);
8154 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8155 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
8157 // if (ptr >= nursery_real_end)) goto continue;
8158 mono_mb_emit_ldarg (mb, 0);
8159 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
8160 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
8163 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
8166 mono_mb_patch_branch (mb, label_continue_1);
8167 mono_mb_patch_branch (mb, label_continue_2);
8169 // Dereference and store in local var
8170 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8171 mono_mb_emit_ldarg (mb, 0);
8172 mono_mb_emit_byte (mb, CEE_LDIND_I);
8173 mono_mb_emit_stloc (mb, dereferenced_var);
8175 // if (*ptr < nursery_start) return;
8176 mono_mb_emit_ldloc (mb, dereferenced_var);
8177 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8178 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
8180 // if (*ptr >= nursery_end) return;
8181 mono_mb_emit_ldloc (mb, dereferenced_var);
8182 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
8183 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
8186 // if (ptr >= stack_end) goto need_wb;
8187 mono_mb_emit_ldarg (mb, 0);
8188 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
8189 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
8191 // if (ptr >= stack_start) return;
8192 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8193 mono_mb_emit_ldarg (mb, 0);
8194 mono_mb_emit_ldloc_addr (mb, dummy_var);
8195 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
8198 mono_mb_patch_branch (mb, label_need_wb);
8200 // buffer = STORE_REMSET_BUFFER;
8201 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8202 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
8203 mono_mb_emit_stloc (mb, buffer_var);
8205 // buffer_index = STORE_REMSET_BUFFER_INDEX;
8206 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8207 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
8208 mono_mb_emit_stloc (mb, buffer_index_var);
8210 // if (buffer [buffer_index] == ptr) return;
8211 mono_mb_emit_ldloc (mb, buffer_var);
8212 mono_mb_emit_ldloc (mb, buffer_index_var);
8213 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8214 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8215 mono_mb_emit_byte (mb, CEE_SHL);
8216 mono_mb_emit_byte (mb, CEE_ADD);
8217 mono_mb_emit_byte (mb, CEE_LDIND_I);
8218 mono_mb_emit_ldarg (mb, 0);
8219 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
8222 mono_mb_emit_ldloc (mb, buffer_index_var);
8223 mono_mb_emit_icon (mb, 1);
8224 mono_mb_emit_byte (mb, CEE_ADD);
8225 mono_mb_emit_stloc (mb, buffer_index_var);
8227 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
8228 mono_mb_emit_ldloc (mb, buffer_index_var);
8229 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
8230 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
8232 // buffer [buffer_index] = ptr;
8233 mono_mb_emit_ldloc (mb, buffer_var);
8234 mono_mb_emit_ldloc (mb, buffer_index_var);
8235 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8236 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8237 mono_mb_emit_byte (mb, CEE_SHL);
8238 mono_mb_emit_byte (mb, CEE_ADD);
8239 mono_mb_emit_ldarg (mb, 0);
8240 mono_mb_emit_byte (mb, CEE_STIND_I);
8242 // STORE_REMSET_BUFFER_INDEX = buffer_index;
8243 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8244 mono_mb_emit_ldloc (mb, buffer_index_var);
8245 mono_mb_emit_byte (mb, CEE_STIND_I);
8248 mono_mb_patch_branch (mb, label_no_wb_1);
8249 mono_mb_patch_branch (mb, label_no_wb_2);
8250 mono_mb_patch_branch (mb, label_no_wb_3);
8251 mono_mb_patch_branch (mb, label_no_wb_4);
8252 #ifndef ALIGN_NURSERY
8253 mono_mb_patch_branch (mb, label_no_wb_5);
8255 mono_mb_emit_byte (mb, CEE_RET);
8258 mono_mb_patch_branch (mb, label_slow_path);
8262 mono_mb_emit_ldarg (mb, 0);
8263 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
8264 mono_mb_emit_byte (mb, CEE_RET);
8266 res = mono_mb_create_method (mb, sig, 16);
8269 mono_loader_lock ();
8270 if (write_barrier_method) {
8271 /* Already created */
8272 mono_free_method (res);
8274 /* double-checked locking */
8275 mono_memory_barrier ();
8276 write_barrier_method = res;
8278 mono_loader_unlock ();
8280 return write_barrier_method;
8283 #endif /* HAVE_SGEN_GC */