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.
172 *) Explore placing unreachable objects on unused nursery memory.
173 Instead of memset'ng a region to zero, place an int[] covering it.
174 A good place to start is add_nursery_frag. The tricky thing here is
175 placing those objects atomically outside of a collection.
185 #include <semaphore.h>
190 #include "metadata/metadata-internals.h"
191 #include "metadata/class-internals.h"
192 #include "metadata/gc-internal.h"
193 #include "metadata/object-internals.h"
194 #include "metadata/threads.h"
195 #include "metadata/sgen-gc.h"
196 #include "metadata/sgen-archdep.h"
197 #include "metadata/mono-gc.h"
198 #include "metadata/method-builder.h"
199 #include "metadata/profiler-private.h"
200 #include "metadata/monitor.h"
201 #include "metadata/threadpool-internals.h"
202 #include "metadata/mempool-internals.h"
203 #include "metadata/marshal.h"
204 #include "utils/mono-mmap.h"
205 #include "utils/mono-time.h"
206 #include "utils/mono-semaphore.h"
207 #include "utils/mono-counters.h"
209 #include <mono/utils/memcheck.h>
211 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
215 #include "mono/cil/opcode.def"
222 * ######################################################################
223 * ######## Types and constants used by the GC.
224 * ######################################################################
226 #if SIZEOF_VOID_P == 4
227 typedef guint32 mword;
229 typedef guint64 mword;
232 static int gc_initialized = 0;
233 static int gc_debug_level = 0;
234 static FILE* gc_debug_file;
235 /* If set, do a minor collection before every allocation */
236 static gboolean collect_before_allocs = FALSE;
237 /* If set, do a heap consistency check before each minor collection */
238 static gboolean consistency_check_at_minor_collection = FALSE;
239 /* If set, check that there are no references to the domain left at domain unload */
240 static gboolean xdomain_checks = FALSE;
241 /* If not null, dump the heap after each collection into this file */
242 static FILE *heap_dump_file = NULL;
243 /* If set, mark stacks conservatively, even if precise marking is possible */
244 static gboolean conservative_stack_mark = TRUE;
245 /* If set, do a plausibility check on the scan_starts before and after
247 static gboolean do_scan_starts_check = FALSE;
250 * Turning on heavy statistics will turn off the managed allocator and
251 * the managed write barrier.
253 //#define HEAVY_STATISTICS
255 #ifdef HEAVY_STATISTICS
256 #define HEAVY_STAT(x) x
258 #define HEAVY_STAT(x)
261 #ifdef HEAVY_STATISTICS
262 static long long stat_objects_alloced = 0;
263 static long long stat_bytes_alloced = 0;
264 static long long stat_objects_alloced_degraded = 0;
265 static long long stat_bytes_alloced_degraded = 0;
266 static long long stat_bytes_alloced_los = 0;
268 static long long stat_copy_object_called_nursery = 0;
269 static long long stat_objects_copied_nursery = 0;
270 static long long stat_copy_object_called_major = 0;
271 static long long stat_objects_copied_major = 0;
273 static long long stat_scan_object_called_nursery = 0;
274 static long long stat_scan_object_called_major = 0;
276 static long long stat_nursery_copy_object_failed_from_space = 0;
277 static long long stat_nursery_copy_object_failed_forwarded = 0;
278 static long long stat_nursery_copy_object_failed_pinned = 0;
280 static long long stat_store_remsets = 0;
281 static long long stat_store_remsets_unique = 0;
282 static long long stat_saved_remsets_1 = 0;
283 static long long stat_saved_remsets_2 = 0;
284 static long long stat_global_remsets_added = 0;
285 static long long stat_global_remsets_readded = 0;
286 static long long stat_global_remsets_processed = 0;
287 static long long stat_global_remsets_discarded = 0;
289 static long long stat_wasted_fragments_used = 0;
290 static long long stat_wasted_fragments_bytes = 0;
292 static int stat_wbarrier_set_field = 0;
293 static int stat_wbarrier_set_arrayref = 0;
294 static int stat_wbarrier_arrayref_copy = 0;
295 static int stat_wbarrier_generic_store = 0;
296 static int stat_wbarrier_generic_store_remset = 0;
297 static int stat_wbarrier_set_root = 0;
298 static int stat_wbarrier_value_copy = 0;
299 static int stat_wbarrier_object_copy = 0;
302 static long long time_minor_pre_collection_fragment_clear = 0;
303 static long long time_minor_pinning = 0;
304 static long long time_minor_scan_remsets = 0;
305 static long long time_minor_scan_pinned = 0;
306 static long long time_minor_scan_registered_roots = 0;
307 static long long time_minor_scan_thread_data = 0;
308 static long long time_minor_finish_gray_stack = 0;
309 static long long time_minor_fragment_creation = 0;
311 static long long time_major_pre_collection_fragment_clear = 0;
312 static long long time_major_pinning = 0;
313 static long long time_major_scan_pinned = 0;
314 static long long time_major_scan_registered_roots = 0;
315 static long long time_major_scan_thread_data = 0;
316 static long long time_major_scan_alloc_pinned = 0;
317 static long long time_major_scan_finalized = 0;
318 static long long time_major_scan_big_objects = 0;
319 static long long time_major_finish_gray_stack = 0;
320 static long long time_major_free_bigobjs = 0;
321 static long long time_major_los_sweep = 0;
322 static long long time_major_sweep = 0;
323 static long long time_major_fragment_creation = 0;
325 static long long pinned_chunk_bytes_alloced = 0;
326 static long long large_internal_bytes_alloced = 0;
328 /* Keep in sync with internal_mem_names in dump_heap()! */
330 INTERNAL_MEM_PIN_QUEUE,
331 INTERNAL_MEM_FRAGMENT,
332 INTERNAL_MEM_SECTION,
333 INTERNAL_MEM_SCAN_STARTS,
334 INTERNAL_MEM_FIN_TABLE,
335 INTERNAL_MEM_FINALIZE_ENTRY,
336 INTERNAL_MEM_DISLINK_TABLE,
337 INTERNAL_MEM_DISLINK,
338 INTERNAL_MEM_ROOTS_TABLE,
339 INTERNAL_MEM_ROOT_RECORD,
340 INTERNAL_MEM_STATISTICS,
342 INTERNAL_MEM_GRAY_QUEUE,
343 INTERNAL_MEM_STORE_REMSET,
344 INTERNAL_MEM_MS_TABLES,
345 INTERNAL_MEM_MS_BLOCK_INFO,
346 INTERNAL_MEM_EPHEMERON_LINK,
350 static long small_internal_mem_bytes [INTERNAL_MEM_MAX];
354 mono_gc_flush_info (void)
356 fflush (gc_debug_file);
360 #define MAX_DEBUG_LEVEL 2
361 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
363 /* Define this to allow the user to change some of the constants by specifying
364 * their values in the MONO_GC_PARAMS environmental variable. See
365 * mono_gc_base_init for details. */
366 #define USER_CONFIG 1
368 #define TV_DECLARE(name) gint64 name
369 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
370 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
371 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
373 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
375 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
383 typedef struct _Block Block;
389 /* each request from the OS ends up in a GCMemSection */
390 typedef struct _GCMemSection GCMemSection;
391 struct _GCMemSection {
395 /* pointer where more data could be allocated if it fits */
399 * scan starts is an array of pointers to objects equally spaced in the allocation area
400 * They let use quickly find pinned objects from pinning pointers.
403 /* in major collections indexes in the pin_queue for objects that pin this section */
406 unsigned short num_scan_start;
407 gboolean is_to_space;
410 #define SIZEOF_GC_MEM_SECTION ((sizeof (GCMemSection) + 7) & ~7)
412 /* Pinned objects are allocated in the LOS space if bigger than half a page
413 * or from freelists otherwise. We assume that pinned objects are relatively few
414 * and they have a slow dying speed (like interned strings, thread objects).
415 * As such they will be collected only at major collections.
416 * free lists are not global: when we need memory we allocate a PinnedChunk.
417 * Each pinned chunk is made of several pages, the first of wich is used
418 * internally for bookeeping (here think of a page as 4KB). The bookeeping
419 * includes the freelists vectors and info about the object size of each page
420 * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
421 * a size is assigned to it, the page is divided in the proper chunks and each
422 * chunk is added to the freelist. To not waste space, the remaining space in the
423 * first page is used as objects of size 16 or 32 (need to measure which are more
425 * We use this same structure to allocate memory used internally by the GC, so
426 * we never use malloc/free if we need to alloc during collection: the world is stopped
427 * and malloc/free will deadlock.
428 * When we want to iterate over pinned objects, we just scan a page at a time
429 * linearly according to the size of objects in the page: the next pointer used to link
430 * the items in the freelist uses the same word as the vtable. Since we keep freelists
431 * for each pinned chunk, if the word points outside the pinned chunk it means
433 * We could avoid this expensive scanning in creative ways. We could have a policy
434 * of putting in the pinned space only objects we know about that have no struct fields
435 * with references and we can easily use a even expensive write barrier for them,
436 * since pointer writes on such objects should be rare.
437 * The best compromise is to just alloc interned strings and System.MonoType in them.
438 * It would be nice to allocate MonoThread in it, too: must check that we properly
439 * use write barriers so we don't have to do any expensive scanning of the whole pinned
440 * chunk list during minor collections. We can avoid it now because we alloc in it only
441 * reference-free objects.
443 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
444 #define MAX_FREELIST_SIZE 8192
445 typedef struct _PinnedChunk PinnedChunk;
446 struct _PinnedChunk {
449 int *page_sizes; /* a 0 means the page is still unused */
452 void *data [1]; /* page sizes and free lists are stored here */
455 /* The method used to clear the nursery */
456 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
457 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
462 CLEAR_AT_TLAB_CREATION
463 } NurseryClearPolicy;
465 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
468 * If this is set, the nursery is aligned to an address aligned to its size, ie.
469 * a 1MB nursery will be aligned to an address divisible by 1MB. This allows us to
470 * speed up ptr_in_nursery () checks which are very frequent. This requires the
471 * nursery size to be a compile time constant.
473 #define ALIGN_NURSERY 1
476 * The young generation is divided into fragments. This is because
477 * we can hand one fragments to a thread for lock-less fast alloc and
478 * because the young generation ends up fragmented anyway by pinned objects.
479 * Once a collection is done, a list of fragments is created. When doing
480 * thread local alloc we use smallish nurseries so we allow new threads to
481 * allocate memory from gen0 without triggering a collection. Threads that
482 * are found to allocate lots of memory are given bigger fragments. This
483 * should make the finalizer thread use little nursery memory after a while.
484 * We should start assigning threads very small fragments: if there are many
485 * threads the nursery will be full of reserved space that the threads may not
486 * use at all, slowing down allocation speed.
487 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
488 * Allocation Buffers (TLABs).
490 typedef struct _Fragment Fragment;
494 char *fragment_start;
495 char *fragment_limit; /* the current soft limit for allocation */
499 /* the runtime can register areas of memory as roots: we keep two lists of roots,
500 * a pinned root set for conservatively scanned roots and a normal one for
501 * precisely scanned roots (currently implemented as a single list).
503 typedef struct _RootRecord RootRecord;
511 /* for use with write barriers */
512 typedef struct _RememberedSet RememberedSet;
513 struct _RememberedSet {
517 mword data [MONO_ZERO_LEN_ARRAY];
521 * We're never actually using the first element. It's always set to
522 * NULL to simplify the elimination of consecutive duplicate
525 #define STORE_REMSET_BUFFER_SIZE 1024
527 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
528 struct _GenericStoreRememberedSet {
529 GenericStoreRememberedSet *next;
530 /* We need one entry less because the first entry of store
531 remset buffers is always a dummy and we don't copy it. */
532 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
535 /* we have 4 possible values in the low 2 bits */
537 REMSET_LOCATION, /* just a pointer to the exact location */
538 REMSET_RANGE, /* range of pointer fields */
539 REMSET_OBJECT, /* mark all the object for scanning */
540 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
541 REMSET_TYPE_MASK = 0x3
544 #ifdef HAVE_KW_THREAD
545 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
547 static pthread_key_t remembered_set_key;
548 static RememberedSet *global_remset;
549 static RememberedSet *freed_thread_remsets;
550 static GenericStoreRememberedSet *generic_store_remsets = NULL;
552 /*A two slots cache for recently inserted remsets */
553 static gpointer global_remset_cache [2];
555 /* FIXME: later choose a size that takes into account the RememberedSet struct
556 * and doesn't waste any alloc paddin space.
558 #define DEFAULT_REMSET_SIZE 1024
559 static RememberedSet* alloc_remset (int size, gpointer id);
561 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
562 * no cast from a pointer to an integer
569 /* these bits are set in the object vtable: we could merge them since an object can be
570 * either pinned or forwarded but not both.
571 * We store them in the vtable slot because the bits are used in the sync block for
572 * other purposes: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
573 * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
574 * would be an invalid combination for the monitor and hash code).
575 * The values are already shifted.
576 * The forwarding address is stored in the sync block.
578 #define FORWARDED_BIT 1
580 #define VTABLE_BITS_MASK 0x3
582 /* returns NULL if not forwarded, or the forwarded address */
583 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
584 /* set the forwarded address fw_addr for object obj */
585 #define forward_object(obj,fw_addr) do { \
586 ((mword*)(obj))[0] |= FORWARDED_BIT; \
587 ((mword*)(obj))[1] = (mword)(fw_addr); \
590 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
591 #define pin_object(obj) do { \
592 ((mword*)(obj))[0] |= PINNED_BIT; \
594 #define unpin_object(obj) do { \
595 ((mword*)(obj))[0] &= ~PINNED_BIT; \
599 #define ptr_in_nursery(ptr) (((mword)(ptr) & ~((1 << DEFAULT_NURSERY_BITS) - 1)) == (mword)nursery_start)
601 #define ptr_in_nursery(ptr) ((char*)(ptr) >= nursery_start && (char*)(ptr) < nursery_real_end)
605 * Since we set bits in the vtable, use the macro to load it from the pointer to
606 * an object that is potentially pinned.
608 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
611 safe_name (void* obj)
613 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
614 return vt->klass->name;
618 safe_object_get_size (MonoObject* o)
620 MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
621 if (klass == mono_defaults.string_class) {
622 return sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
623 } else if (klass->rank) {
624 MonoArray *array = (MonoArray*)o;
625 size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length_fast (array);
626 if (G_UNLIKELY (array->bounds)) {
627 size += sizeof (mono_array_size_t) - 1;
628 size &= ~(sizeof (mono_array_size_t) - 1);
629 size += sizeof (MonoArrayBounds) * klass->rank;
633 /* from a created object: the class must be inited already */
634 return klass->instance_size;
639 * ######################################################################
640 * ######## Global data.
641 * ######################################################################
643 static LOCK_DECLARE (gc_mutex);
644 static int gc_disabled = 0;
645 static int num_minor_gcs = 0;
646 static int num_major_gcs = 0;
650 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
651 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
652 static int default_nursery_size = (1 << 22);
654 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
655 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
656 static int default_nursery_bits = 22;
661 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
663 #define DEFAULT_NURSERY_BITS 22
668 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
669 /* to quickly find the head of an object pinned by a conservative address
670 * we keep track of the objects allocated for each SCAN_START_SIZE memory
671 * chunk in the nursery or other memory sections. Larger values have less
672 * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
674 #define SCAN_START_SIZE (4096*2)
675 /* the minimum size of a fragment that we consider useful for allocation */
676 #define FRAGMENT_MIN_SIZE (512)
677 /* This is a fixed value used for pinned chunks, not the system pagesize */
678 #define FREELIST_PAGESIZE (16*1024)
680 static mword pagesize = 4096;
681 static mword nursery_size;
682 static int degraded_mode = 0;
684 static mword total_alloc = 0;
685 /* use this to tune when to do a major/minor collection */
686 static mword memory_pressure = 0;
687 static int minor_collection_allowance;
688 static int minor_collection_sections_alloced = 0;
690 static GCMemSection *nursery_section = NULL;
691 static mword lowest_heap_address = ~(mword)0;
692 static mword highest_heap_address = 0;
694 static LOCK_DECLARE (interruption_mutex);
696 typedef struct _FinalizeEntry FinalizeEntry;
697 struct _FinalizeEntry {
702 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
703 struct _FinalizeEntryHashTable {
704 FinalizeEntry **table;
709 typedef struct _DisappearingLink DisappearingLink;
710 struct _DisappearingLink {
711 DisappearingLink *next;
715 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
716 struct _DisappearingLinkHashTable {
717 DisappearingLink **table;
722 typedef struct _EphemeronLinkNode EphemeronLinkNode;
724 struct _EphemeronLinkNode {
725 EphemeronLinkNode *next;
734 #define LARGE_INTERNAL_MEM_HEADER_MAGIC 0x7d289f3a
736 typedef struct _LargeInternalMemHeader LargeInternalMemHeader;
737 struct _LargeInternalMemHeader {
749 int current_collection_generation = -1;
752 * The link pointer is hidden by negating each bit. We use the lowest
753 * bit of the link (before negation) to store whether it needs
754 * resurrection tracking.
756 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
757 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
759 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
760 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
763 * The finalizable hash has the object as the key, the
764 * disappearing_link hash, has the link address as key.
766 static FinalizeEntryHashTable minor_finalizable_hash;
767 static FinalizeEntryHashTable major_finalizable_hash;
768 /* objects that are ready to be finalized */
769 static FinalizeEntry *fin_ready_list = NULL;
770 static FinalizeEntry *critical_fin_list = NULL;
772 static DisappearingLinkHashTable minor_disappearing_link_hash;
773 static DisappearingLinkHashTable major_disappearing_link_hash;
775 static EphemeronLinkNode *ephemeron_list;
777 static int num_ready_finalizers = 0;
778 static int no_finalize = 0;
780 /* keep each size a multiple of ALLOC_ALIGN */
781 /* on 64 bit systems 8 is likely completely unused. */
782 static const int freelist_sizes [] = {
783 8, 16, 24, 32, 40, 48, 64, 80,
784 96, 128, 160, 192, 224, 256, 320, 384,
785 448, 512, 584, 680, 816, 1024, 1360, 2048,
786 2336, 2728, 3272, 4096, 5456, 8192 };
787 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
789 /* This is also the MAJOR_SECTION_SIZE for the copying major
791 #define PINNED_CHUNK_SIZE (128 * 1024)
793 /* internal_chunk_list is used for allocating structures needed by the GC */
794 static PinnedChunk *internal_chunk_list = NULL;
796 static int slot_for_size (size_t size);
799 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
800 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
801 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
805 /* registered roots: the key to the hash is the root start address */
807 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
809 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
810 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
811 static mword roots_size = 0; /* amount of memory in the root set */
812 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
815 * The current allocation cursors
816 * We allocate objects in the nursery.
817 * The nursery is the area between nursery_start and nursery_real_end.
818 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
819 * from nursery fragments.
820 * tlab_next is the pointer to the space inside the TLAB where the next object will
822 * tlab_temp_end is the pointer to the end of the temporary space reserved for
823 * the allocation: it allows us to set the scan starts at reasonable intervals.
824 * tlab_real_end points to the end of the TLAB.
825 * nursery_frag_real_end points to the end of the currently used nursery fragment.
826 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
827 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
828 * At the next allocation, the area of the nursery where objects can be present is
829 * between MIN(nursery_first_pinned_start, first_fragment_start) and
830 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
832 static char *nursery_start = NULL;
834 /* eventually share with MonoThread? */
835 typedef struct _SgenThreadInfo SgenThreadInfo;
837 struct _SgenThreadInfo {
838 SgenThreadInfo *next;
840 unsigned int stop_count; /* to catch duplicate signals */
843 volatile int in_critical_region;
846 void *stack_start_limit;
847 char **tlab_next_addr;
848 char **tlab_start_addr;
849 char **tlab_temp_end_addr;
850 char **tlab_real_end_addr;
851 gpointer **store_remset_buffer_addr;
852 long *store_remset_buffer_index_addr;
853 RememberedSet *remset;
854 gpointer runtime_data;
855 gpointer stopped_ip; /* only valid if the thread is stopped */
856 MonoDomain *stopped_domain; /* ditto */
857 gpointer *stopped_regs; /* ditto */
858 #ifndef HAVE_KW_THREAD
863 gpointer *store_remset_buffer;
864 long store_remset_buffer_index;
868 #ifdef HAVE_KW_THREAD
869 #define TLAB_ACCESS_INIT
870 #define TLAB_START tlab_start
871 #define TLAB_NEXT tlab_next
872 #define TLAB_TEMP_END tlab_temp_end
873 #define TLAB_REAL_END tlab_real_end
874 #define REMEMBERED_SET remembered_set
875 #define STORE_REMSET_BUFFER store_remset_buffer
876 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
877 #define IN_CRITICAL_REGION thread_info->in_critical_region
879 static pthread_key_t thread_info_key;
880 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
881 #define TLAB_START (__thread_info__->tlab_start)
882 #define TLAB_NEXT (__thread_info__->tlab_next)
883 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
884 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
885 #define REMEMBERED_SET (__thread_info__->remset)
886 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
887 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
888 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
891 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
892 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
893 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
896 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
897 * variables for next+temp_end ?
899 #ifdef HAVE_KW_THREAD
900 static __thread SgenThreadInfo *thread_info;
901 static __thread char *tlab_start;
902 static __thread char *tlab_next;
903 static __thread char *tlab_temp_end;
904 static __thread char *tlab_real_end;
905 static __thread gpointer *store_remset_buffer;
906 static __thread long store_remset_buffer_index;
907 /* Used by the managed allocator/wbarrier */
908 static __thread char **tlab_next_addr;
909 static __thread char *stack_end;
910 static __thread long *store_remset_buffer_index_addr;
912 static char *nursery_next = NULL;
913 static char *nursery_frag_real_end = NULL;
914 static char *nursery_real_end = NULL;
915 static char *nursery_last_pinned_end = NULL;
917 /* The size of a TLAB */
918 /* The bigger the value, the less often we have to go to the slow path to allocate a new
919 * one, but the more space is wasted by threads not allocating much memory.
921 * FIXME: Make this self-tuning for each thread.
923 static guint32 tlab_size = (1024 * 4);
925 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
926 #define MAX_NURSERY_TLAB_WASTE 512
928 /* fragments that are free and ready to be used for allocation */
929 static Fragment *nursery_fragments = NULL;
930 /* freeelist of fragment structures */
931 static Fragment *fragment_freelist = NULL;
934 * Objects bigger then this go into the large object space. This size
935 * has a few constraints. It must fit into the major heap, which in
936 * the case of the copying collector means that it must fit into a
937 * pinned chunk. It must also play well with the GC descriptors, some
938 * of which (DESC_TYPE_RUN_LENGTH, DESC_TYPE_SMALL_BITMAP) encode the
941 #define MAX_SMALL_OBJ_SIZE 8000
943 /* Functions supplied by the runtime to be called by the GC */
944 static MonoGCCallbacks gc_callbacks;
946 #define ALLOC_ALIGN 8
947 #define ALLOC_ALIGN_BITS 3
949 #define MOVED_OBJECTS_NUM 64
950 static void *moved_objects [MOVED_OBJECTS_NUM];
951 static int moved_objects_idx = 0;
954 * ######################################################################
955 * ######## Macros and function declarations.
956 * ######################################################################
959 #define UPDATE_HEAP_BOUNDARIES(low,high) do { \
960 if ((mword)(low) < lowest_heap_address) \
961 lowest_heap_address = (mword)(low); \
962 if ((mword)(high) > highest_heap_address) \
963 highest_heap_address = (mword)(high); \
965 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
968 align_pointer (void *ptr)
970 mword p = (mword)ptr;
971 p += sizeof (gpointer) - 1;
972 p &= ~ (sizeof (gpointer) - 1);
976 typedef void (*CopyOrMarkObjectFunc) (void**);
977 typedef char* (*ScanObjectFunc) (char*);
979 /* forward declarations */
980 static void* get_internal_mem (size_t size, int type);
981 static void free_internal_mem (void *addr, int type);
982 static void* get_os_memory (size_t size, int activate);
983 static void* get_os_memory_aligned (mword size, mword alignment, gboolean activate);
984 static void free_os_memory (void *addr, size_t size);
985 static G_GNUC_UNUSED void report_internal_mem_usage (void);
987 static int stop_world (void);
988 static int restart_world (void);
989 static void add_to_global_remset (gpointer ptr);
990 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
991 static void scan_from_remsets (void *start_nursery, void *end_nursery);
992 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type);
993 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list);
994 static void find_pinning_ref_from_thread (char *obj, size_t size);
995 static void update_current_thread_stack (void *start);
996 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
997 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
998 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
999 static void null_links_for_domain (MonoDomain *domain, int generation);
1000 static gboolean search_fragment_for_size (size_t size);
1001 static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
1002 static void build_nursery_fragments (int start_pin, int end_pin);
1003 static void clear_nursery_fragments (char *next);
1004 static void pin_from_roots (void *start_nursery, void *end_nursery);
1005 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery);
1006 static void pin_objects_in_section (GCMemSection *section);
1007 static void optimize_pin_queue (int start_slot);
1008 static void clear_remsets (void);
1009 static void clear_tlabs (void);
1010 typedef void (*IterateObjectCallbackFunc) (char*, size_t, void*);
1011 static void scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data);
1012 static void scan_object (char *start);
1013 static void major_scan_object (char *start);
1014 static void* copy_object_no_checks (void *obj);
1015 static void copy_object (void **obj_slot);
1016 static void* get_chunk_freelist (PinnedChunk *chunk, int slot);
1017 static PinnedChunk* alloc_pinned_chunk (void);
1018 static void sort_addresses (void **array, int size);
1019 static void drain_gray_stack (void);
1020 static void finish_gray_stack (char *start_addr, char *end_addr, int generation);
1021 static gboolean need_major_collection (void);
1022 static void major_collection (const char *reason);
1024 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
1026 void describe_ptr (char *ptr);
1027 static void check_consistency (void);
1028 static void check_major_refs (void);
1029 static void check_section_scan_starts (GCMemSection *section);
1030 static void check_scan_starts (void);
1031 static void check_for_xdomain_refs (void);
1032 static void dump_occupied (char *start, char *end, char *section_start);
1033 static void dump_section (GCMemSection *section, const char *type);
1034 static void dump_heap (const char *type, int num, const char *reason);
1035 static void commit_stats (int generation);
1036 static void report_pinned_chunk (PinnedChunk *chunk, int seq);
1038 void mono_gc_scan_for_specific_ref (MonoObject *key);
1040 static void init_stats (void);
1042 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end);
1043 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end);
1044 static void null_ephemerons_for_domain (MonoDomain *domain);
1046 //#define BINARY_PROTOCOL
1047 #include "sgen-protocol.c"
1048 #include "sgen-pinning.c"
1049 #include "sgen-pinning-stats.c"
1050 #include "sgen-gray.c"
1051 #include "sgen-los.c"
1054 * ######################################################################
1055 * ######## GC descriptors
1056 * ######################################################################
1057 * Used to quickly get the info the GC needs about an object: size and
1058 * where the references are held.
1060 /* objects are aligned to 8 bytes boundaries
1061 * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
1062 * The low 3 bits define the type of the descriptor. The other bits
1063 * depend on the type.
1064 * As a general rule the 13 remaining low bits define the size, either
1065 * of the whole object or of the elements in the arrays. While for objects
1066 * the size is already in bytes, for arrays we need to shift, because
1067 * array elements might be smaller than 8 bytes. In case of arrays, we
1068 * use two bits to describe what the additional high bits represents,
1069 * so the default behaviour can handle element sizes less than 2048 bytes.
1070 * The high 16 bits, if 0 it means the object is pointer-free.
1071 * This design should make it easy and fast to skip over ptr-free data.
1072 * The first 4 types should cover >95% of the objects.
1073 * Note that since the size of objects is limited to 64K, larger objects
1074 * will be allocated in the large object heap.
1075 * If we want 4-bytes alignment, we need to put vector and small bitmap
1080 * We don't use 0 so that 0 isn't a valid GC descriptor. No
1081 * deep reason for this other than to be able to identify a
1082 * non-inited descriptor for debugging.
1084 * If an object contains no references, its GC descriptor is
1085 * always DESC_TYPE_RUN_LENGTH, without a size, no exceptions.
1086 * This is so that we can quickly check for that in
1087 * copy_object_no_checks(), without having to fetch the
1090 DESC_TYPE_RUN_LENGTH = 1, /* 15 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
1091 DESC_TYPE_SMALL_BITMAP, /* 15 bits aligned byte size | 16-48 bit bitmap */
1092 DESC_TYPE_COMPLEX, /* index for bitmap into complex_descriptors */
1093 DESC_TYPE_VECTOR, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1094 DESC_TYPE_ARRAY, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1095 DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
1096 DESC_TYPE_COMPLEX_ARR, /* index for bitmap into complex_descriptors */
1097 /* subtypes for arrays and vectors */
1098 DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value */
1099 DESC_TYPE_V_REFS, /* all the array elements are refs */
1100 DESC_TYPE_V_RUN_LEN, /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
1101 DESC_TYPE_V_BITMAP /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
1104 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
1105 #define LOW_TYPE_BITS 3
1106 #define SMALL_BITMAP_SHIFT 16
1107 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
1108 #define VECTOR_INFO_SHIFT 14
1109 #define VECTOR_ELSIZE_SHIFT 3
1110 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
1111 #define MAX_ELEMENT_SIZE 0x3ff
1112 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
1113 #define VECTOR_SUBTYPE_REFS (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
1114 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
1115 #define VECTOR_SUBTYPE_BITMAP (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
1118 /* Root bitmap descriptors are simpler: the lower three bits describe the type
1119 * and we either have 30/62 bitmap bits or nibble-based run-length,
1120 * or a complex descriptor, or a user defined marker function.
1123 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
1128 ROOT_DESC_TYPE_MASK = 0x7,
1129 ROOT_DESC_TYPE_SHIFT = 3,
1132 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
1134 #define MAX_USER_DESCRIPTORS 16
1136 static gsize* complex_descriptors = NULL;
1137 static int complex_descriptors_size = 0;
1138 static int complex_descriptors_next = 0;
1139 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
1140 static int user_descriptors_next = 0;
1143 alloc_complex_descriptor (gsize *bitmap, int numbits)
1147 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
1148 nwords = numbits / GC_BITS_PER_WORD + 1;
1151 res = complex_descriptors_next;
1152 /* linear search, so we don't have duplicates with domain load/unload
1153 * this should not be performance critical or we'd have bigger issues
1154 * (the number and size of complex descriptors should be small).
1156 for (i = 0; i < complex_descriptors_next; ) {
1157 if (complex_descriptors [i] == nwords) {
1158 int j, found = TRUE;
1159 for (j = 0; j < nwords - 1; ++j) {
1160 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
1170 i += complex_descriptors [i];
1172 if (complex_descriptors_next + nwords > complex_descriptors_size) {
1173 int new_size = complex_descriptors_size * 2 + nwords;
1174 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
1175 complex_descriptors_size = new_size;
1177 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
1178 complex_descriptors_next += nwords;
1179 complex_descriptors [res] = nwords;
1180 for (i = 0; i < nwords - 1; ++i) {
1181 complex_descriptors [res + 1 + i] = bitmap [i];
1182 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
1189 * Descriptor builders.
1192 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
1194 return (void*) DESC_TYPE_RUN_LENGTH;
1198 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
1200 int first_set = -1, num_set = 0, last_set = -1, i;
1202 size_t stored_size = obj_size;
1203 for (i = 0; i < numbits; ++i) {
1204 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1212 * We don't encode the size of types that don't contain
1213 * references because they might not be aligned, i.e. the
1214 * bottom two bits might be set, which would clash with the
1215 * bits we need to encode the descriptor type. Since we don't
1216 * use the encoded size to skip objects, other than for
1217 * processing remsets, in which case only the positions of
1218 * references are relevant, this is not a problem.
1221 return DESC_TYPE_RUN_LENGTH;
1222 g_assert (!(stored_size & 0x3));
1223 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
1224 /* check run-length encoding first: one byte offset, one byte number of pointers
1225 * on 64 bit archs, we can have 3 runs, just one on 32.
1226 * It may be better to use nibbles.
1228 if (first_set < 0) {
1229 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
1230 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1231 return (void*) desc;
1232 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1233 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
1234 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));
1235 return (void*) desc;
1237 /* we know the 2-word header is ptr-free */
1238 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1239 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1240 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1241 return (void*) desc;
1244 /* we know the 2-word header is ptr-free */
1245 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1246 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1247 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1248 return (void*) desc;
1250 /* it's a complex object ... */
1251 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1252 return (void*) desc;
1255 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1257 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1259 int first_set = -1, num_set = 0, last_set = -1, i;
1260 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1261 for (i = 0; i < numbits; ++i) {
1262 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1269 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
1271 return DESC_TYPE_RUN_LENGTH;
1272 if (elem_size <= MAX_ELEMENT_SIZE) {
1273 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1275 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1277 /* Note: we also handle structs with just ref fields */
1278 if (num_set * sizeof (gpointer) == elem_size) {
1279 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1281 /* FIXME: try run-len first */
1282 /* Note: we can't skip the object header here, because it's not present */
1283 if (last_set <= SMALL_BITMAP_SIZE) {
1284 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1287 /* it's am array of complex structs ... */
1288 desc = DESC_TYPE_COMPLEX_ARR;
1289 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1290 return (void*) desc;
1293 /* Return the bitmap encoded by a descriptor */
1295 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1297 mword d = (mword)descr;
1301 case DESC_TYPE_RUN_LENGTH: {
1302 int first_set = (d >> 16) & 0xff;
1303 int num_set = (d >> 24) & 0xff;
1306 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1308 for (i = first_set; i < first_set + num_set; ++i)
1309 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1311 *numbits = first_set + num_set;
1315 case DESC_TYPE_SMALL_BITMAP:
1316 bitmap = g_new0 (gsize, 1);
1318 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1320 *numbits = GC_BITS_PER_WORD;
1324 g_assert_not_reached ();
1328 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
1329 #define STRING_SIZE(size,str) do { \
1330 (size) = sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*)(str)) + 2; \
1331 (size) += (ALLOC_ALIGN - 1); \
1332 (size) &= ~(ALLOC_ALIGN - 1); \
1335 #define OBJ_RUN_LEN_SIZE(size,desc,obj) do { \
1336 (size) = ((desc) & 0xfff8) >> 1; \
1339 #define OBJ_BITMAP_SIZE(size,desc,obj) do { \
1340 (size) = ((desc) & 0xfff8) >> 1; \
1343 //#define PREFETCH(addr) __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(addr)))
1344 #define PREFETCH(addr)
1346 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
1347 #define OBJ_RUN_LEN_FOREACH_PTR(desc,obj) do { \
1348 if ((desc) & 0xffff0000) { \
1349 /* there are pointers */ \
1350 void **_objptr_end; \
1351 void **_objptr = (void**)(obj); \
1352 _objptr += ((desc) >> 16) & 0xff; \
1353 _objptr_end = _objptr + (((desc) >> 24) & 0xff); \
1354 while (_objptr < _objptr_end) { \
1355 HANDLE_PTR (_objptr, (obj)); \
1361 /* a bitmap desc means that there are pointer references or we'd have
1362 * choosen run-length, instead: add an assert to check.
1364 #define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
1365 /* there are pointers */ \
1366 void **_objptr = (void**)(obj); \
1367 gsize _bmap = (desc) >> 16; \
1368 _objptr += OBJECT_HEADER_WORDS; \
1370 if ((_bmap & 1)) { \
1371 HANDLE_PTR (_objptr, (obj)); \
1378 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj) do { \
1379 /* there are pointers */ \
1380 void **_objptr = (void**)(obj); \
1381 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS; \
1382 _objptr += OBJECT_HEADER_WORDS; \
1384 if ((_bmap & 1)) { \
1385 HANDLE_PTR (_objptr, (obj)); \
1392 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do { \
1393 /* there are pointers */ \
1394 void **_objptr = (void**)(obj); \
1395 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1396 int bwords = (*bitmap_data) - 1; \
1397 void **start_run = _objptr; \
1400 MonoObject *myobj = (MonoObject*)obj; \
1401 g_print ("found %d at %p (0x%zx): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1403 while (bwords-- > 0) { \
1404 gsize _bmap = *bitmap_data++; \
1405 _objptr = start_run; \
1406 /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/ \
1408 if ((_bmap & 1)) { \
1409 HANDLE_PTR (_objptr, (obj)); \
1414 start_run += GC_BITS_PER_WORD; \
1418 /* this one is untested */
1419 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj) do { \
1420 /* there are pointers */ \
1421 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1422 int mbwords = (*mbitmap_data++) - 1; \
1423 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass); \
1424 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1425 char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj)); \
1427 MonoObject *myobj = (MonoObject*)start; \
1428 g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1430 while (e_start < e_end) { \
1431 void **_objptr = (void**)e_start; \
1432 gsize *bitmap_data = mbitmap_data; \
1433 unsigned int bwords = mbwords; \
1434 while (bwords-- > 0) { \
1435 gsize _bmap = *bitmap_data++; \
1436 void **start_run = _objptr; \
1437 /*g_print ("bitmap: 0x%x\n", _bmap);*/ \
1439 if ((_bmap & 1)) { \
1440 HANDLE_PTR (_objptr, (obj)); \
1445 _objptr = start_run + GC_BITS_PER_WORD; \
1447 e_start += el_size; \
1451 #define OBJ_VECTOR_FOREACH_PTR(vt,obj) do { \
1452 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */ \
1453 if ((vt)->desc & 0xffffc000) { \
1454 int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE; \
1455 /* there are pointers */ \
1456 int etype = (vt)->desc & 0xc000; \
1457 if (etype == (DESC_TYPE_V_REFS << 14)) { \
1458 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector)); \
1459 void **end_refs = (void**)((char*)p + el_size * mono_array_length_fast ((MonoArray*)(obj))); \
1460 /* Note: this code can handle also arrays of struct with only references in them */ \
1461 while (p < end_refs) { \
1462 HANDLE_PTR (p, (obj)); \
1465 } else if (etype == DESC_TYPE_V_RUN_LEN << 14) { \
1466 int offset = ((vt)->desc >> 16) & 0xff; \
1467 int num_refs = ((vt)->desc >> 24) & 0xff; \
1468 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1469 char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj)); \
1470 while (e_start < e_end) { \
1471 void **p = (void**)e_start; \
1474 for (i = 0; i < num_refs; ++i) { \
1475 HANDLE_PTR (p + i, (obj)); \
1477 e_start += el_size; \
1479 } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
1480 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1481 char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj)); \
1482 while (e_start < e_end) { \
1483 void **p = (void**)e_start; \
1484 gsize _bmap = (vt)->desc >> 16; \
1485 /* Note: there is no object header here to skip */ \
1487 if ((_bmap & 1)) { \
1488 HANDLE_PTR (p, (obj)); \
1493 e_start += el_size; \
1499 //#include "sgen-major-copying.c"
1500 #include "sgen-marksweep.c"
1503 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1505 MonoObject *o = (MonoObject*)(obj);
1506 MonoObject *ref = (MonoObject*)*(ptr);
1507 int offset = (char*)(ptr) - (char*)o;
1509 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1511 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1513 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1514 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1516 /* Thread.cached_culture_info */
1517 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1518 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1519 !strcmp(o->vtable->klass->name_space, "System") &&
1520 !strcmp(o->vtable->klass->name, "Object[]"))
1523 * 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
1524 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1525 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1526 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1527 * 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
1528 * 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
1529 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1530 * 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
1531 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1533 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1534 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1535 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1536 !strcmp (o->vtable->klass->name, "MemoryStream"))
1538 /* append_job() in threadpool.c */
1539 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1540 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1541 !strcmp (o->vtable->klass->name_space, "System") &&
1542 !strcmp (o->vtable->klass->name, "Object[]") &&
1543 mono_thread_pool_is_queue_array ((MonoArray*) o))
1549 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1551 MonoObject *o = (MonoObject*)(obj);
1552 MonoObject *ref = (MonoObject*)*(ptr);
1553 int offset = (char*)(ptr) - (char*)o;
1555 MonoClassField *field;
1558 if (!ref || ref->vtable->domain == domain)
1560 if (is_xdomain_ref_allowed (ptr, obj, domain))
1564 for (class = o->vtable->klass; class; class = class->parent) {
1567 for (i = 0; i < class->field.count; ++i) {
1568 if (class->fields[i].offset == offset) {
1569 field = &class->fields[i];
1577 if (ref->vtable->klass == mono_defaults.string_class)
1578 str = mono_string_to_utf8 ((MonoString*)ref);
1581 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1582 o, o->vtable->klass->name_space, o->vtable->klass->name,
1583 offset, field ? field->name : "",
1584 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1585 mono_gc_scan_for_specific_ref (o);
1591 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1594 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1596 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1598 #include "sgen-scan-object.h"
1602 #define HANDLE_PTR(ptr,obj) do { \
1603 if ((MonoObject*)*(ptr) == key) { \
1604 g_print ("found ref to %p in object %p (%s) at offset %zd\n", \
1605 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1610 scan_object_for_specific_ref (char *start, MonoObject *key)
1612 #include "sgen-scan-object.h"
1616 scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data)
1618 while (start < end) {
1620 if (!*(void**)start) {
1621 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1625 size = safe_object_get_size ((MonoObject*) start);
1626 size += ALLOC_ALIGN - 1;
1627 size &= ~(ALLOC_ALIGN - 1);
1629 callback (start, size, data);
1636 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1638 scan_object_for_specific_ref (obj, key);
1642 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1646 g_print ("found ref to %p in root record %p\n", key, root);
1649 static MonoObject *check_key = NULL;
1650 static RootRecord *check_root = NULL;
1653 check_root_obj_specific_ref_from_marker (void **obj)
1655 check_root_obj_specific_ref (check_root, check_key, *obj);
1659 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1664 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1665 for (root = roots_hash [root_type][i]; root; root = root->next) {
1666 void **start_root = (void**)root->start_root;
1667 mword desc = root->root_desc;
1671 switch (desc & ROOT_DESC_TYPE_MASK) {
1672 case ROOT_DESC_BITMAP:
1673 desc >>= ROOT_DESC_TYPE_SHIFT;
1676 check_root_obj_specific_ref (root, key, *start_root);
1681 case ROOT_DESC_COMPLEX: {
1682 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1683 int bwords = (*bitmap_data) - 1;
1684 void **start_run = start_root;
1686 while (bwords-- > 0) {
1687 gsize bmap = *bitmap_data++;
1688 void **objptr = start_run;
1691 check_root_obj_specific_ref (root, key, *objptr);
1695 start_run += GC_BITS_PER_WORD;
1699 case ROOT_DESC_USER: {
1700 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1701 marker (start_root, check_root_obj_specific_ref_from_marker);
1704 case ROOT_DESC_RUN_LEN:
1705 g_assert_not_reached ();
1707 g_assert_not_reached ();
1716 mono_gc_scan_for_specific_ref (MonoObject *key)
1722 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1723 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1725 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1727 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1728 scan_object_for_specific_ref (bigobj->data, key);
1730 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1731 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1733 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1734 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1735 void **ptr = (void**)root->start_root;
1737 while (ptr < (void**)root->end_root) {
1738 check_root_obj_specific_ref (root, *ptr, key);
1745 /* Clear all remaining nursery fragments */
1747 clear_nursery_fragments (char *next)
1750 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1751 g_assert (next <= nursery_frag_real_end);
1752 memset (next, 0, nursery_frag_real_end - next);
1753 for (frag = nursery_fragments; frag; frag = frag->next) {
1754 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1760 need_remove_object_for_domain (char *start, MonoDomain *domain)
1762 if (mono_object_domain (start) == domain) {
1763 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1764 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1771 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1773 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1774 if (vt->klass == mono_defaults.internal_thread_class)
1775 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1776 /* The object could be a proxy for an object in the domain
1778 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1779 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1781 /* The server could already have been zeroed out, so
1782 we need to check for that, too. */
1783 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1784 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1786 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1791 static MonoDomain *check_domain = NULL;
1794 check_obj_not_in_domain (void **o)
1796 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1800 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1804 check_domain = domain;
1805 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1806 for (root = roots_hash [root_type][i]; root; root = root->next) {
1807 void **start_root = (void**)root->start_root;
1808 mword desc = root->root_desc;
1810 /* The MonoDomain struct is allowed to hold
1811 references to objects in its own domain. */
1812 if (start_root == (void**)domain)
1815 switch (desc & ROOT_DESC_TYPE_MASK) {
1816 case ROOT_DESC_BITMAP:
1817 desc >>= ROOT_DESC_TYPE_SHIFT;
1819 if ((desc & 1) && *start_root)
1820 check_obj_not_in_domain (*start_root);
1825 case ROOT_DESC_COMPLEX: {
1826 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1827 int bwords = (*bitmap_data) - 1;
1828 void **start_run = start_root;
1830 while (bwords-- > 0) {
1831 gsize bmap = *bitmap_data++;
1832 void **objptr = start_run;
1834 if ((bmap & 1) && *objptr)
1835 check_obj_not_in_domain (*objptr);
1839 start_run += GC_BITS_PER_WORD;
1843 case ROOT_DESC_USER: {
1844 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1845 marker (start_root, check_obj_not_in_domain);
1848 case ROOT_DESC_RUN_LEN:
1849 g_assert_not_reached ();
1851 g_assert_not_reached ();
1855 check_domain = NULL;
1859 check_for_xdomain_refs (void)
1863 scan_area_with_callback (nursery_section->data, nursery_section->end_data, scan_object_for_xdomain_refs, NULL);
1865 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1867 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1868 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1872 clear_domain_process_object (char *obj, MonoDomain *domain)
1876 process_object_for_domain_clearing (obj, domain);
1877 remove = need_remove_object_for_domain (obj, domain);
1879 if (remove && ((MonoObject*)obj)->synchronisation) {
1880 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1882 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1889 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1891 if (clear_domain_process_object (obj, domain))
1892 memset (obj, 0, size);
1896 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1898 clear_domain_process_object (obj, domain);
1902 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1904 if (need_remove_object_for_domain (obj, domain))
1905 major_free_non_pinned_object (obj, size);
1909 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1911 if (need_remove_object_for_domain (obj, domain))
1912 free_pinned_object (obj, size);
1916 * When appdomains are unloaded we can easily remove objects that have finalizers,
1917 * but all the others could still be present in random places on the heap.
1918 * We need a sweep to get rid of them even though it's going to be costly
1920 * The reason we need to remove them is because we access the vtable and class
1921 * structures to know the object size and the reference bitmap: once the domain is
1922 * unloaded the point to random memory.
1925 mono_gc_clear_domain (MonoDomain * domain)
1927 LOSObject *bigobj, *prev;
1932 clear_nursery_fragments (nursery_next);
1934 if (xdomain_checks && domain != mono_get_root_domain ()) {
1935 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1936 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1937 check_for_xdomain_refs ();
1940 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1941 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain);
1943 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1944 to memory returned to the OS.*/
1945 null_ephemerons_for_domain (domain);
1947 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1948 null_links_for_domain (domain, i);
1950 /* We need two passes over major and large objects because
1951 freeing such objects might give their memory back to the OS
1952 (in the case of large objects) or obliterate its vtable
1953 (pinned objects with major-copying or pinned and non-pinned
1954 objects with major-mark&sweep), but we might need to
1955 dereference a pointer from an object to another object if
1956 the first object is a proxy. */
1957 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1958 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1959 clear_domain_process_object (bigobj->data, domain);
1962 for (bigobj = los_object_list; bigobj;) {
1963 if (need_remove_object_for_domain (bigobj->data, domain)) {
1964 LOSObject *to_free = bigobj;
1966 prev->next = bigobj->next;
1968 los_object_list = bigobj->next;
1969 bigobj = bigobj->next;
1970 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1972 free_large_object (to_free);
1976 bigobj = bigobj->next;
1978 major_iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1979 major_iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1985 global_remset_cache_clear (void)
1987 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1991 * Tries to check if a given remset location was already added to the global remset.
1994 * A 2 entry, LRU cache of recently saw location remsets.
1996 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1998 * Returns TRUE is the element was added..
2001 global_remset_location_was_not_added (gpointer ptr)
2004 gpointer first = global_remset_cache [0], second;
2006 HEAVY_STAT (++stat_global_remsets_discarded);
2010 second = global_remset_cache [1];
2012 if (second == ptr) {
2013 /*Move the second to the front*/
2014 global_remset_cache [0] = second;
2015 global_remset_cache [1] = first;
2017 HEAVY_STAT (++stat_global_remsets_discarded);
2021 global_remset_cache [0] = second;
2022 global_remset_cache [1] = ptr;
2027 * add_to_global_remset:
2029 * The global remset contains locations which point into newspace after
2030 * a minor collection. This can happen if the objects they point to are pinned.
2033 add_to_global_remset (gpointer ptr)
2037 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
2039 if (!global_remset_location_was_not_added (ptr))
2042 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
2043 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
2045 HEAVY_STAT (++stat_global_remsets_added);
2048 * FIXME: If an object remains pinned, we need to add it at every minor collection.
2049 * To avoid uncontrolled growth of the global remset, only add each pointer once.
2051 if (global_remset->store_next + 3 < global_remset->end_set) {
2052 *(global_remset->store_next++) = (mword)ptr;
2055 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
2056 rs->next = global_remset;
2058 *(global_remset->store_next++) = (mword)ptr;
2061 int global_rs_size = 0;
2063 for (rs = global_remset; rs; rs = rs->next) {
2064 global_rs_size += rs->store_next - rs->data;
2066 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
2071 * FIXME: allocate before calling this function and pass the
2072 * destination address.
2075 copy_object_no_checks (void *obj)
2077 static const void *copy_labels [] = { &&LAB_0, &&LAB_1, &&LAB_2, &&LAB_3, &&LAB_4, &&LAB_5, &&LAB_6, &&LAB_7, &&LAB_8 };
2081 MonoVTable *vt = ((MonoObject*)obj)->vtable;
2082 gboolean has_references = vt->gc_descr != DESC_TYPE_RUN_LENGTH;
2084 objsize = safe_object_get_size ((MonoObject*)obj);
2085 objsize += ALLOC_ALIGN - 1;
2086 objsize &= ~(ALLOC_ALIGN - 1);
2088 DEBUG (9, g_assert (vt->klass->inited));
2089 MAJOR_GET_COPY_OBJECT_SPACE (destination, objsize, has_references);
2091 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %zd)\n", destination, ((MonoObject*)obj)->vtable->klass->name, objsize));
2092 binary_protocol_copy (obj, destination, ((MonoObject*)obj)->vtable, objsize);
2094 if (objsize <= sizeof (gpointer) * 8) {
2095 mword *dest = (mword*)destination;
2096 goto *copy_labels [objsize / sizeof (gpointer)];
2098 (dest) [7] = ((mword*)obj) [7];
2100 (dest) [6] = ((mword*)obj) [6];
2102 (dest) [5] = ((mword*)obj) [5];
2104 (dest) [4] = ((mword*)obj) [4];
2106 (dest) [3] = ((mword*)obj) [3];
2108 (dest) [2] = ((mword*)obj) [2];
2110 (dest) [1] = ((mword*)obj) [1];
2112 (dest) [0] = ((mword*)obj) [0];
2120 char* edi = destination;
2121 __asm__ __volatile__(
2123 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
2124 : "0" (objsize/4), "1" (edi),"2" (esi)
2129 memcpy (destination, obj, objsize);
2132 /* adjust array->bounds */
2133 DEBUG (9, g_assert (vt->gc_descr));
2134 if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
2135 MonoArray *array = (MonoArray*)destination;
2136 array->bounds = (MonoArrayBounds*)((char*)destination + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
2137 DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %zd, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
2139 /* set the forwarding pointer */
2140 forward_object (obj, destination);
2141 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
2142 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2143 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2144 moved_objects_idx = 0;
2146 moved_objects [moved_objects_idx++] = obj;
2147 moved_objects [moved_objects_idx++] = destination;
2150 if (has_references) {
2151 DEBUG (9, fprintf (gc_debug_file, "Enqueuing gray object %p (%s)\n", obj, safe_name (obj)));
2152 GRAY_OBJECT_ENQUEUE (obj);
2158 * This is how the copying happens from the nursery to the old generation.
2159 * We assume that at this time all the pinned objects have been identified and
2161 * We run scan_object() for each pinned object so that each referenced
2162 * objects if possible are copied. The new gray objects created can have
2163 * scan_object() run on them right away, too.
2164 * Then we run copy_object() for the precisely tracked roots. At this point
2165 * all the roots are either gray or black. We run scan_object() on the gray
2166 * objects until no more gray objects are created.
2167 * At the end of the process we walk again the pinned list and we unmark
2168 * the pinned flag. As we go we also create the list of free space for use
2169 * in the next allocation runs.
2171 * We need to remember objects from the old generation that point to the new one
2172 * (or just addresses?).
2174 * copy_object could be made into a macro once debugged (use inline for now).
2177 static void __attribute__((noinline))
2178 copy_object (void **obj_slot)
2181 char *obj = *obj_slot;
2183 DEBUG (9, g_assert (current_collection_generation == GENERATION_NURSERY));
2185 HEAVY_STAT (++stat_copy_object_called_nursery);
2187 if (!ptr_in_nursery (obj)) {
2188 HEAVY_STAT (++stat_nursery_copy_object_failed_from_space);
2192 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
2195 * Before we can copy the object we must make sure that we are
2196 * allowed to, i.e. that the object not pinned or not already
2200 if ((forwarded = object_is_forwarded (obj))) {
2201 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2202 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
2203 HEAVY_STAT (++stat_nursery_copy_object_failed_forwarded);
2204 *obj_slot = forwarded;
2207 if (object_is_pinned (obj)) {
2208 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2209 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
2210 HEAVY_STAT (++stat_nursery_copy_object_failed_pinned);
2214 HEAVY_STAT (++stat_objects_copied_nursery);
2216 *obj_slot = copy_object_no_checks (obj);
2220 #define HANDLE_PTR(ptr,obj) do { \
2221 void *__old = *(ptr); \
2224 copy_object ((ptr)); \
2226 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2227 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2228 add_to_global_remset ((ptr)); \
2233 * Scan the object pointed to by @start for references to
2234 * other objects between @from_start and @from_end and copy
2235 * them to the gray_objects area.
2238 scan_object (char *start)
2240 #include "sgen-scan-object.h"
2242 HEAVY_STAT (++stat_scan_object_called_nursery);
2248 * Scan the valuetype pointed to by START, described by DESC for references to
2249 * other objects between @from_start and @from_end and copy them to the gray_objects area.
2250 * Returns a pointer to the end of the object.
2253 scan_vtype (char *start, mword desc, char* from_start, char* from_end)
2257 /* The descriptors include info about the MonoObject header as well */
2258 start -= sizeof (MonoObject);
2260 switch (desc & 0x7) {
2261 case DESC_TYPE_RUN_LENGTH:
2262 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
2263 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
2264 g_assert (skip_size);
2265 return start + skip_size;
2266 case DESC_TYPE_SMALL_BITMAP:
2267 OBJ_BITMAP_FOREACH_PTR (desc,start);
2268 OBJ_BITMAP_SIZE (skip_size, desc, start);
2269 return start + skip_size;
2270 case DESC_TYPE_LARGE_BITMAP:
2271 case DESC_TYPE_COMPLEX:
2273 g_assert_not_reached ();
2276 // The other descriptors can't happen with vtypes
2277 g_assert_not_reached ();
2284 #define HANDLE_PTR(ptr,obj) do { \
2285 void *__old = *(ptr); \
2288 major_copy_or_mark_object ((ptr)); \
2290 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2291 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2292 add_to_global_remset ((ptr)); \
2297 major_scan_object (char *start)
2299 #include "sgen-scan-object.h"
2301 HEAVY_STAT (++stat_scan_object_called_major);
2307 * Scan objects in the gray stack until the stack is empty. This should be called
2308 * frequently after each object is copied, to achieve better locality and cache
2312 drain_gray_stack (void)
2316 if (current_collection_generation == GENERATION_NURSERY) {
2318 GRAY_OBJECT_DEQUEUE (obj);
2321 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2326 GRAY_OBJECT_DEQUEUE (obj);
2329 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2330 major_scan_object (obj);
2336 * Addresses from start to end are already sorted. This function finds
2337 * the object header for each address and pins the object. The
2338 * addresses must be inside the passed section. The (start of the)
2339 * address array is overwritten with the addresses of the actually
2340 * pinned objects. Return the number of pinned objects.
2343 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
2348 void *last_obj = NULL;
2349 size_t last_obj_size = 0;
2352 void **definitely_pinned = start;
2353 while (start < end) {
2355 /* the range check should be reduntant */
2356 if (addr != last && addr >= start_nursery && addr < end_nursery) {
2357 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
2358 /* multiple pointers to the same object */
2359 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
2363 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
2364 g_assert (idx < section->num_scan_start);
2365 search_start = (void*)section->scan_starts [idx];
2366 if (!search_start || search_start > addr) {
2369 search_start = section->scan_starts [idx];
2370 if (search_start && search_start <= addr)
2373 if (!search_start || search_start > addr)
2374 search_start = start_nursery;
2376 if (search_start < last_obj)
2377 search_start = (char*)last_obj + last_obj_size;
2378 /* now addr should be in an object a short distance from search_start
2379 * Note that search_start must point to zeroed mem or point to an object.
2382 if (!*(void**)search_start) {
2383 mword p = (mword)search_start;
2384 p += sizeof (gpointer);
2385 p += ALLOC_ALIGN - 1;
2386 p &= ~(ALLOC_ALIGN - 1);
2387 search_start = (void*)p;
2390 last_obj = search_start;
2391 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
2392 last_obj_size += ALLOC_ALIGN - 1;
2393 last_obj_size &= ~(ALLOC_ALIGN - 1);
2394 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
2395 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
2396 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));
2397 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
2398 pin_object (search_start);
2399 GRAY_OBJECT_ENQUEUE (search_start);
2401 pin_stats_register_object (search_start, last_obj_size);
2402 definitely_pinned [count] = search_start;
2406 /* skip to the next object */
2407 search_start = (void*)((char*)search_start + last_obj_size);
2408 } while (search_start <= addr);
2409 /* we either pinned the correct object or we ignored the addr because
2410 * it points to unused zeroed memory.
2416 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
2421 pin_objects_in_section (GCMemSection *section)
2423 int start = section->pin_queue_start;
2424 int end = section->pin_queue_end;
2427 reduced_to = pin_objects_from_addresses (section, pin_queue + start, pin_queue + end,
2428 section->data, section->next_data);
2429 section->pin_queue_start = start;
2430 section->pin_queue_end = start + reduced_to;
2437 gap = (gap * 10) / 13;
2438 if (gap == 9 || gap == 10)
2445 /* Sort the addresses in array in increasing order.
2446 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
2449 sort_addresses (void **array, int size)
2454 for (i = 1; i < size; ++i) {
2457 int parent = (child - 1) / 2;
2459 if (array [parent] >= array [child])
2462 tmp = array [parent];
2463 array [parent] = array [child];
2464 array [child] = tmp;
2470 for (i = size - 1; i > 0; --i) {
2473 array [i] = array [0];
2479 while (root * 2 + 1 <= end) {
2480 int child = root * 2 + 1;
2482 if (child < end && array [child] < array [child + 1])
2484 if (array [root] >= array [child])
2488 array [root] = array [child];
2489 array [child] = tmp;
2496 static G_GNUC_UNUSED void
2497 print_nursery_gaps (void* start_nursery, void *end_nursery)
2500 gpointer first = start_nursery;
2502 for (i = 0; i < next_pin_slot; ++i) {
2503 next = pin_queue [i];
2504 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2508 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2511 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
2513 optimize_pin_queue (int start_slot)
2515 void **start, **cur, **end;
2516 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
2517 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
2518 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
2519 if ((next_pin_slot - start_slot) > 1)
2520 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
2521 start = cur = pin_queue + start_slot;
2522 end = pin_queue + next_pin_slot;
2525 while (*start == *cur && cur < end)
2529 next_pin_slot = start - pin_queue;
2530 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2531 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2536 * Scan the memory between start and end and queue values which could be pointers
2537 * to the area between start_nursery and end_nursery for later consideration.
2538 * Typically used for thread stacks.
2541 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2544 while (start < end) {
2545 if (*start >= start_nursery && *start < end_nursery) {
2547 * *start can point to the middle of an object
2548 * note: should we handle pointing at the end of an object?
2549 * pinning in C# code disallows pointing at the end of an object
2550 * but there is some small chance that an optimizing C compiler
2551 * may keep the only reference to an object by pointing
2552 * at the end of it. We ignore this small chance for now.
2553 * Pointers to the end of an object are indistinguishable
2554 * from pointers to the start of the next object in memory
2555 * so if we allow that we'd need to pin two objects...
2556 * We queue the pointer in an array, the
2557 * array will then be sorted and uniqued. This way
2558 * we can coalesce several pinning pointers and it should
2559 * be faster since we'd do a memory scan with increasing
2560 * addresses. Note: we can align the address to the allocation
2561 * alignment, so the unique process is more effective.
2563 mword addr = (mword)*start;
2564 addr &= ~(ALLOC_ALIGN - 1);
2565 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2566 pin_stage_ptr ((void*)addr);
2568 pin_stats_register_address ((char*)addr, pin_type);
2569 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
2574 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2578 * Debugging function: find in the conservative roots where @obj is being pinned.
2580 static G_GNUC_UNUSED void
2581 find_pinning_reference (char *obj, size_t size)
2585 char *endobj = obj + size;
2586 for (i = 0; i < roots_hash_size [0]; ++i) {
2587 for (root = roots_hash [0][i]; root; root = root->next) {
2588 /* if desc is non-null it has precise info */
2589 if (!root->root_desc) {
2590 char ** start = (char**)root->start_root;
2591 while (start < (char**)root->end_root) {
2592 if (*start >= obj && *start < endobj) {
2593 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));
2600 find_pinning_ref_from_thread (obj, size);
2604 * The first thing we do in a collection is to identify pinned objects.
2605 * This function considers all the areas of memory that need to be
2606 * conservatively scanned.
2609 pin_from_roots (void *start_nursery, void *end_nursery)
2613 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]));
2614 /* objects pinned from the API are inside these roots */
2615 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2616 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2617 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2618 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2621 /* now deal with the thread stacks
2622 * in the future we should be able to conservatively scan only:
2623 * *) the cpu registers
2624 * *) the unmanaged stack frames
2625 * *) the _last_ managed stack frame
2626 * *) pointers slots in managed frames
2628 scan_thread_data (start_nursery, end_nursery, FALSE);
2630 evacuate_pin_staging_area ();
2634 * The memory area from start_root to end_root contains pointers to objects.
2635 * Their position is precisely described by @desc (this means that the pointer
2636 * can be either NULL or the pointer to the start of an object).
2637 * This functions copies them to to_space updates them.
2640 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
2642 switch (desc & ROOT_DESC_TYPE_MASK) {
2643 case ROOT_DESC_BITMAP:
2644 desc >>= ROOT_DESC_TYPE_SHIFT;
2646 if ((desc & 1) && *start_root) {
2647 copy_func (start_root);
2648 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2649 drain_gray_stack ();
2655 case ROOT_DESC_COMPLEX: {
2656 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2657 int bwords = (*bitmap_data) - 1;
2658 void **start_run = start_root;
2660 while (bwords-- > 0) {
2661 gsize bmap = *bitmap_data++;
2662 void **objptr = start_run;
2664 if ((bmap & 1) && *objptr) {
2666 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2667 drain_gray_stack ();
2672 start_run += GC_BITS_PER_WORD;
2676 case ROOT_DESC_USER: {
2677 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2678 marker (start_root, copy_func);
2681 case ROOT_DESC_RUN_LEN:
2682 g_assert_not_reached ();
2684 g_assert_not_reached ();
2689 alloc_fragment (void)
2691 Fragment *frag = fragment_freelist;
2693 fragment_freelist = frag->next;
2697 frag = get_internal_mem (sizeof (Fragment), INTERNAL_MEM_FRAGMENT);
2702 /* size must be a power of 2 */
2704 get_os_memory_aligned (mword size, mword alignment, gboolean activate)
2706 /* Allocate twice the memory to be able to put the block on an aligned address */
2707 char *mem = get_os_memory (size + alignment, activate);
2712 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2713 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2716 free_os_memory (mem, aligned - mem);
2717 if (aligned + size < mem + size + alignment)
2718 free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2724 * Allocate and setup the data structures needed to be able to allocate objects
2725 * in the nursery. The nursery is stored in nursery_section.
2728 alloc_nursery (void)
2730 GCMemSection *section;
2736 if (nursery_section)
2738 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %zd\n", nursery_size));
2739 /* later we will alloc a larger area for the nursery but only activate
2740 * what we need. The rest will be used as expansion if we have too many pinned
2741 * objects in the existing nursery.
2743 /* FIXME: handle OOM */
2744 section = get_internal_mem (SIZEOF_GC_MEM_SECTION, INTERNAL_MEM_SECTION);
2746 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2747 alloc_size = nursery_size;
2748 #ifdef ALIGN_NURSERY
2749 data = get_os_memory_aligned (alloc_size, alloc_size, TRUE);
2751 data = get_os_memory (alloc_size, TRUE);
2753 nursery_start = data;
2754 nursery_real_end = nursery_start + nursery_size;
2755 UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
2756 nursery_next = nursery_start;
2757 total_alloc += alloc_size;
2758 DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %zd, total: %zd\n", data, data + alloc_size, nursery_size, total_alloc));
2759 section->data = section->next_data = data;
2760 section->size = alloc_size;
2761 section->end_data = nursery_real_end;
2762 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2763 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2764 section->num_scan_start = scan_starts;
2765 section->block.role = MEMORY_ROLE_GEN0;
2766 section->block.next = NULL;
2768 nursery_section = section;
2770 /* Setup the single first large fragment */
2771 frag = alloc_fragment ();
2772 frag->fragment_start = nursery_start;
2773 frag->fragment_limit = nursery_start;
2774 frag->fragment_end = nursery_real_end;
2775 nursery_frag_real_end = nursery_real_end;
2776 /* FIXME: frag here is lost */
2780 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list) {
2783 for (fin = list; fin; fin = fin->next) {
2786 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2787 copy_func (&fin->object);
2791 static mword fragment_total = 0;
2793 * We found a fragment of free memory in the nursery: memzero it and if
2794 * it is big enough, add it to the list of fragments that can be used for
2798 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2801 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2802 binary_protocol_empty (frag_start, frag_size);
2803 /* memsetting just the first chunk start is bound to provide better cache locality */
2804 if (nursery_clear_policy == CLEAR_AT_GC)
2805 memset (frag_start, 0, frag_size);
2806 /* Not worth dealing with smaller fragments: need to tune */
2807 if (frag_size >= FRAGMENT_MIN_SIZE) {
2808 fragment = alloc_fragment ();
2809 fragment->fragment_start = frag_start;
2810 fragment->fragment_limit = frag_start;
2811 fragment->fragment_end = frag_end;
2812 fragment->next = nursery_fragments;
2813 nursery_fragments = fragment;
2814 fragment_total += frag_size;
2816 /* Clear unused fragments, pinning depends on this */
2817 /*TODO place an int[] here instead of the memset if size justify it*/
2818 memset (frag_start, 0, frag_size);
2823 generation_name (int generation)
2825 switch (generation) {
2826 case GENERATION_NURSERY: return "nursery";
2827 case GENERATION_OLD: return "old";
2828 default: g_assert_not_reached ();
2832 static DisappearingLinkHashTable*
2833 get_dislink_hash_table (int generation)
2835 switch (generation) {
2836 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2837 case GENERATION_OLD: return &major_disappearing_link_hash;
2838 default: g_assert_not_reached ();
2842 static FinalizeEntryHashTable*
2843 get_finalize_entry_hash_table (int generation)
2845 switch (generation) {
2846 case GENERATION_NURSERY: return &minor_finalizable_hash;
2847 case GENERATION_OLD: return &major_finalizable_hash;
2848 default: g_assert_not_reached ();
2853 finish_gray_stack (char *start_addr, char *end_addr, int generation)
2858 int ephemeron_rounds = 0;
2859 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? copy_object : major_copy_or_mark_object;
2862 * We copied all the reachable objects. Now it's the time to copy
2863 * the objects that were not referenced by the roots, but by the copied objects.
2864 * we built a stack of objects pointed to by gray_start: they are
2865 * additional roots and we may add more items as we go.
2866 * We loop until gray_start == gray_objects which means no more objects have
2867 * been added. Note this is iterative: no recursion is involved.
2868 * We need to walk the LO list as well in search of marked big objects
2869 * (use a flag since this is needed only on major collections). We need to loop
2870 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2871 * To achieve better cache locality and cache usage, we drain the gray stack
2872 * frequently, after each object is copied, and just finish the work here.
2874 drain_gray_stack ();
2876 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2877 /* walk the finalization queue and move also the objects that need to be
2878 * finalized: use the finalized objects as new roots so the objects they depend
2879 * on are also not reclaimed. As with the roots above, only objects in the nursery
2880 * are marked/copied.
2881 * We need a loop here, since objects ready for finalizers may reference other objects
2882 * that are fin-ready. Speedup with a flag?
2886 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2887 * before processing finalizable objects to avoid finalizing reachable values.
2889 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2890 * while they are been finalized.
2892 int done_with_ephemerons = 0;
2894 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr);
2895 drain_gray_stack ();
2897 } while (!done_with_ephemerons);
2899 fin_ready = num_ready_finalizers;
2900 finalize_in_range (copy_func, start_addr, end_addr, generation);
2901 if (generation == GENERATION_OLD)
2902 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY);
2904 /* drain the new stack that might have been created */
2905 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2906 drain_gray_stack ();
2907 } while (fin_ready != num_ready_finalizers);
2910 * Clear ephemeron pairs with unreachable keys.
2911 * We pass the copy func so we can figure out if an array was promoted or not.
2913 clear_unreachable_ephemerons (copy_func, start_addr, end_addr);
2916 DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron roundss\n", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds));
2919 * handle disappearing links
2920 * Note we do this after checking the finalization queue because if an object
2921 * survives (at least long enough to be finalized) we don't clear the link.
2922 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2923 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2926 g_assert (gray_object_queue_is_empty ());
2928 null_link_in_range (copy_func, start_addr, end_addr, generation);
2929 if (generation == GENERATION_OLD)
2930 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY);
2931 if (gray_object_queue_is_empty ())
2933 drain_gray_stack ();
2936 g_assert (gray_object_queue_is_empty ());
2940 check_section_scan_starts (GCMemSection *section)
2943 for (i = 0; i < section->num_scan_start; ++i) {
2944 if (section->scan_starts [i]) {
2945 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2946 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2952 check_scan_starts (void)
2954 if (!do_scan_starts_check)
2956 check_section_scan_starts (nursery_section);
2957 major_check_scan_starts ();
2960 static int last_num_pinned = 0;
2963 build_nursery_fragments (int start_pin, int end_pin)
2965 char *frag_start, *frag_end;
2969 while (nursery_fragments) {
2970 Fragment *next = nursery_fragments->next;
2971 nursery_fragments->next = fragment_freelist;
2972 fragment_freelist = nursery_fragments;
2973 nursery_fragments = next;
2975 frag_start = nursery_start;
2977 /* clear scan starts */
2978 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2979 for (i = start_pin; i < end_pin; ++i) {
2980 frag_end = pin_queue [i];
2981 /* remove the pin bit from pinned objects */
2982 unpin_object (frag_end);
2983 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2984 frag_size = frag_end - frag_start;
2986 add_nursery_frag (frag_size, frag_start, frag_end);
2987 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
2988 frag_size += ALLOC_ALIGN - 1;
2989 frag_size &= ~(ALLOC_ALIGN - 1);
2990 frag_start = (char*)pin_queue [i] + frag_size;
2992 nursery_last_pinned_end = frag_start;
2993 frag_end = nursery_real_end;
2994 frag_size = frag_end - frag_start;
2996 add_nursery_frag (frag_size, frag_start, frag_end);
2997 if (!nursery_fragments) {
2998 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
2999 for (i = start_pin; i < end_pin; ++i) {
3000 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])));
3005 nursery_next = nursery_frag_real_end = NULL;
3007 /* Clear TLABs for all threads */
3012 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type)
3016 for (i = 0; i < roots_hash_size [root_type]; ++i) {
3017 for (root = roots_hash [root_type][i]; root; root = root->next) {
3018 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
3019 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
3025 dump_occupied (char *start, char *end, char *section_start)
3027 fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
3031 dump_section (GCMemSection *section, const char *type)
3033 char *start = section->data;
3034 char *end = section->data + section->size;
3035 char *occ_start = NULL;
3037 char *old_start = NULL; /* just for debugging */
3039 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", type, section->size);
3041 while (start < end) {
3045 if (!*(void**)start) {
3047 dump_occupied (occ_start, start, section->data);
3050 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
3053 g_assert (start < section->next_data);
3058 vt = (GCVTable*)LOAD_VTABLE (start);
3061 size = safe_object_get_size ((MonoObject*) start);
3062 size += ALLOC_ALIGN - 1;
3063 size &= ~(ALLOC_ALIGN - 1);
3066 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
3067 start - section->data,
3068 vt->klass->name_space, vt->klass->name,
3076 dump_occupied (occ_start, start, section->data);
3078 fprintf (heap_dump_file, "</section>\n");
3082 dump_object (MonoObject *obj, gboolean dump_location)
3084 static char class_name [1024];
3086 MonoClass *class = mono_object_class (obj);
3090 * Python's XML parser is too stupid to parse angle brackets
3091 * in strings, so we just ignore them;
3094 while (class->name [i] && j < sizeof (class_name) - 1) {
3095 if (!strchr ("<>\"", class->name [i]))
3096 class_name [j++] = class->name [i];
3099 g_assert (j < sizeof (class_name));
3102 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
3103 class->name_space, class_name,
3104 safe_object_get_size (obj));
3105 if (dump_location) {
3106 const char *location;
3107 if (ptr_in_nursery (obj))
3108 location = "nursery";
3109 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
3113 fprintf (heap_dump_file, " location=\"%s\"", location);
3115 fprintf (heap_dump_file, "/>\n");
3119 dump_heap (const char *type, int num, const char *reason)
3121 static char const *internal_mem_names [] = { "pin-queue", "fragment", "section", "scan-starts",
3122 "fin-table", "finalize-entry", "dislink-table",
3123 "dislink", "roots-table", "root-record", "statistics",
3124 "remset", "gray-queue", "store-remset", "marksweep-tables",
3125 "marksweep-block-info", "ephemeron-link" };
3131 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
3133 fprintf (heap_dump_file, " reason=\"%s\"", reason);
3134 fprintf (heap_dump_file, ">\n");
3135 fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%lld\"/>\n", pinned_chunk_bytes_alloced);
3136 fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%lld\"/>\n", large_internal_bytes_alloced);
3137 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
3138 for (i = 0; i < INTERNAL_MEM_MAX; ++i)
3139 fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n", internal_mem_names [i], small_internal_mem_bytes [i]);
3140 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
3141 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
3142 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
3144 fprintf (heap_dump_file, "<pinned-objects>\n");
3145 for (list = pinned_objects; list; list = list->next)
3146 dump_object (list->obj, TRUE);
3147 fprintf (heap_dump_file, "</pinned-objects>\n");
3149 dump_section (nursery_section, "nursery");
3153 fprintf (heap_dump_file, "<los>\n");
3154 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
3155 dump_object ((MonoObject*)bigobj->data, FALSE);
3156 fprintf (heap_dump_file, "</los>\n");
3158 fprintf (heap_dump_file, "</collection>\n");
3164 static gboolean inited = FALSE;
3169 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
3170 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
3171 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
3172 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
3173 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
3174 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
3175 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3176 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
3178 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
3179 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
3180 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
3181 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
3182 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
3183 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
3184 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
3185 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
3186 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
3187 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
3188 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
3189 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
3190 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
3192 #ifdef HEAVY_STATISTICS
3193 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
3194 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
3195 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
3196 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
3197 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
3198 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
3199 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
3200 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
3202 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
3203 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
3204 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
3205 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
3206 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
3208 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
3209 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
3210 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
3211 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
3213 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
3214 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
3216 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
3217 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
3218 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
3220 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
3221 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
3223 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
3224 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
3225 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
3226 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
3227 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
3228 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
3229 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
3230 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
3238 need_major_collection (void)
3240 mword los_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
3241 return minor_collection_sections_alloced * MAJOR_SECTION_SIZE + los_alloced > minor_collection_allowance;
3245 * Collect objects in the nursery. Returns whether to trigger a major
3249 collect_nursery (size_t requested_size)
3251 size_t max_garbage_amount;
3252 char *orig_nursery_next;
3253 TV_DECLARE (all_atv);
3254 TV_DECLARE (all_btv);
3258 current_collection_generation = GENERATION_NURSERY;
3261 binary_protocol_collection (GENERATION_NURSERY);
3262 check_scan_starts ();
3265 orig_nursery_next = nursery_next;
3266 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3267 /* FIXME: optimize later to use the higher address where an object can be present */
3268 nursery_next = MAX (nursery_next, nursery_real_end);
3270 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)));
3271 max_garbage_amount = nursery_next - nursery_start;
3272 g_assert (nursery_section->size >= max_garbage_amount);
3274 /* world must be stopped already */
3275 TV_GETTIME (all_atv);
3278 /* Pinning depends on this */
3279 clear_nursery_fragments (orig_nursery_next);
3282 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3285 check_for_xdomain_refs ();
3287 nursery_section->next_data = nursery_next;
3289 major_start_nursery_collection ();
3291 gray_object_queue_init ();
3294 mono_stats.minor_gc_count ++;
3296 global_remset_cache_clear ();
3298 /* pin from pinned handles */
3300 pin_from_roots (nursery_start, nursery_next);
3301 /* identify pinned objects */
3302 optimize_pin_queue (0);
3303 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
3304 nursery_section->pin_queue_start = 0;
3305 nursery_section->pin_queue_end = next_pin_slot;
3307 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3308 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3309 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3311 if (consistency_check_at_minor_collection)
3312 check_consistency ();
3315 * walk all the roots and copy the young objects to the old generation,
3316 * starting from to_space
3319 scan_from_remsets (nursery_start, nursery_next);
3320 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3322 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3323 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3325 drain_gray_stack ();
3328 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3329 /* registered roots, this includes static fields */
3330 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL);
3331 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER);
3333 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3335 scan_thread_data (nursery_start, nursery_next, TRUE);
3337 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3340 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY);
3342 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3344 /* walk the pin_queue, build up the fragment list of free memory, unmark
3345 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3348 build_nursery_fragments (0, next_pin_slot);
3350 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3351 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %zd bytes available\n", TV_ELAPSED (atv, btv), fragment_total));
3353 if (consistency_check_at_minor_collection)
3354 check_major_refs ();
3356 major_finish_nursery_collection ();
3358 TV_GETTIME (all_btv);
3359 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3362 dump_heap ("minor", num_minor_gcs - 1, NULL);
3364 /* prepare the pin queue for the next collection */
3365 last_num_pinned = next_pin_slot;
3367 if (fin_ready_list || critical_fin_list) {
3368 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3369 mono_gc_finalize_notify ();
3373 g_assert (gray_object_queue_is_empty ());
3375 check_scan_starts ();
3377 current_collection_generation = -1;
3379 return need_major_collection ();
3383 major_do_collection (const char *reason)
3385 LOSObject *bigobj, *prevbo;
3386 TV_DECLARE (all_atv);
3387 TV_DECLARE (all_btv);
3390 /* FIXME: only use these values for the precise scan
3391 * note that to_space pointers should be excluded anyway...
3393 char *heap_start = NULL;
3394 char *heap_end = (char*)-1;
3395 int old_num_major_sections = num_major_sections;
3396 int num_major_sections_saved, save_target, allowance_target;
3397 mword los_memory_saved, los_memory_alloced, old_los_memory_usage;
3400 * A domain could have been freed, resulting in
3401 * los_memory_usage being less than last_los_memory_usage.
3403 los_memory_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
3404 old_los_memory_usage = los_memory_usage;
3406 //count_ref_nonref_objs ();
3407 //consistency_check ();
3410 binary_protocol_collection (GENERATION_OLD);
3411 check_scan_starts ();
3412 gray_object_queue_init ();
3415 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3417 mono_stats.major_gc_count ++;
3419 /* world must be stopped already */
3420 TV_GETTIME (all_atv);
3423 /* Pinning depends on this */
3424 clear_nursery_fragments (nursery_next);
3427 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3430 check_for_xdomain_refs ();
3432 nursery_section->next_data = nursery_real_end;
3433 /* we should also coalesce scanning from sections close to each other
3434 * and deal with pointers outside of the sections later.
3436 /* The remsets are not useful for a major collection */
3438 global_remset_cache_clear ();
3442 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3443 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3444 optimize_pin_queue (0);
3447 * pin_queue now contains all candidate pointers, sorted and
3448 * uniqued. We must do two passes now to figure out which
3449 * objects are pinned.
3451 * The first is to find within the pin_queue the area for each
3452 * section. This requires that the pin_queue be sorted. We
3453 * also process the LOS objects and pinned chunks here.
3455 * The second, destructive, pass is to reduce the section
3456 * areas to pointers to the actually pinned objects.
3458 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3459 /* first pass for the sections */
3460 find_section_pin_queue_start_end (nursery_section);
3461 major_find_pin_queue_start_ends ();
3462 /* identify possible pointers to the insize of large objects */
3463 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3464 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3466 find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &start, &end);
3468 pin_object (bigobj->data);
3469 /* FIXME: only enqueue if object has references */
3470 GRAY_OBJECT_ENQUEUE (bigobj->data);
3472 pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3473 DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
3476 /* second pass for the sections */
3477 pin_objects_in_section (nursery_section);
3478 major_pin_objects ();
3481 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3482 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3483 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3485 major_init_to_space ();
3487 drain_gray_stack ();
3490 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3492 /* registered roots, this includes static fields */
3493 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL);
3494 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER);
3496 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3499 /* FIXME: This is the wrong place for this, because it does
3501 scan_thread_data (heap_start, heap_end, TRUE);
3503 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3506 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3508 /* scan the list of objects ready for finalization */
3509 scan_finalizer_entries (major_copy_or_mark_object, fin_ready_list);
3510 scan_finalizer_entries (major_copy_or_mark_object, critical_fin_list);
3512 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3513 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3516 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3518 /* all the objects in the heap */
3519 finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
3521 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3523 /* sweep the big objects list */
3525 for (bigobj = los_object_list; bigobj;) {
3526 if (object_is_pinned (bigobj->data)) {
3527 unpin_object (bigobj->data);
3530 /* not referenced anywhere, so we can free it */
3532 prevbo->next = bigobj->next;
3534 los_object_list = bigobj->next;
3536 bigobj = bigobj->next;
3537 free_large_object (to_free);
3541 bigobj = bigobj->next;
3545 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3550 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3555 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3557 /* walk the pin_queue, build up the fragment list of free memory, unmark
3558 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3561 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
3564 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3566 TV_GETTIME (all_btv);
3567 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3570 dump_heap ("major", num_major_gcs - 1, reason);
3572 /* prepare the pin queue for the next collection */
3574 if (fin_ready_list || critical_fin_list) {
3575 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3576 mono_gc_finalize_notify ();
3580 g_assert (gray_object_queue_is_empty ());
3582 num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 0);
3583 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
3585 save_target = ((num_major_sections * MAJOR_SECTION_SIZE) + los_memory_saved) / 2;
3587 * We aim to allow the allocation of as many sections as is
3588 * necessary to reclaim save_target sections in the next
3589 * collection. We assume the collection pattern won't change.
3590 * In the last cycle, we had num_major_sections_saved for
3591 * minor_collection_sections_alloced. Assuming things won't
3592 * change, this must be the same ratio as save_target for
3593 * allowance_target, i.e.
3595 * num_major_sections_saved save_target
3596 * --------------------------------- == ----------------
3597 * minor_collection_sections_alloced allowance_target
3601 allowance_target = (mword)((double)save_target * (double)(minor_collection_sections_alloced * MAJOR_SECTION_SIZE + los_memory_alloced) / (double)(num_major_sections_saved * MAJOR_SECTION_SIZE + los_memory_saved));
3603 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * MAJOR_SECTION_SIZE + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
3605 minor_collection_sections_alloced = 0;
3606 last_los_memory_usage = los_memory_usage;
3608 check_scan_starts ();
3610 //consistency_check ();
3614 major_collection (const char *reason)
3616 if (g_getenv ("MONO_GC_NO_MAJOR")) {
3617 collect_nursery (0);
3621 current_collection_generation = GENERATION_OLD;
3622 major_do_collection (reason);
3623 current_collection_generation = -1;
3627 * When deciding if it's better to collect or to expand, keep track
3628 * of how much garbage was reclaimed with the last collection: if it's too
3630 * This is called when we could not allocate a small object.
3632 static void __attribute__((noinline))
3633 minor_collect_or_expand_inner (size_t size)
3635 int do_minor_collection = 1;
3637 if (!nursery_section) {
3641 if (do_minor_collection) {
3643 if (collect_nursery (size))
3644 major_collection ("minor overflow");
3645 DEBUG (2, fprintf (gc_debug_file, "Heap size: %zd, LOS size: %zd\n", total_alloc, los_memory_usage));
3647 /* this also sets the proper pointers for the next allocation */
3648 if (!search_fragment_for_size (size)) {
3650 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3651 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3652 for (i = 0; i < last_num_pinned; ++i) {
3653 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])));
3658 //report_internal_mem_usage ();
3662 * ######################################################################
3663 * ######## Memory allocation from the OS
3664 * ######################################################################
3665 * This section of code deals with getting memory from the OS and
3666 * allocating memory for GC-internal data structures.
3667 * Internal memory can be handled with a freelist for small objects.
3671 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3672 * This must not require any lock.
3675 get_os_memory (size_t size, int activate)
3678 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3680 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3681 size += pagesize - 1;
3682 size &= ~(pagesize - 1);
3683 ptr = mono_valloc (0, size, prot_flags);
3688 * Free the memory returned by get_os_memory (), returning it to the OS.
3691 free_os_memory (void *addr, size_t size)
3693 mono_vfree (addr, size);
3700 report_pinned_chunk (PinnedChunk *chunk, int seq) {
3702 int i, free_pages, num_free, free_mem;
3704 for (i = 0; i < chunk->num_pages; ++i) {
3705 if (!chunk->page_sizes [i])
3708 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);
3709 free_mem = FREELIST_PAGESIZE * free_pages;
3710 for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
3711 if (!chunk->free_list [i])
3714 p = chunk->free_list [i];
3719 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
3720 free_mem += freelist_sizes [i] * num_free;
3722 printf ("\tfree memory in chunk: %d\n", free_mem);
3728 static G_GNUC_UNUSED void
3729 report_internal_mem_usage (void) {
3732 printf ("Internal memory usage:\n");
3734 for (chunk = internal_chunk_list; chunk; chunk = chunk->block.next) {
3735 report_pinned_chunk (chunk, i++);
3737 printf ("Pinned memory usage:\n");
3738 major_report_pinned_memory_usage ();
3742 * Find the slot number in the freelist for memory chunks that
3743 * can contain @size objects.
3746 slot_for_size (size_t size)
3749 /* do a binary search or lookup table later. */
3750 for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
3751 if (freelist_sizes [slot] >= size)
3754 g_assert_not_reached ();
3759 * Build a free list for @size memory chunks from the memory area between
3760 * start_page and end_page.
3763 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
3767 /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
3768 p = (void**)start_page;
3769 end = (void**)(end_page - size);
3770 g_assert (!chunk->free_list [slot]);
3771 chunk->free_list [slot] = p;
3772 while ((char*)p + size <= (char*)end) {
3774 *p = (void*)((char*)p + size);
3778 /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
3782 alloc_pinned_chunk (void)
3786 int size = PINNED_CHUNK_SIZE;
3788 chunk = get_os_memory_aligned (size, size, TRUE);
3789 chunk->block.role = MEMORY_ROLE_PINNED;
3791 UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
3792 total_alloc += size;
3793 pinned_chunk_bytes_alloced += size;
3795 /* setup the bookeeping fields */
3796 chunk->num_pages = size / FREELIST_PAGESIZE;
3797 offset = G_STRUCT_OFFSET (PinnedChunk, data);
3798 chunk->page_sizes = (void*)((char*)chunk + offset);
3799 offset += sizeof (int) * chunk->num_pages;
3800 offset += ALLOC_ALIGN - 1;
3801 offset &= ~(ALLOC_ALIGN - 1);
3802 chunk->free_list = (void*)((char*)chunk + offset);
3803 offset += sizeof (void*) * FREELIST_NUM_SLOTS;
3804 offset += ALLOC_ALIGN - 1;
3805 offset &= ~(ALLOC_ALIGN - 1);
3806 chunk->start_data = (void*)((char*)chunk + offset);
3808 /* allocate the first page to the freelist */
3809 chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
3810 build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
3811 DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
3815 /* assumes freelist for slot is empty, so try to alloc a new page */
3817 get_chunk_freelist (PinnedChunk *chunk, int slot)
3821 p = chunk->free_list [slot];
3823 chunk->free_list [slot] = *p;
3826 for (i = 0; i < chunk->num_pages; ++i) {
3828 if (chunk->page_sizes [i])
3830 size = freelist_sizes [slot];
3831 chunk->page_sizes [i] = size;
3832 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
3836 p = chunk->free_list [slot];
3838 chunk->free_list [slot] = *p;
3844 /* used for the GC-internal data structures */
3846 get_internal_mem (size_t size, int type)
3850 PinnedChunk *pchunk;
3852 if (size > freelist_sizes [FREELIST_NUM_SLOTS - 1]) {
3853 LargeInternalMemHeader *mh;
3855 size += sizeof (LargeInternalMemHeader);
3856 mh = get_os_memory (size, TRUE);
3857 mh->magic = LARGE_INTERNAL_MEM_HEADER_MAGIC;
3860 large_internal_bytes_alloced += size;
3865 slot = slot_for_size (size);
3866 g_assert (size <= freelist_sizes [slot]);
3868 small_internal_mem_bytes [type] += freelist_sizes [slot];
3870 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3871 void **p = pchunk->free_list [slot];
3873 pchunk->free_list [slot] = *p;
3874 memset (p, 0, size);
3878 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3879 res = get_chunk_freelist (pchunk, slot);
3881 memset (res, 0, size);
3885 pchunk = alloc_pinned_chunk ();
3886 /* FIXME: handle OOM */
3887 pchunk->block.next = internal_chunk_list;
3888 internal_chunk_list = pchunk;
3889 res = get_chunk_freelist (pchunk, slot);
3890 memset (res, 0, size);
3895 free_internal_mem (void *addr, int type)
3897 PinnedChunk *pchunk;
3898 LargeInternalMemHeader *mh;
3901 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3902 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
3903 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
3904 int offset = (char*)addr - (char*)pchunk;
3905 int page = offset / FREELIST_PAGESIZE;
3906 int slot = slot_for_size (pchunk->page_sizes [page]);
3908 *p = pchunk->free_list [slot];
3909 pchunk->free_list [slot] = p;
3911 small_internal_mem_bytes [type] -= freelist_sizes [slot];
3916 mh = (LargeInternalMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargeInternalMemHeader, data));
3917 g_assert (mh->magic == LARGE_INTERNAL_MEM_HEADER_MAGIC);
3918 large_internal_bytes_alloced -= mh->size;
3919 free_os_memory (mh, mh->size);
3923 * ######################################################################
3924 * ######## Object allocation
3925 * ######################################################################
3926 * This section of code deals with allocating memory for objects.
3927 * There are several ways:
3928 * *) allocate large objects
3929 * *) allocate normal objects
3930 * *) fast lock-free allocation
3931 * *) allocation of pinned objects
3935 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3937 /* remove from the list */
3939 prev->next = frag->next;
3941 nursery_fragments = frag->next;
3942 nursery_next = frag->fragment_start;
3943 nursery_frag_real_end = frag->fragment_end;
3945 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));
3946 frag->next = fragment_freelist;
3947 fragment_freelist = frag;
3950 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3951 * an object of size @size
3952 * Return FALSE if not found (which means we need a collection)
3955 search_fragment_for_size (size_t size)
3957 Fragment *frag, *prev;
3958 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3960 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3961 /* Clear the remaining space, pinning depends on this */
3962 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3965 for (frag = nursery_fragments; frag; frag = frag->next) {
3966 if (size <= (frag->fragment_end - frag->fragment_start)) {
3967 setup_fragment (frag, prev, size);
3976 * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3977 * This improves nursery usage.
3980 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3982 Fragment *frag, *prev, *min_prev;
3983 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, desired size: %zd minimum size %zd\n", nursery_frag_real_end, desired_size, minimum_size));
3985 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3986 /* Clear the remaining space, pinning depends on this */
3987 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3989 min_prev = GINT_TO_POINTER (-1);
3992 for (frag = nursery_fragments; frag; frag = frag->next) {
3993 int frag_size = frag->fragment_end - frag->fragment_start;
3994 if (desired_size <= frag_size) {
3995 setup_fragment (frag, prev, desired_size);
3996 return desired_size;
3998 if (minimum_size <= frag_size)
4004 if (min_prev != GINT_TO_POINTER (-1)) {
4007 frag = min_prev->next;
4009 frag = nursery_fragments;
4011 frag_size = frag->fragment_end - frag->fragment_start;
4012 HEAVY_STAT (++stat_wasted_fragments_used);
4013 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
4015 setup_fragment (frag, min_prev, minimum_size);
4023 alloc_degraded (MonoVTable *vtable, size_t size)
4025 if (need_major_collection ()) {
4027 major_collection ("degraded overflow");
4031 return major_alloc_degraded (vtable, size);
4035 * Provide a variant that takes just the vtable for small fixed-size objects.
4036 * The aligned size is already computed and stored in vt->gc_descr.
4037 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
4038 * processing. We can keep track of where objects start, for example,
4039 * so when we scan the thread stacks for pinned objects, we can start
4040 * a search for the pinned object in SCAN_START_SIZE chunks.
4043 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4045 /* FIXME: handle OOM */
4050 HEAVY_STAT (++stat_objects_alloced);
4051 if (size <= MAX_SMALL_OBJ_SIZE)
4052 HEAVY_STAT (stat_bytes_alloced += size);
4054 HEAVY_STAT (stat_bytes_alloced_los += size);
4056 size += ALLOC_ALIGN - 1;
4057 size &= ~(ALLOC_ALIGN - 1);
4059 g_assert (vtable->gc_descr);
4061 if (G_UNLIKELY (collect_before_allocs)) {
4062 if (nursery_section) {
4064 collect_nursery (0);
4066 if (!degraded_mode && !search_fragment_for_size (size)) {
4068 g_assert_not_reached ();
4074 * We must already have the lock here instead of after the
4075 * fast path because we might be interrupted in the fast path
4076 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
4077 * and we'll end up allocating an object in a fragment which
4078 * no longer belongs to us.
4080 * The managed allocator does not do this, but it's treated
4081 * specially by the world-stopping code.
4084 if (size > MAX_SMALL_OBJ_SIZE) {
4085 p = alloc_large_inner (vtable, size);
4087 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4089 p = (void**)TLAB_NEXT;
4090 /* FIXME: handle overflow */
4091 new_next = (char*)p + size;
4092 TLAB_NEXT = new_next;
4094 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4098 * FIXME: We might need a memory barrier here so the change to tlab_next is
4099 * visible before the vtable store.
4102 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4103 binary_protocol_alloc (p , vtable, size);
4104 g_assert (*p == NULL);
4107 g_assert (TLAB_NEXT == new_next);
4114 /* there are two cases: the object is too big or we run out of space in the TLAB */
4115 /* we also reach here when the thread does its first allocation after a minor
4116 * collection, since the tlab_ variables are initialized to NULL.
4117 * there can be another case (from ORP), if we cooperate with the runtime a bit:
4118 * objects that need finalizers can have the high bit set in their size
4119 * so the above check fails and we can readily add the object to the queue.
4120 * This avoids taking again the GC lock when registering, but this is moot when
4121 * doing thread-local allocation, so it may not be a good idea.
4123 g_assert (TLAB_NEXT == new_next);
4124 if (TLAB_NEXT >= TLAB_REAL_END) {
4126 * Run out of space in the TLAB. When this happens, some amount of space
4127 * remains in the TLAB, but not enough to satisfy the current allocation
4128 * request. Currently, we retire the TLAB in all cases, later we could
4129 * keep it if the remaining space is above a treshold, and satisfy the
4130 * allocation directly from the nursery.
4133 /* when running in degraded mode, we continue allocing that way
4134 * for a while, to decrease the number of useless nursery collections.
4136 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
4137 p = alloc_degraded (vtable, size);
4138 binary_protocol_alloc_degraded (p, vtable, size);
4142 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
4143 if (size > tlab_size) {
4144 /* Allocate directly from the nursery */
4145 if (nursery_next + size >= nursery_frag_real_end) {
4146 if (!search_fragment_for_size (size)) {
4147 minor_collect_or_expand_inner (size);
4148 if (degraded_mode) {
4149 p = alloc_degraded (vtable, size);
4150 binary_protocol_alloc_degraded (p, vtable, size);
4156 p = (void*)nursery_next;
4157 nursery_next += size;
4158 if (nursery_next > nursery_frag_real_end) {
4163 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4164 memset (p, 0, size);
4166 int alloc_size = tlab_size;
4167 int available_in_nursery = nursery_frag_real_end - nursery_next;
4169 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
4171 if (alloc_size >= available_in_nursery) {
4172 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
4173 alloc_size = available_in_nursery;
4175 alloc_size = search_fragment_for_size_range (tlab_size, size);
4177 alloc_size = tlab_size;
4178 minor_collect_or_expand_inner (tlab_size);
4179 if (degraded_mode) {
4180 p = alloc_degraded (vtable, size);
4181 binary_protocol_alloc_degraded (p, vtable, size);
4188 /* Allocate a new TLAB from the current nursery fragment */
4189 TLAB_START = nursery_next;
4190 nursery_next += alloc_size;
4191 TLAB_NEXT = TLAB_START;
4192 TLAB_REAL_END = TLAB_START + alloc_size;
4193 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
4195 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4196 memset (TLAB_START, 0, alloc_size);
4198 /* Allocate from the TLAB */
4199 p = (void*)TLAB_NEXT;
4201 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4203 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4206 /* Reached tlab_temp_end */
4208 /* record the scan start so we can find pinned objects more easily */
4209 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4210 /* we just bump tlab_temp_end as well */
4211 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4212 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4216 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4217 binary_protocol_alloc (p, vtable, size);
4224 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4230 size += ALLOC_ALIGN - 1;
4231 size &= ~(ALLOC_ALIGN - 1);
4233 g_assert (vtable->gc_descr);
4234 if (size <= MAX_SMALL_OBJ_SIZE) {
4235 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4237 p = (void**)TLAB_NEXT;
4238 /* FIXME: handle overflow */
4239 new_next = (char*)p + size;
4240 TLAB_NEXT = new_next;
4242 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4246 * FIXME: We might need a memory barrier here so the change to tlab_next is
4247 * visible before the vtable store.
4250 HEAVY_STAT (++stat_objects_alloced);
4251 HEAVY_STAT (stat_bytes_alloced += size);
4253 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4254 binary_protocol_alloc (p, vtable, size);
4255 g_assert (*p == NULL);
4258 g_assert (TLAB_NEXT == new_next);
4267 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4270 #ifndef DISABLE_CRITICAL_REGION
4272 ENTER_CRITICAL_REGION;
4273 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4275 EXIT_CRITICAL_REGION;
4278 EXIT_CRITICAL_REGION;
4281 res = mono_gc_alloc_obj_nolock (vtable, size);
4287 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4290 #ifndef DISABLE_CRITICAL_REGION
4292 ENTER_CRITICAL_REGION;
4293 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4295 arr->max_length = max_length;
4296 EXIT_CRITICAL_REGION;
4299 EXIT_CRITICAL_REGION;
4304 arr = mono_gc_alloc_obj_nolock (vtable, size);
4305 arr->max_length = max_length;
4313 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4316 MonoArrayBounds *bounds;
4320 arr = mono_gc_alloc_obj_nolock (vtable, size);
4321 arr->max_length = max_length;
4323 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4324 arr->bounds = bounds;
4332 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4335 #ifndef DISABLE_CRITICAL_REGION
4337 ENTER_CRITICAL_REGION;
4338 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4341 EXIT_CRITICAL_REGION;
4344 EXIT_CRITICAL_REGION;
4349 str = mono_gc_alloc_obj_nolock (vtable, size);
4358 * To be used for interned strings and possibly MonoThread, reflection handles.
4359 * We may want to explicitly free these objects.
4362 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4364 /* FIXME: handle OOM */
4366 size += ALLOC_ALIGN - 1;
4367 size &= ~(ALLOC_ALIGN - 1);
4369 if (size > MAX_SMALL_OBJ_SIZE) {
4370 /* large objects are always pinned anyway */
4371 p = alloc_large_inner (vtable, size);
4373 DEBUG (9, g_assert (vtable->klass->inited));
4374 p = major_alloc_small_pinned_obj (size, vtable->klass->has_references);
4376 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4377 binary_protocol_alloc_pinned (p, vtable, size);
4384 * ######################################################################
4385 * ######## Finalization support
4386 * ######################################################################
4390 * this is valid for the nursery: if the object has been forwarded it means it's
4391 * still refrenced from a root. If it is pinned it's still alive as well.
4392 * Return TRUE if @obj is ready to be finalized.
4394 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4397 is_critical_finalizer (FinalizeEntry *entry)
4402 if (!mono_defaults.critical_finalizer_object)
4405 obj = entry->object;
4406 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4408 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4412 queue_finalization_entry (FinalizeEntry *entry) {
4413 if (is_critical_finalizer (entry)) {
4414 entry->next = critical_fin_list;
4415 critical_fin_list = entry;
4417 entry->next = fin_ready_list;
4418 fin_ready_list = entry;
4422 /* LOCKING: requires that the GC lock is held */
4424 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4426 FinalizeEntry **finalizable_hash = hash_table->table;
4427 mword finalizable_hash_size = hash_table->size;
4430 FinalizeEntry **new_hash;
4431 FinalizeEntry *entry, *next;
4432 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4434 new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4435 for (i = 0; i < finalizable_hash_size; ++i) {
4436 for (entry = finalizable_hash [i]; entry; entry = next) {
4437 hash = mono_object_hash (entry->object) % new_size;
4439 entry->next = new_hash [hash];
4440 new_hash [hash] = entry;
4443 free_internal_mem (finalizable_hash, INTERNAL_MEM_FIN_TABLE);
4444 hash_table->table = new_hash;
4445 hash_table->size = new_size;
4448 /* LOCKING: requires that the GC lock is held */
4450 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4452 if (hash_table->num_registered >= hash_table->size * 2)
4453 rehash_fin_table (hash_table);
4456 /* LOCKING: requires that the GC lock is held */
4458 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4460 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4461 FinalizeEntry *entry, *prev;
4463 FinalizeEntry **finalizable_hash = hash_table->table;
4464 mword finalizable_hash_size = hash_table->size;
4468 for (i = 0; i < finalizable_hash_size; ++i) {
4470 for (entry = finalizable_hash [i]; entry;) {
4471 if ((char*)entry->object >= start && (char*)entry->object < end && !major_is_object_live (entry->object)) {
4472 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4473 char *copy = entry->object;
4474 copy_func ((void**)©);
4477 FinalizeEntry *next;
4478 /* remove and put in fin_ready_list */
4480 prev->next = entry->next;
4482 finalizable_hash [i] = entry->next;
4484 num_ready_finalizers++;
4485 hash_table->num_registered--;
4486 queue_finalization_entry (entry);
4487 /* Make it survive */
4488 from = entry->object;
4489 entry->object = copy;
4490 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));
4494 char *from = entry->object;
4495 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4496 FinalizeEntry *next = entry->next;
4497 unsigned int major_hash;
4498 /* remove from the list */
4500 prev->next = entry->next;
4502 finalizable_hash [i] = entry->next;
4503 hash_table->num_registered--;
4505 entry->object = copy;
4507 /* insert it into the major hash */
4508 rehash_fin_table_if_necessary (&major_finalizable_hash);
4509 major_hash = mono_object_hash ((MonoObject*) copy) %
4510 major_finalizable_hash.size;
4511 entry->next = major_finalizable_hash.table [major_hash];
4512 major_finalizable_hash.table [major_hash] = entry;
4513 major_finalizable_hash.num_registered++;
4515 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4520 /* update pointer */
4521 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4522 entry->object = copy;
4527 entry = entry->next;
4533 object_is_reachable (char *object, char *start, char *end)
4535 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4536 if (object < start || object >= end)
4538 return !object_is_fin_ready (object) || major_is_object_live (object);
4541 /* LOCKING: requires that the GC lock is held */
4543 null_ephemerons_for_domain (MonoDomain *domain)
4545 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4548 MonoObject *object = (MonoObject*)current->array;
4550 if (object && !object->vtable) {
4551 EphemeronLinkNode *tmp = current;
4554 prev->next = current->next;
4556 ephemeron_list = current->next;
4558 current = current->next;
4559 free_internal_mem (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4562 current = current->next;
4567 /* LOCKING: requires that the GC lock is held */
4569 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end)
4571 int was_in_nursery, was_promoted;
4572 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4574 Ephemeron *cur, *array_end;
4578 char *object = current->array;
4580 if (!object_is_reachable (object, start, end)) {
4581 EphemeronLinkNode *tmp = current;
4583 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4586 prev->next = current->next;
4588 ephemeron_list = current->next;
4590 current = current->next;
4591 free_internal_mem (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4596 was_in_nursery = ptr_in_nursery (object);
4597 copy_func ((void**)&object);
4598 current->array = object;
4600 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4601 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4603 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4605 array = (MonoArray*)object;
4606 cur = mono_array_addr (array, Ephemeron, 0);
4607 array_end = cur + mono_array_length_fast (array);
4608 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4610 for (; cur < array_end; ++cur) {
4611 char *key = (char*)cur->key;
4613 if (!key || key == tombstone)
4616 DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4617 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4618 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4620 if (!object_is_reachable (key, start, end)) {
4621 cur->key = tombstone;
4627 if (ptr_in_nursery (key)) {/*key was not promoted*/
4628 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4629 add_to_global_remset (&cur->key);
4631 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4632 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4633 add_to_global_remset (&cur->value);
4638 current = current->next;
4642 /* LOCKING: requires that the GC lock is held */
4644 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end)
4646 int nothing_marked = 1;
4647 EphemeronLinkNode *current = ephemeron_list;
4649 Ephemeron *cur, *array_end;
4652 for (current = ephemeron_list; current; current = current->next) {
4653 char *object = current->array;
4654 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4656 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4657 if (object < start || object >= end)
4660 /*It has to be alive*/
4661 if (!object_is_reachable (object, start, end)) {
4662 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4666 copy_func ((void**)&object);
4668 array = (MonoArray*)object;
4669 cur = mono_array_addr (array, Ephemeron, 0);
4670 array_end = cur + mono_array_length_fast (array);
4671 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4673 for (; cur < array_end; ++cur) {
4674 char *key = cur->key;
4676 if (!key || key == tombstone)
4679 DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4680 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4681 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4683 if (object_is_reachable (key, start, end)) {
4684 char *value = cur->value;
4686 copy_func ((void**)&cur->key);
4688 if (!object_is_reachable (value, start, end))
4690 copy_func ((void**)&cur->value);
4696 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4697 return nothing_marked;
4700 /* LOCKING: requires that the GC lock is held */
4702 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4704 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4705 DisappearingLink **disappearing_link_hash = hash->table;
4706 int disappearing_link_hash_size = hash->size;
4707 DisappearingLink *entry, *prev;
4709 if (!hash->num_links)
4711 for (i = 0; i < disappearing_link_hash_size; ++i) {
4713 for (entry = disappearing_link_hash [i]; entry;) {
4714 char *object = DISLINK_OBJECT (entry);
4715 if (object >= start && object < end && !major_is_object_live (object)) {
4716 gboolean track = DISLINK_TRACK (entry);
4717 if (!track && object_is_fin_ready (object)) {
4718 void **p = entry->link;
4719 DisappearingLink *old;
4721 /* remove from list */
4723 prev->next = entry->next;
4725 disappearing_link_hash [i] = entry->next;
4726 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4728 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4733 char *copy = object;
4734 copy_func ((void**)©);
4736 /* Update pointer if it's moved. If the object
4737 * has been moved out of the nursery, we need to
4738 * remove the link from the minor hash table to
4741 * FIXME: what if an object is moved earlier?
4744 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4745 void **link = entry->link;
4746 DisappearingLink *old;
4747 /* remove from list */
4749 prev->next = entry->next;
4751 disappearing_link_hash [i] = entry->next;
4753 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4757 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4758 track, GENERATION_OLD);
4760 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4764 /* We set the track resurrection bit to
4765 * FALSE if the object is to be finalized
4766 * so that the object can be collected in
4767 * the next cycle (i.e. after it was
4770 *entry->link = HIDE_POINTER (copy,
4771 object_is_fin_ready (object) ? FALSE : track);
4772 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4777 entry = entry->next;
4782 /* LOCKING: requires that the GC lock is held */
4784 null_links_for_domain (MonoDomain *domain, int generation)
4786 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4787 DisappearingLink **disappearing_link_hash = hash->table;
4788 int disappearing_link_hash_size = hash->size;
4789 DisappearingLink *entry, *prev;
4791 for (i = 0; i < disappearing_link_hash_size; ++i) {
4793 for (entry = disappearing_link_hash [i]; entry; ) {
4794 char *object = DISLINK_OBJECT (entry);
4795 if (object && !((MonoObject*)object)->vtable) {
4796 DisappearingLink *next = entry->next;
4801 disappearing_link_hash [i] = next;
4803 if (*(entry->link)) {
4804 *(entry->link) = NULL;
4805 g_warning ("Disappearing link %p not freed", entry->link);
4807 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4814 entry = entry->next;
4819 /* LOCKING: requires that the GC lock is held */
4821 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4822 FinalizeEntryHashTable *hash_table)
4824 FinalizeEntry **finalizable_hash = hash_table->table;
4825 mword finalizable_hash_size = hash_table->size;
4826 FinalizeEntry *entry, *prev;
4829 if (no_finalize || !out_size || !out_array)
4832 for (i = 0; i < finalizable_hash_size; ++i) {
4834 for (entry = finalizable_hash [i]; entry;) {
4835 if (mono_object_domain (entry->object) == domain) {
4836 FinalizeEntry *next;
4837 /* remove and put in out_array */
4839 prev->next = entry->next;
4841 finalizable_hash [i] = entry->next;
4843 hash_table->num_registered--;
4844 out_array [count ++] = entry->object;
4845 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));
4847 if (count == out_size)
4852 entry = entry->next;
4859 * mono_gc_finalizers_for_domain:
4860 * @domain: the unloading appdomain
4861 * @out_array: output array
4862 * @out_size: size of output array
4864 * Store inside @out_array up to @out_size objects that belong to the unloading
4865 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4866 * until it returns 0.
4867 * The items are removed from the finalizer data structure, so the caller is supposed
4869 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4872 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4877 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4878 if (result < out_size) {
4879 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4880 &major_finalizable_hash);
4888 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4890 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4891 FinalizeEntry **finalizable_hash;
4892 mword finalizable_hash_size;
4893 FinalizeEntry *entry, *prev;
4897 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4898 hash = mono_object_hash (obj);
4900 rehash_fin_table_if_necessary (hash_table);
4901 finalizable_hash = hash_table->table;
4902 finalizable_hash_size = hash_table->size;
4903 hash %= finalizable_hash_size;
4905 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4906 if (entry->object == obj) {
4908 /* remove from the list */
4910 prev->next = entry->next;
4912 finalizable_hash [hash] = entry->next;
4913 hash_table->num_registered--;
4914 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));
4915 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4923 /* request to deregister, but already out of the list */
4927 entry = get_internal_mem (sizeof (FinalizeEntry), INTERNAL_MEM_FINALIZE_ENTRY);
4928 entry->object = obj;
4929 entry->next = finalizable_hash [hash];
4930 finalizable_hash [hash] = entry;
4931 hash_table->num_registered++;
4932 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)));
4937 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4939 if (ptr_in_nursery (obj))
4940 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4942 register_for_finalization (obj, user_data, GENERATION_OLD);
4946 rehash_dislink (DisappearingLinkHashTable *hash_table)
4948 DisappearingLink **disappearing_link_hash = hash_table->table;
4949 int disappearing_link_hash_size = hash_table->size;
4952 DisappearingLink **new_hash;
4953 DisappearingLink *entry, *next;
4954 int new_size = g_spaced_primes_closest (hash_table->num_links);
4956 new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4957 for (i = 0; i < disappearing_link_hash_size; ++i) {
4958 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4959 hash = mono_aligned_addr_hash (entry->link) % new_size;
4961 entry->next = new_hash [hash];
4962 new_hash [hash] = entry;
4965 free_internal_mem (disappearing_link_hash, INTERNAL_MEM_DISLINK_TABLE);
4966 hash_table->table = new_hash;
4967 hash_table->size = new_size;
4970 /* LOCKING: assumes the GC lock is held */
4972 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4974 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4975 DisappearingLink *entry, *prev;
4977 DisappearingLink **disappearing_link_hash = hash_table->table;
4978 int disappearing_link_hash_size = hash_table->size;
4980 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4981 rehash_dislink (hash_table);
4982 disappearing_link_hash = hash_table->table;
4983 disappearing_link_hash_size = hash_table->size;
4985 /* FIXME: add check that link is not in the heap */
4986 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4987 entry = disappearing_link_hash [hash];
4989 for (; entry; entry = entry->next) {
4990 /* link already added */
4991 if (link == entry->link) {
4992 /* NULL obj means remove */
4995 prev->next = entry->next;
4997 disappearing_link_hash [hash] = entry->next;
4998 hash_table->num_links--;
4999 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
5000 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5003 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
5011 entry = get_internal_mem (sizeof (DisappearingLink), INTERNAL_MEM_DISLINK);
5012 *link = HIDE_POINTER (obj, track);
5014 entry->next = disappearing_link_hash [hash];
5015 disappearing_link_hash [hash] = entry;
5016 hash_table->num_links++;
5017 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)));
5020 /* LOCKING: assumes the GC lock is held */
5022 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
5024 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
5025 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
5027 if (ptr_in_nursery (obj))
5028 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
5030 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
5035 mono_gc_invoke_finalizers (void)
5037 FinalizeEntry *entry = NULL;
5038 gboolean entry_is_critical = FALSE;
5041 /* FIXME: batch to reduce lock contention */
5042 while (fin_ready_list || critical_fin_list) {
5046 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
5048 /* We have finalized entry in the last
5049 interation, now we need to remove it from
5052 *list = entry->next;
5054 FinalizeEntry *e = *list;
5055 while (e->next != entry)
5057 e->next = entry->next;
5059 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
5063 /* Now look for the first non-null entry. */
5064 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
5067 entry_is_critical = FALSE;
5069 entry_is_critical = TRUE;
5070 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
5075 g_assert (entry->object);
5076 num_ready_finalizers--;
5077 obj = entry->object;
5078 entry->object = NULL;
5079 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
5087 g_assert (entry->object == NULL);
5089 /* the object is on the stack so it is pinned */
5090 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
5091 mono_gc_run_finalize (obj, NULL);
5098 mono_gc_pending_finalizers (void)
5100 return fin_ready_list || critical_fin_list;
5103 /* Negative value to remove */
5105 mono_gc_add_memory_pressure (gint64 value)
5107 /* FIXME: Use interlocked functions */
5109 memory_pressure += value;
5114 * ######################################################################
5115 * ######## registered roots support
5116 * ######################################################################
5120 rehash_roots (gboolean pinned)
5124 RootRecord **new_hash;
5125 RootRecord *entry, *next;
5128 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
5129 new_hash = get_internal_mem (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5130 for (i = 0; i < roots_hash_size [pinned]; ++i) {
5131 for (entry = roots_hash [pinned][i]; entry; entry = next) {
5132 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5134 entry->next = new_hash [hash];
5135 new_hash [hash] = entry;
5138 free_internal_mem (roots_hash [pinned], INTERNAL_MEM_ROOTS_TABLE);
5139 roots_hash [pinned] = new_hash;
5140 roots_hash_size [pinned] = new_size;
5144 find_root (int root_type, char *start, guint32 addr_hash)
5146 RootRecord *new_root;
5148 guint32 hash = addr_hash % roots_hash_size [root_type];
5149 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5150 /* we allow changing the size and the descriptor (for thread statics etc) */
5151 if (new_root->start_root == start) {
5160 * We do not coalesce roots.
5163 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5165 RootRecord *new_root;
5166 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5169 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5170 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5173 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5174 new_root = find_root (i, start, addr_hash);
5175 /* we allow changing the size and the descriptor (for thread statics etc) */
5177 size_t old_size = new_root->end_root - new_root->start_root;
5178 new_root->end_root = new_root->start_root + size;
5179 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5180 ((new_root->root_desc == 0) && (descr == NULL)));
5181 new_root->root_desc = (mword)descr;
5183 roots_size -= old_size;
5188 new_root = get_internal_mem (sizeof (RootRecord), INTERNAL_MEM_ROOT_RECORD);
5190 new_root->start_root = start;
5191 new_root->end_root = new_root->start_root + size;
5192 new_root->root_desc = (mword)descr;
5194 hash = addr_hash % roots_hash_size [root_type];
5195 num_roots_entries [root_type]++;
5196 new_root->next = roots_hash [root_type] [hash];
5197 roots_hash [root_type][hash] = new_root;
5198 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));
5208 mono_gc_register_root (char *start, size_t size, void *descr)
5210 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5214 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5216 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5220 mono_gc_deregister_root (char* addr)
5222 RootRecord *tmp, *prev;
5223 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5227 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5228 hash = addr_hash % roots_hash_size [root_type];
5229 tmp = roots_hash [root_type][hash];
5232 if (tmp->start_root == (char*)addr) {
5234 prev->next = tmp->next;
5236 roots_hash [root_type][hash] = tmp->next;
5237 roots_size -= (tmp->end_root - tmp->start_root);
5238 num_roots_entries [root_type]--;
5239 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5240 free_internal_mem (tmp, INTERNAL_MEM_ROOT_RECORD);
5251 * ######################################################################
5252 * ######## Thread handling (stop/start code)
5253 * ######################################################################
5256 /* FIXME: handle large/small config */
5257 #define THREAD_HASH_SIZE 11
5258 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5260 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5262 #if USE_SIGNAL_BASED_START_STOP_WORLD
5264 static MonoSemType suspend_ack_semaphore;
5265 static MonoSemType *suspend_ack_semaphore_ptr;
5266 static unsigned int global_stop_count = 0;
5268 static int suspend_signal_num = SIGXFSZ;
5270 static int suspend_signal_num = SIGPWR;
5272 static int restart_signal_num = SIGXCPU;
5273 static sigset_t suspend_signal_mask;
5274 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5276 /* LOCKING: assumes the GC lock is held */
5277 static SgenThreadInfo*
5278 thread_info_lookup (ARCH_THREAD_TYPE id)
5280 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5281 SgenThreadInfo *info;
5283 info = thread_table [hash];
5284 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5291 update_current_thread_stack (void *start)
5293 void *ptr = cur_thread_regs;
5294 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5296 info->stack_start = align_pointer (&ptr);
5297 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5298 ARCH_STORE_REGS (ptr);
5299 info->stopped_regs = ptr;
5300 if (gc_callbacks.thread_suspend_func)
5301 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5305 signal_desc (int signum)
5307 if (signum == suspend_signal_num)
5309 if (signum == restart_signal_num)
5315 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5316 * have cross-domain checks in the write barrier.
5318 //#define XDOMAIN_CHECKS_IN_WBARRIER
5320 #ifndef BINARY_PROTOCOL
5321 #ifndef HEAVY_STATISTICS
5322 #define MANAGED_ALLOCATION
5323 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5324 #define MANAGED_WBARRIER
5330 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5333 wait_for_suspend_ack (int count)
5337 for (i = 0; i < count; ++i) {
5338 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5339 if (errno != EINTR) {
5340 g_error ("sem_wait ()");
5346 /* LOCKING: assumes the GC lock is held */
5348 thread_handshake (int signum)
5350 int count, i, result;
5351 SgenThreadInfo *info;
5352 pthread_t me = pthread_self ();
5355 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5356 for (info = thread_table [i]; info; info = info->next) {
5357 DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
5358 if (ARCH_THREAD_EQUALS (info->id, me)) {
5359 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
5362 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
5364 result = pthread_kill (info->id, signum);
5366 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
5369 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
5375 wait_for_suspend_ack (count);
5381 restart_threads_until_none_in_managed_allocator (void)
5383 SgenThreadInfo *info;
5384 int i, result, num_threads_died = 0;
5385 int sleep_duration = -1;
5388 int restart_count = 0, restarted_count = 0;
5389 /* restart all threads that stopped in the
5391 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5392 for (info = thread_table [i]; info; info = info->next) {
5395 if (!info->stack_start || info->in_critical_region ||
5396 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5397 binary_protocol_thread_restart ((gpointer)info->id);
5398 result = pthread_kill (info->id, restart_signal_num);
5405 /* we set the stopped_ip to
5406 NULL for threads which
5407 we're not restarting so
5408 that we can easily identify
5410 info->stopped_ip = NULL;
5411 info->stopped_domain = NULL;
5415 /* if no threads were restarted, we're done */
5416 if (restart_count == 0)
5419 /* wait for the threads to signal their restart */
5420 wait_for_suspend_ack (restart_count);
5422 if (sleep_duration < 0) {
5426 g_usleep (sleep_duration);
5427 sleep_duration += 10;
5430 /* stop them again */
5431 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5432 for (info = thread_table [i]; info; info = info->next) {
5433 if (info->skip || info->stopped_ip == NULL)
5435 result = pthread_kill (info->id, suspend_signal_num);
5443 /* some threads might have died */
5444 num_threads_died += restart_count - restarted_count;
5445 /* wait for the threads to signal their suspension
5447 wait_for_suspend_ack (restart_count);
5450 return num_threads_died;
5453 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5455 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5457 SgenThreadInfo *info;
5460 int old_errno = errno;
5461 gpointer regs [ARCH_NUM_REGS];
5462 gpointer stack_start;
5464 id = pthread_self ();
5465 info = thread_info_lookup (id);
5466 info->stopped_domain = mono_domain_get ();
5467 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5468 stop_count = global_stop_count;
5469 /* duplicate signal */
5470 if (0 && info->stop_count == stop_count) {
5474 #ifdef HAVE_KW_THREAD
5475 /* update the remset info in the thread data structure */
5476 info->remset = remembered_set;
5478 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5479 /* If stack_start is not within the limits, then don't set it
5480 in info and we will be restarted. */
5481 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5482 info->stack_start = stack_start;
5484 ARCH_COPY_SIGCTX_REGS (regs, context);
5485 info->stopped_regs = regs;
5487 g_assert (!info->stack_start);
5490 /* Notify the JIT */
5491 if (gc_callbacks.thread_suspend_func)
5492 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5494 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5495 /* notify the waiting thread */
5496 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5497 info->stop_count = stop_count;
5499 /* wait until we receive the restart signal */
5502 sigsuspend (&suspend_signal_mask);
5503 } while (info->signal != restart_signal_num);
5505 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5506 /* notify the waiting thread */
5507 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5513 restart_handler (int sig)
5515 SgenThreadInfo *info;
5516 int old_errno = errno;
5518 info = thread_info_lookup (pthread_self ());
5519 info->signal = restart_signal_num;
5520 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5526 acquire_gc_locks (void)
5532 release_gc_locks (void)
5534 UNLOCK_INTERRUPTION;
5537 static TV_DECLARE (stop_world_time);
5538 static unsigned long max_pause_usec = 0;
5540 /* LOCKING: assumes the GC lock is held */
5546 acquire_gc_locks ();
5548 update_current_thread_stack (&count);
5550 global_stop_count++;
5551 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 ()));
5552 TV_GETTIME (stop_world_time);
5553 count = thread_handshake (suspend_signal_num);
5554 count -= restart_threads_until_none_in_managed_allocator ();
5555 g_assert (count >= 0);
5556 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5560 /* LOCKING: assumes the GC lock is held */
5562 restart_world (void)
5565 SgenThreadInfo *info;
5566 TV_DECLARE (end_sw);
5569 /* notify the profiler of the leftovers */
5570 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5571 if (moved_objects_idx) {
5572 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5573 moved_objects_idx = 0;
5576 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5577 for (info = thread_table [i]; info; info = info->next) {
5578 info->stack_start = NULL;
5579 info->stopped_regs = NULL;
5583 release_gc_locks ();
5585 count = thread_handshake (restart_signal_num);
5586 TV_GETTIME (end_sw);
5587 usec = TV_ELAPSED (stop_world_time, end_sw);
5588 max_pause_usec = MAX (usec, max_pause_usec);
5589 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5593 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5596 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5598 gc_callbacks = *callbacks;
5601 /* Variables holding start/end nursery so it won't have to be passed at every call */
5602 static void *scan_area_arg_start, *scan_area_arg_end;
5605 mono_gc_conservatively_scan_area (void *start, void *end)
5607 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5611 mono_gc_scan_object (void *obj)
5613 if (current_collection_generation == GENERATION_NURSERY)
5616 major_copy_or_mark_object (&obj);
5621 * Mark from thread stacks and registers.
5624 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5627 SgenThreadInfo *info;
5629 scan_area_arg_start = start_nursery;
5630 scan_area_arg_end = end_nursery;
5632 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5633 for (info = thread_table [i]; info; info = info->next) {
5635 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));
5638 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));
5639 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
5640 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5642 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5645 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5646 start_nursery, end_nursery, PIN_TYPE_STACK);
5652 find_pinning_ref_from_thread (char *obj, size_t size)
5655 SgenThreadInfo *info;
5656 char *endobj = obj + size;
5658 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5659 for (info = thread_table [i]; info; info = info->next) {
5660 char **start = (char**)info->stack_start;
5663 while (start < (char**)info->stack_end) {
5664 if (*start >= obj && *start < endobj) {
5665 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));
5670 /* FIXME: check info->stopped_regs */
5676 ptr_on_stack (void *ptr)
5678 gpointer stack_start = &stack_start;
5679 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5681 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5687 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
5694 HEAVY_STAT (++stat_global_remsets_processed);
5696 /* FIXME: exclude stack locations */
5697 switch ((*p) & REMSET_TYPE_MASK) {
5698 case REMSET_LOCATION:
5700 //__builtin_prefetch (ptr);
5701 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5702 gpointer old = *ptr;
5704 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5706 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5707 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5709 * If the object is pinned, each reference to it from nonpinned objects
5710 * becomes part of the global remset, which can grow very large.
5712 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5713 add_to_global_remset (ptr);
5716 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5720 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5721 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5724 while (count-- > 0) {
5726 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5727 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5728 add_to_global_remset (ptr);
5733 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5734 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5736 scan_object ((char*)ptr);
5738 case REMSET_VTYPE: {
5739 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5740 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5745 ptr = (void**) scan_vtype ((char*)ptr, desc, start_nursery, end_nursery);
5749 g_assert_not_reached ();
5754 #ifdef HEAVY_STATISTICS
5756 collect_store_remsets (RememberedSet *remset, mword *bumper)
5758 mword *p = remset->data;
5763 while (p < remset->store_next) {
5764 switch ((*p) & REMSET_TYPE_MASK) {
5765 case REMSET_LOCATION:
5768 ++stat_saved_remsets_1;
5770 if (*p == last1 || *p == last2) {
5771 ++stat_saved_remsets_2;
5788 g_assert_not_reached ();
5798 RememberedSet *remset;
5800 SgenThreadInfo *info;
5802 mword *addresses, *bumper, *p, *r;
5804 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5805 for (info = thread_table [i]; info; info = info->next) {
5806 for (remset = info->remset; remset; remset = remset->next)
5807 size += remset->store_next - remset->data;
5810 for (remset = freed_thread_remsets; remset; remset = remset->next)
5811 size += remset->store_next - remset->data;
5812 for (remset = global_remset; remset; remset = remset->next)
5813 size += remset->store_next - remset->data;
5815 bumper = addresses = get_internal_mem (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5817 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5818 for (info = thread_table [i]; info; info = info->next) {
5819 for (remset = info->remset; remset; remset = remset->next)
5820 bumper = collect_store_remsets (remset, bumper);
5823 for (remset = global_remset; remset; remset = remset->next)
5824 bumper = collect_store_remsets (remset, bumper);
5825 for (remset = freed_thread_remsets; remset; remset = remset->next)
5826 bumper = collect_store_remsets (remset, bumper);
5828 g_assert (bumper <= addresses + size);
5830 stat_store_remsets += bumper - addresses;
5832 sort_addresses ((void**)addresses, bumper - addresses);
5835 while (r < bumper) {
5841 stat_store_remsets_unique += p - addresses;
5843 free_internal_mem (addresses, INTERNAL_MEM_STATISTICS);
5848 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5850 *info->store_remset_buffer_index_addr = 0;
5851 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5855 scan_from_remsets (void *start_nursery, void *end_nursery)
5858 SgenThreadInfo *info;
5859 RememberedSet *remset;
5860 GenericStoreRememberedSet *store_remset;
5861 mword *p, *next_p, *store_pos;
5863 #ifdef HEAVY_STATISTICS
5867 /* the global one */
5868 for (remset = global_remset; remset; remset = remset->next) {
5869 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));
5870 store_pos = remset->data;
5871 for (p = remset->data; p < remset->store_next; p = next_p) {
5874 /*Ignore previously processed remset.*/
5875 if (!global_remset_location_was_not_added (ptr)) {
5880 next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
5883 * Clear global remsets of locations which no longer point to the
5884 * nursery. Otherwise, they could grow indefinitely between major
5887 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5889 if (ptr_in_nursery (*ptr)) {
5890 *store_pos ++ = p [0];
5891 HEAVY_STAT (++stat_global_remsets_readded);
5895 /* Truncate the remset */
5896 remset->store_next = store_pos;
5899 /* the generic store ones */
5900 store_remset = generic_store_remsets;
5901 while (store_remset) {
5902 GenericStoreRememberedSet *next = store_remset->next;
5904 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5905 gpointer addr = store_remset->data [i];
5907 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE);
5910 free_internal_mem (store_remset, INTERNAL_MEM_STORE_REMSET);
5912 store_remset = next;
5914 generic_store_remsets = NULL;
5916 /* the per-thread ones */
5917 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5918 for (info = thread_table [i]; info; info = info->next) {
5919 RememberedSet *next;
5921 for (remset = info->remset; remset; remset = next) {
5922 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));
5923 for (p = remset->data; p < remset->store_next;) {
5924 p = handle_remset (p, start_nursery, end_nursery, FALSE);
5926 remset->store_next = remset->data;
5927 next = remset->next;
5928 remset->next = NULL;
5929 if (remset != info->remset) {
5930 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5931 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5934 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5935 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE);
5936 clear_thread_store_remset_buffer (info);
5940 /* the freed thread ones */
5941 while (freed_thread_remsets) {
5942 RememberedSet *next;
5943 remset = freed_thread_remsets;
5944 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));
5945 for (p = remset->data; p < remset->store_next;) {
5946 p = handle_remset (p, start_nursery, end_nursery, FALSE);
5948 next = remset->next;
5949 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5950 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5951 freed_thread_remsets = next;
5956 * Clear the info in the remembered sets: we're doing a major collection, so
5957 * the per-thread ones are not needed and the global ones will be reconstructed
5961 clear_remsets (void)
5964 SgenThreadInfo *info;
5965 RememberedSet *remset, *next;
5967 /* the global list */
5968 for (remset = global_remset; remset; remset = next) {
5969 remset->store_next = remset->data;
5970 next = remset->next;
5971 remset->next = NULL;
5972 if (remset != global_remset) {
5973 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5974 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5977 /* the generic store ones */
5978 while (generic_store_remsets) {
5979 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5980 free_internal_mem (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5981 generic_store_remsets = gs_next;
5983 /* the per-thread ones */
5984 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5985 for (info = thread_table [i]; info; info = info->next) {
5986 for (remset = info->remset; remset; remset = next) {
5987 remset->store_next = remset->data;
5988 next = remset->next;
5989 remset->next = NULL;
5990 if (remset != info->remset) {
5991 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5992 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5995 clear_thread_store_remset_buffer (info);
5999 /* the freed thread ones */
6000 while (freed_thread_remsets) {
6001 next = freed_thread_remsets->next;
6002 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
6003 free_internal_mem (freed_thread_remsets, INTERNAL_MEM_REMSET);
6004 freed_thread_remsets = next;
6009 * Clear the thread local TLAB variables for all threads.
6014 SgenThreadInfo *info;
6017 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6018 for (info = thread_table [i]; info; info = info->next) {
6019 /* A new TLAB will be allocated when the thread does its first allocation */
6020 *info->tlab_start_addr = NULL;
6021 *info->tlab_next_addr = NULL;
6022 *info->tlab_temp_end_addr = NULL;
6023 *info->tlab_real_end_addr = NULL;
6028 /* LOCKING: assumes the GC lock is held */
6029 static SgenThreadInfo*
6030 gc_register_current_thread (void *addr)
6033 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
6034 #ifndef HAVE_KW_THREAD
6035 SgenThreadInfo *__thread_info__ = info;
6041 memset (info, 0, sizeof (SgenThreadInfo));
6042 #ifndef HAVE_KW_THREAD
6043 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
6045 g_assert (!pthread_getspecific (thread_info_key));
6046 pthread_setspecific (thread_info_key, info);
6051 info->id = ARCH_GET_THREAD ();
6052 info->stop_count = -1;
6055 info->stack_start = NULL;
6056 info->tlab_start_addr = &TLAB_START;
6057 info->tlab_next_addr = &TLAB_NEXT;
6058 info->tlab_temp_end_addr = &TLAB_TEMP_END;
6059 info->tlab_real_end_addr = &TLAB_REAL_END;
6060 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
6061 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
6062 info->stopped_ip = NULL;
6063 info->stopped_domain = NULL;
6064 info->stopped_regs = NULL;
6066 binary_protocol_thread_register ((gpointer)info->id);
6068 #ifdef HAVE_KW_THREAD
6069 tlab_next_addr = &tlab_next;
6070 store_remset_buffer_index_addr = &store_remset_buffer_index;
6073 /* try to get it with attributes first */
6074 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
6078 pthread_attr_t attr;
6079 pthread_getattr_np (pthread_self (), &attr);
6080 pthread_attr_getstack (&attr, &sstart, &size);
6081 info->stack_start_limit = sstart;
6082 info->stack_end = (char*)sstart + size;
6083 pthread_attr_destroy (&attr);
6085 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
6086 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
6087 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
6090 /* FIXME: we assume the stack grows down */
6091 gsize stack_bottom = (gsize)addr;
6092 stack_bottom += 4095;
6093 stack_bottom &= ~4095;
6094 info->stack_end = (char*)stack_bottom;
6098 #ifdef HAVE_KW_THREAD
6099 stack_end = info->stack_end;
6102 /* hash into the table */
6103 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
6104 info->next = thread_table [hash];
6105 thread_table [hash] = info;
6107 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
6108 pthread_setspecific (remembered_set_key, info->remset);
6109 #ifdef HAVE_KW_THREAD
6110 remembered_set = info->remset;
6113 STORE_REMSET_BUFFER = get_internal_mem (sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE, INTERNAL_MEM_STORE_REMSET);
6114 STORE_REMSET_BUFFER_INDEX = 0;
6116 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
6118 if (gc_callbacks.thread_attach_func)
6119 info->runtime_data = gc_callbacks.thread_attach_func ();
6125 add_generic_store_remset_from_buffer (gpointer *buffer)
6127 GenericStoreRememberedSet *remset = get_internal_mem (sizeof (GenericStoreRememberedSet), INTERNAL_MEM_STORE_REMSET);
6128 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6129 remset->next = generic_store_remsets;
6130 generic_store_remsets = remset;
6134 unregister_current_thread (void)
6137 SgenThreadInfo *prev = NULL;
6139 RememberedSet *rset;
6140 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6142 binary_protocol_thread_unregister ((gpointer)id);
6144 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6145 p = thread_table [hash];
6147 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6148 while (!ARCH_THREAD_EQUALS (p->id, id)) {
6153 thread_table [hash] = p->next;
6155 prev->next = p->next;
6158 if (freed_thread_remsets) {
6159 for (rset = p->remset; rset->next; rset = rset->next)
6161 rset->next = freed_thread_remsets;
6162 freed_thread_remsets = p->remset;
6164 freed_thread_remsets = p->remset;
6167 if (*p->store_remset_buffer_index_addr)
6168 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6169 free_internal_mem (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6174 unregister_thread (void *k)
6176 g_assert (!mono_domain_get ());
6178 unregister_current_thread ();
6183 mono_gc_register_thread (void *baseptr)
6185 SgenThreadInfo *info;
6189 info = thread_info_lookup (ARCH_GET_THREAD ());
6191 info = gc_register_current_thread (baseptr);
6193 return info != NULL;
6196 #if USE_PTHREAD_INTERCEPT
6198 #undef pthread_create
6200 #undef pthread_detach
6203 void *(*start_routine) (void *);
6206 MonoSemType registered;
6207 } SgenThreadStartInfo;
6210 gc_start_thread (void *arg)
6212 SgenThreadStartInfo *start_info = arg;
6213 SgenThreadInfo* info;
6214 void *t_arg = start_info->arg;
6215 void *(*start_func) (void*) = start_info->start_routine;
6220 info = gc_register_current_thread (&result);
6222 post_result = MONO_SEM_POST (&(start_info->registered));
6223 g_assert (!post_result);
6224 result = start_func (t_arg);
6225 g_assert (!mono_domain_get ());
6227 * this is done by the pthread key dtor
6229 unregister_current_thread ();
6237 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6239 SgenThreadStartInfo *start_info;
6242 start_info = malloc (sizeof (SgenThreadStartInfo));
6245 result = MONO_SEM_INIT (&(start_info->registered), 0);
6247 start_info->arg = arg;
6248 start_info->start_routine = start_routine;
6250 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6252 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6253 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6256 MONO_SEM_DESTROY (&(start_info->registered));
6262 mono_gc_pthread_join (pthread_t thread, void **retval)
6264 return pthread_join (thread, retval);
6268 mono_gc_pthread_detach (pthread_t thread)
6270 return pthread_detach (thread);
6273 #endif /* USE_PTHREAD_INTERCEPT */
6276 * ######################################################################
6277 * ######## Write barriers
6278 * ######################################################################
6281 static RememberedSet*
6282 alloc_remset (int size, gpointer id) {
6283 RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6284 res->store_next = res->data;
6285 res->end_set = res->data + size;
6287 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6292 * Note: the write barriers first do the needed GC work and then do the actual store:
6293 * this way the value is visible to the conservative GC scan after the write barrier
6294 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6295 * the conservative scan, otherwise by the remembered set scan.
6298 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6302 HEAVY_STAT (++stat_wbarrier_set_field);
6303 if (ptr_in_nursery (field_ptr)) {
6304 *(void**)field_ptr = value;
6307 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6309 rs = REMEMBERED_SET;
6310 if (rs->store_next < rs->end_set) {
6311 *(rs->store_next++) = (mword)field_ptr;
6312 *(void**)field_ptr = value;
6316 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6317 rs->next = REMEMBERED_SET;
6318 REMEMBERED_SET = rs;
6319 #ifdef HAVE_KW_THREAD
6320 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6322 *(rs->store_next++) = (mword)field_ptr;
6323 *(void**)field_ptr = value;
6328 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6332 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6333 if (ptr_in_nursery (slot_ptr)) {
6334 *(void**)slot_ptr = value;
6337 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6339 rs = REMEMBERED_SET;
6340 if (rs->store_next < rs->end_set) {
6341 *(rs->store_next++) = (mword)slot_ptr;
6342 *(void**)slot_ptr = value;
6346 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6347 rs->next = REMEMBERED_SET;
6348 REMEMBERED_SET = rs;
6349 #ifdef HAVE_KW_THREAD
6350 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6352 *(rs->store_next++) = (mword)slot_ptr;
6353 *(void**)slot_ptr = value;
6358 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6362 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6364 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6365 if (ptr_in_nursery (dest_ptr)) {
6369 rs = REMEMBERED_SET;
6370 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6371 if (rs->store_next + 1 < rs->end_set) {
6372 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6373 *(rs->store_next++) = count;
6377 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6378 rs->next = REMEMBERED_SET;
6379 REMEMBERED_SET = rs;
6380 #ifdef HAVE_KW_THREAD
6381 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6383 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6384 *(rs->store_next++) = count;
6388 static char *found_obj;
6391 find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
6393 if (ptr >= obj && ptr < obj + size) {
6394 g_assert (!found_obj);
6399 /* for use in the debugger */
6400 char* find_object_for_ptr (char *ptr);
6402 find_object_for_ptr (char *ptr)
6406 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6408 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6409 (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
6414 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
6415 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
6416 return bigobj->data;
6420 * Very inefficient, but this is debugging code, supposed to
6421 * be called from gdb, so we don't care.
6424 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
6429 evacuate_remset_buffer (void)
6434 buffer = STORE_REMSET_BUFFER;
6436 add_generic_store_remset_from_buffer (buffer);
6437 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6439 STORE_REMSET_BUFFER_INDEX = 0;
6443 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6449 HEAVY_STAT (++stat_wbarrier_generic_store);
6451 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6452 /* FIXME: ptr_in_heap must be called with the GC lock held */
6453 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6454 char *start = find_object_for_ptr (ptr);
6455 MonoObject *value = *(MonoObject**)ptr;
6459 MonoObject *obj = (MonoObject*)start;
6460 if (obj->vtable->domain != value->vtable->domain)
6461 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6469 if (*(gpointer*)ptr)
6470 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6472 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6473 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6478 buffer = STORE_REMSET_BUFFER;
6479 index = STORE_REMSET_BUFFER_INDEX;
6480 /* This simple optimization eliminates a sizable portion of
6481 entries. Comparing it to the last but one entry as well
6482 doesn't eliminate significantly more entries. */
6483 if (buffer [index] == ptr) {
6488 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6489 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6492 if (index >= STORE_REMSET_BUFFER_SIZE) {
6493 evacuate_remset_buffer ();
6494 index = STORE_REMSET_BUFFER_INDEX;
6495 g_assert (index == 0);
6498 buffer [index] = ptr;
6499 STORE_REMSET_BUFFER_INDEX = index;
6505 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6507 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6508 *(void**)ptr = value;
6509 if (ptr_in_nursery (value))
6510 mono_gc_wbarrier_generic_nostore (ptr);
6514 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6518 HEAVY_STAT (++stat_wbarrier_value_copy);
6519 g_assert (klass->valuetype);
6521 memmove (dest, src, count * mono_class_value_size (klass, NULL));
6522 rs = REMEMBERED_SET;
6523 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !klass->has_references) {
6527 g_assert (klass->gc_descr_inited);
6528 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));
6530 if (rs->store_next + 3 < rs->end_set) {
6531 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6532 *(rs->store_next++) = (mword)klass->gc_descr;
6533 *(rs->store_next++) = (mword)count;
6537 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6538 rs->next = REMEMBERED_SET;
6539 REMEMBERED_SET = rs;
6540 #ifdef HAVE_KW_THREAD
6541 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6543 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6544 *(rs->store_next++) = (mword)klass->gc_descr;
6545 *(rs->store_next++) = (mword)count;
6550 * mono_gc_wbarrier_object_copy:
6552 * Write barrier to call when obj is the result of a clone or copy of an object.
6555 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6561 HEAVY_STAT (++stat_wbarrier_object_copy);
6562 rs = REMEMBERED_SET;
6563 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6564 size = mono_object_class (obj)->instance_size;
6566 /* do not copy the sync state */
6567 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6568 size - sizeof (MonoObject));
6569 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6573 if (rs->store_next < rs->end_set) {
6574 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6578 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6579 rs->next = REMEMBERED_SET;
6580 REMEMBERED_SET = rs;
6581 #ifdef HAVE_KW_THREAD
6582 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6584 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6589 * ######################################################################
6590 * ######## Collector debugging
6591 * ######################################################################
6594 const char*descriptor_types [] = {
6606 describe_ptr (char *ptr)
6612 if (ptr_in_nursery (ptr)) {
6613 printf ("Pointer inside nursery.\n");
6615 if (major_ptr_is_in_non_pinned_space (ptr)) {
6616 printf ("Pointer inside oldspace.\n");
6617 } else if (obj_is_from_pinned_alloc (ptr)) {
6618 printf ("Pointer is inside a pinned chunk.\n");
6620 printf ("Pointer unknown.\n");
6625 if (object_is_pinned (ptr))
6626 printf ("Object is pinned.\n");
6628 if (object_is_forwarded (ptr))
6629 printf ("Object is forwared.\n");
6631 // FIXME: Handle pointers to the inside of objects
6632 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6634 printf ("VTable: %p\n", vtable);
6635 if (vtable == NULL) {
6636 printf ("VTable is invalid (empty).\n");
6639 if (ptr_in_nursery (vtable)) {
6640 printf ("VTable is invalid (points inside nursery).\n");
6643 printf ("Class: %s\n", vtable->klass->name);
6645 desc = ((GCVTable*)vtable)->desc;
6646 printf ("Descriptor: %lx\n", (long)desc);
6649 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6653 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6659 switch ((*p) & REMSET_TYPE_MASK) {
6660 case REMSET_LOCATION:
6661 if (*p == (mword)addr)
6665 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6667 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6671 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6672 count = safe_object_get_size ((MonoObject*)ptr);
6673 count += (ALLOC_ALIGN - 1);
6674 count &= (ALLOC_ALIGN - 1);
6675 count /= sizeof (mword);
6676 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6680 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6684 switch (desc & 0x7) {
6685 case DESC_TYPE_RUN_LENGTH:
6686 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6688 case DESC_TYPE_SMALL_BITMAP:
6689 OBJ_BITMAP_SIZE (skip_size, desc, start);
6693 g_assert_not_reached ();
6696 /* The descriptor includes the size of MonoObject */
6697 skip_size -= sizeof (MonoObject);
6699 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6704 g_assert_not_reached ();
6710 * Return whenever ADDR occurs in the remembered sets
6713 find_in_remsets (char *addr)
6716 SgenThreadInfo *info;
6717 RememberedSet *remset;
6718 GenericStoreRememberedSet *store_remset;
6720 gboolean found = FALSE;
6722 /* the global one */
6723 for (remset = global_remset; remset; remset = remset->next) {
6724 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));
6725 for (p = remset->data; p < remset->store_next;) {
6726 p = find_in_remset_loc (p, addr, &found);
6732 /* the generic store ones */
6733 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6734 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6735 if (store_remset->data [i] == addr)
6740 /* the per-thread ones */
6741 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6742 for (info = thread_table [i]; info; info = info->next) {
6744 for (remset = info->remset; remset; remset = remset->next) {
6745 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));
6746 for (p = remset->data; p < remset->store_next;) {
6747 p = find_in_remset_loc (p, addr, &found);
6752 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6753 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6759 /* the freed thread ones */
6760 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6761 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));
6762 for (p = remset->data; p < remset->store_next;) {
6763 p = find_in_remset_loc (p, addr, &found);
6772 static gboolean missing_remsets;
6775 * We let a missing remset slide if the target object is pinned,
6776 * because the store might have happened but the remset not yet added,
6777 * but in that case the target must be pinned. We might theoretically
6778 * miss some missing remsets this way, but it's very unlikely.
6781 #define HANDLE_PTR(ptr,obj) do { \
6782 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6783 if (!find_in_remsets ((char*)(ptr))) { \
6784 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); \
6785 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6786 if (!object_is_pinned (*(ptr))) \
6787 missing_remsets = TRUE; \
6793 * Check that each object reference which points into the nursery can
6794 * be found in the remembered sets.
6797 check_consistency_callback (char *start, size_t size, void *dummy)
6799 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6800 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6802 #define SCAN_OBJECT_ACTION
6803 #include "sgen-scan-object.h"
6807 * Perform consistency check of the heap.
6809 * Assumes the world is stopped.
6812 check_consistency (void)
6816 // Need to add more checks
6818 missing_remsets = FALSE;
6820 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6822 // Check that oldspace->newspace pointers are registered with the collector
6823 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6825 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6826 check_consistency_callback (bigobj->data, bigobj->size, NULL);
6828 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6830 #ifdef BINARY_PROTOCOL
6831 if (!binary_protocol_file)
6833 g_assert (!missing_remsets);
6838 #define HANDLE_PTR(ptr,obj) do { \
6840 g_assert (LOAD_VTABLE (*(ptr))); \
6844 check_major_refs_callback (char *start, size_t size, void *dummy)
6846 #define SCAN_OBJECT_ACTION
6847 #include "sgen-scan-object.h"
6851 check_major_refs (void)
6855 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6857 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6858 check_major_refs_callback (bigobj->data, bigobj->size, NULL);
6861 /* Check that the reference is valid */
6863 #define HANDLE_PTR(ptr,obj) do { \
6865 g_assert (safe_name (*(ptr)) != NULL); \
6872 * Perform consistency check on an object. Currently we only check that the
6873 * reference fields are valid.
6876 check_object (char *start)
6881 #include "sgen-scan-object.h"
6885 * ######################################################################
6886 * ######## Other mono public interface functions.
6887 * ######################################################################
6891 mono_gc_collect (int generation)
6895 if (generation == 0) {
6896 collect_nursery (0);
6898 major_collection ("user request");
6905 mono_gc_max_generation (void)
6911 mono_gc_collection_count (int generation)
6913 if (generation == 0)
6914 return num_minor_gcs;
6915 return num_major_gcs;
6919 mono_gc_get_used_size (void)
6923 tot = los_memory_usage;
6924 tot += nursery_section->next_data - nursery_section->data;
6925 tot += major_get_used_size ();
6926 /* FIXME: account for pinned objects */
6932 mono_gc_get_heap_size (void)
6938 mono_gc_disable (void)
6946 mono_gc_enable (void)
6954 mono_gc_get_los_limit (void)
6956 return MAX_SMALL_OBJ_SIZE;
6960 mono_object_is_alive (MonoObject* o)
6966 mono_gc_get_generation (MonoObject *obj)
6968 if (ptr_in_nursery (obj))
6974 mono_gc_enable_events (void)
6979 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6982 mono_gc_register_disappearing_link (obj, link_addr, track);
6987 mono_gc_weak_link_remove (void **link_addr)
6990 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
6995 mono_gc_weak_link_get (void **link_addr)
6999 return (MonoObject*) REVEAL_POINTER (*link_addr);
7003 mono_gc_ephemeron_array_add (MonoObject *obj)
7005 EphemeronLinkNode *node;
7009 node = get_internal_mem (sizeof (EphemeronLinkNode), INTERNAL_MEM_EPHEMERON_LINK);
7014 node->array = (char*)obj;
7015 node->next = ephemeron_list;
7016 ephemeron_list = node;
7018 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
7025 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7027 if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7028 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7030 mword complex = alloc_complex_descriptor (bitmap, numbits);
7031 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7036 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7040 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7041 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7042 user_descriptors [user_descriptors_next ++] = marker;
7048 mono_gc_alloc_fixed (size_t size, void *descr)
7050 /* FIXME: do a single allocation */
7051 void *res = calloc (1, size);
7054 if (!mono_gc_register_root (res, size, descr)) {
7062 mono_gc_free_fixed (void* addr)
7064 mono_gc_deregister_root (addr);
7069 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7073 result = func (data);
7074 UNLOCK_INTERRUPTION;
7079 mono_gc_is_gc_thread (void)
7083 result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
7090 /* Tries to extract a number from the passed string, taking in to account m, k
7093 parse_environment_string_extract_number (gchar *str, glong *out)
7096 int len = strlen (str), shift = 0;
7098 gboolean is_suffix = FALSE;
7101 switch (str [len - 1]) {
7112 suffix = str [len - 1];
7117 val = strtol (str, &endptr, 10);
7119 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
7120 || (errno != 0 && val == 0) || (endptr == str))
7124 if (*(endptr + 1)) /* Invalid string. */
7136 mono_gc_base_init (void)
7140 struct sigaction sinfo;
7142 LOCK_INIT (gc_mutex);
7144 if (gc_initialized) {
7148 pagesize = mono_pagesize ();
7149 gc_debug_file = stderr;
7153 if ((env = getenv ("MONO_GC_PARAMS"))) {
7154 if (g_str_has_prefix (env, "nursery-size")) {
7157 while (env [index] && env [index++] != '=')
7159 if (env [index] && parse_environment_string_extract_number (env
7161 default_nursery_size = val;
7162 #ifdef ALIGN_NURSERY
7163 if ((val & (val - 1))) {
7164 fprintf (stderr, "The nursery size must be a power of two.\n");
7168 default_nursery_bits = 0;
7169 while (1 << (++ default_nursery_bits) != default_nursery_size)
7173 fprintf (stderr, "nursery-size must be an integer.\n");
7177 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");
7184 nursery_size = DEFAULT_NURSERY_SIZE;
7185 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7189 if ((env = getenv ("MONO_GC_DEBUG"))) {
7190 opts = g_strsplit (env, ",", -1);
7191 for (ptr = opts; ptr && *ptr; ptr ++) {
7193 if (opt [0] >= '0' && opt [0] <= '9') {
7194 gc_debug_level = atoi (opt);
7199 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7200 gc_debug_file = fopen (rf, "wb");
7202 gc_debug_file = stderr;
7205 } else if (!strcmp (opt, "collect-before-allocs")) {
7206 collect_before_allocs = TRUE;
7207 } else if (!strcmp (opt, "check-at-minor-collections")) {
7208 consistency_check_at_minor_collection = TRUE;
7209 nursery_clear_policy = CLEAR_AT_GC;
7210 } else if (!strcmp (opt, "xdomain-checks")) {
7211 xdomain_checks = TRUE;
7212 } else if (!strcmp (opt, "clear-at-gc")) {
7213 nursery_clear_policy = CLEAR_AT_GC;
7214 } else if (!strcmp (opt, "conservative-stack-mark")) {
7215 conservative_stack_mark = TRUE;
7216 } else if (!strcmp (opt, "check-scan-starts")) {
7217 do_scan_starts_check = TRUE;
7218 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7219 char *filename = strchr (opt, '=') + 1;
7220 nursery_clear_policy = CLEAR_AT_GC;
7221 heap_dump_file = fopen (filename, "w");
7223 fprintf (heap_dump_file, "<sgen-dump>\n");
7224 #ifdef BINARY_PROTOCOL
7225 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7226 char *filename = strchr (opt, '=') + 1;
7227 binary_protocol_file = fopen (filename, "w");
7230 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7231 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7232 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
7239 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7240 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7242 sigfillset (&sinfo.sa_mask);
7243 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7244 sinfo.sa_sigaction = suspend_handler;
7245 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7246 g_error ("failed sigaction");
7249 sinfo.sa_handler = restart_handler;
7250 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7251 g_error ("failed sigaction");
7254 sigfillset (&suspend_signal_mask);
7255 sigdelset (&suspend_signal_mask, restart_signal_num);
7257 global_remset = alloc_remset (1024, NULL);
7258 global_remset->next = NULL;
7260 pthread_key_create (&remembered_set_key, unregister_thread);
7262 #ifndef HAVE_KW_THREAD
7263 pthread_key_create (&thread_info_key, NULL);
7266 gc_initialized = TRUE;
7268 mono_gc_register_thread (&sinfo);
7272 mono_gc_get_suspend_signal (void)
7274 return suspend_signal_num;
7284 #ifdef HAVE_KW_THREAD
7285 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7286 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7287 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7288 mono_mb_emit_i4 ((mb), (offset)); \
7291 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7292 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7293 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7294 mono_mb_emit_i4 ((mb), thread_info_key); \
7295 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7296 mono_mb_emit_byte ((mb), CEE_ADD); \
7297 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7301 #ifdef MANAGED_ALLOCATION
7302 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7303 * for each class. This is currently not easy to do, as it is hard to generate basic
7304 * blocks + branches, but it is easy with the linear IL codebase.
7306 * For this to work we'd need to solve the TLAB race, first. Now we
7307 * require the allocator to be in a few known methods to make sure
7308 * that they are executed atomically via the restart mechanism.
7311 create_allocator (int atype)
7313 int p_var, size_var;
7314 guint32 slowpath_branch, max_size_branch;
7315 MonoMethodBuilder *mb;
7317 MonoMethodSignature *csig;
7318 static gboolean registered = FALSE;
7319 int tlab_next_addr_var, new_next_var;
7321 const char *name = NULL;
7322 AllocatorWrapperInfo *info;
7324 #ifdef HAVE_KW_THREAD
7325 int tlab_next_addr_offset = -1;
7326 int tlab_temp_end_offset = -1;
7328 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7329 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7331 g_assert (tlab_next_addr_offset != -1);
7332 g_assert (tlab_temp_end_offset != -1);
7336 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7337 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7341 if (atype == ATYPE_SMALL) {
7343 name = "AllocSmall";
7344 } else if (atype == ATYPE_NORMAL) {
7347 } else if (atype == ATYPE_VECTOR) {
7349 name = "AllocVector";
7351 g_assert_not_reached ();
7354 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7355 csig->ret = &mono_defaults.object_class->byval_arg;
7356 for (i = 0; i < num_params; ++i)
7357 csig->params [i] = &mono_defaults.int_class->byval_arg;
7359 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7360 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7361 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7362 /* size = vtable->klass->instance_size; */
7363 mono_mb_emit_ldarg (mb, 0);
7364 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7365 mono_mb_emit_byte (mb, CEE_ADD);
7366 mono_mb_emit_byte (mb, CEE_LDIND_I);
7367 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7368 mono_mb_emit_byte (mb, CEE_ADD);
7369 /* FIXME: assert instance_size stays a 4 byte integer */
7370 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7371 mono_mb_emit_stloc (mb, size_var);
7372 } else if (atype == ATYPE_VECTOR) {
7373 MonoExceptionClause *clause;
7375 MonoClass *oom_exc_class;
7378 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7379 mono_mb_emit_ldarg (mb, 1);
7380 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7381 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7382 mono_mb_emit_exception (mb, "OverflowException", NULL);
7383 mono_mb_patch_short_branch (mb, pos);
7385 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7386 clause->try_offset = mono_mb_get_label (mb);
7388 /* vtable->klass->sizes.element_size */
7389 mono_mb_emit_ldarg (mb, 0);
7390 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7391 mono_mb_emit_byte (mb, CEE_ADD);
7392 mono_mb_emit_byte (mb, CEE_LDIND_I);
7393 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7394 mono_mb_emit_byte (mb, CEE_ADD);
7395 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7398 mono_mb_emit_ldarg (mb, 1);
7399 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7400 /* + sizeof (MonoArray) */
7401 mono_mb_emit_icon (mb, sizeof (MonoArray));
7402 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7403 mono_mb_emit_stloc (mb, size_var);
7405 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7408 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7409 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7410 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7411 "System", "OverflowException");
7412 g_assert (clause->data.catch_class);
7413 clause->handler_offset = mono_mb_get_label (mb);
7415 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7416 "System", "OutOfMemoryException");
7417 g_assert (oom_exc_class);
7418 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7421 mono_mb_emit_byte (mb, CEE_POP);
7422 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7423 mono_mb_emit_byte (mb, CEE_THROW);
7425 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7426 mono_mb_set_clauses (mb, 1, clause);
7427 mono_mb_patch_branch (mb, pos_leave);
7430 g_assert_not_reached ();
7433 /* size += ALLOC_ALIGN - 1; */
7434 mono_mb_emit_ldloc (mb, size_var);
7435 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7436 mono_mb_emit_byte (mb, CEE_ADD);
7437 /* size &= ~(ALLOC_ALIGN - 1); */
7438 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7439 mono_mb_emit_byte (mb, CEE_AND);
7440 mono_mb_emit_stloc (mb, size_var);
7442 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7443 if (atype != ATYPE_SMALL) {
7444 mono_mb_emit_ldloc (mb, size_var);
7445 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7446 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7450 * We need to modify tlab_next, but the JIT only supports reading, so we read
7451 * another tls var holding its address instead.
7454 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7455 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7456 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7457 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7459 /* p = (void**)tlab_next; */
7460 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7461 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7462 mono_mb_emit_byte (mb, CEE_LDIND_I);
7463 mono_mb_emit_stloc (mb, p_var);
7465 /* new_next = (char*)p + size; */
7466 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7467 mono_mb_emit_ldloc (mb, p_var);
7468 mono_mb_emit_ldloc (mb, size_var);
7469 mono_mb_emit_byte (mb, CEE_CONV_I);
7470 mono_mb_emit_byte (mb, CEE_ADD);
7471 mono_mb_emit_stloc (mb, new_next_var);
7473 /* tlab_next = new_next */
7474 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7475 mono_mb_emit_ldloc (mb, new_next_var);
7476 mono_mb_emit_byte (mb, CEE_STIND_I);
7478 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7479 mono_mb_emit_ldloc (mb, new_next_var);
7480 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7481 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7484 if (atype != ATYPE_SMALL)
7485 mono_mb_patch_short_branch (mb, max_size_branch);
7487 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7488 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7490 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7491 mono_mb_emit_ldarg (mb, 0);
7492 mono_mb_emit_ldloc (mb, size_var);
7493 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7494 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7495 } else if (atype == ATYPE_VECTOR) {
7496 mono_mb_emit_ldarg (mb, 1);
7497 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7499 g_assert_not_reached ();
7501 mono_mb_emit_byte (mb, CEE_RET);
7504 mono_mb_patch_short_branch (mb, slowpath_branch);
7506 /* FIXME: Memory barrier */
7509 mono_mb_emit_ldloc (mb, p_var);
7510 mono_mb_emit_ldarg (mb, 0);
7511 mono_mb_emit_byte (mb, CEE_STIND_I);
7513 if (atype == ATYPE_VECTOR) {
7514 /* arr->max_length = max_length; */
7515 mono_mb_emit_ldloc (mb, p_var);
7516 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7517 mono_mb_emit_ldarg (mb, 1);
7518 mono_mb_emit_byte (mb, CEE_STIND_I);
7522 mono_mb_emit_ldloc (mb, p_var);
7523 mono_mb_emit_byte (mb, CEE_RET);
7525 res = mono_mb_create_method (mb, csig, 8);
7527 mono_method_get_header (res)->init_locals = FALSE;
7529 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7530 info->alloc_type = atype;
7531 mono_marshal_set_wrapper_info (res, info);
7537 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7538 static MonoMethod *write_barrier_method;
7541 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7549 ji = mono_jit_info_table_find (domain, ip);
7552 method = ji->method;
7554 if (method == write_barrier_method)
7556 for (i = 0; i < ATYPE_NUM; ++i)
7557 if (method == alloc_method_cache [i])
7563 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7564 * The signature of the called method is:
7565 * object allocate (MonoVTable *vtable)
7568 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7570 #ifdef MANAGED_ALLOCATION
7571 MonoClass *klass = vtable->klass;
7573 #ifdef HAVE_KW_THREAD
7574 int tlab_next_offset = -1;
7575 int tlab_temp_end_offset = -1;
7576 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7577 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7579 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7583 if (!mono_runtime_has_tls_get ())
7585 if (klass->instance_size > tlab_size)
7587 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7591 if (klass->byval_arg.type == MONO_TYPE_STRING)
7593 if (collect_before_allocs)
7596 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7597 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7599 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7606 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7608 #ifdef MANAGED_ALLOCATION
7609 MonoClass *klass = vtable->klass;
7611 #ifdef HAVE_KW_THREAD
7612 int tlab_next_offset = -1;
7613 int tlab_temp_end_offset = -1;
7614 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7615 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7617 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7623 if (!mono_runtime_has_tls_get ())
7625 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7627 if (collect_before_allocs)
7629 g_assert (!klass->has_finalize && !klass->marshalbyref);
7631 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7638 mono_gc_get_managed_allocator_by_type (int atype)
7640 #ifdef MANAGED_ALLOCATION
7643 if (!mono_runtime_has_tls_get ())
7646 mono_loader_lock ();
7647 res = alloc_method_cache [atype];
7649 res = alloc_method_cache [atype] = create_allocator (atype);
7650 mono_loader_unlock ();
7658 mono_gc_get_managed_allocator_types (void)
7665 mono_gc_get_write_barrier (void)
7668 MonoMethodBuilder *mb;
7669 MonoMethodSignature *sig;
7670 #ifdef MANAGED_WBARRIER
7671 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7672 #ifndef ALIGN_NURSERY
7673 int label_continue_1, label_continue_2, label_no_wb_5;
7674 int dereferenced_var;
7676 int buffer_var, buffer_index_var, dummy_var;
7678 #ifdef HAVE_KW_THREAD
7679 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7680 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7682 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7683 g_assert (stack_end_offset != -1);
7684 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7685 g_assert (store_remset_buffer_offset != -1);
7686 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7687 g_assert (store_remset_buffer_index_offset != -1);
7688 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7689 g_assert (store_remset_buffer_index_addr_offset != -1);
7693 // FIXME: Maybe create a separate version for ctors (the branch would be
7694 // correctly predicted more times)
7695 if (write_barrier_method)
7696 return write_barrier_method;
7698 /* Create the IL version of mono_gc_barrier_generic_store () */
7699 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7700 sig->ret = &mono_defaults.void_class->byval_arg;
7701 sig->params [0] = &mono_defaults.int_class->byval_arg;
7703 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7705 #ifdef MANAGED_WBARRIER
7706 if (mono_runtime_has_tls_get ()) {
7707 #ifdef ALIGN_NURSERY
7708 // if (ptr_in_nursery (ptr)) return;
7710 * Masking out the bits might be faster, but we would have to use 64 bit
7711 * immediates, which might be slower.
7713 mono_mb_emit_ldarg (mb, 0);
7714 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7715 mono_mb_emit_byte (mb, CEE_SHR_UN);
7716 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7717 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7719 // if (!ptr_in_nursery (*ptr)) return;
7720 mono_mb_emit_ldarg (mb, 0);
7721 mono_mb_emit_byte (mb, CEE_LDIND_I);
7722 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7723 mono_mb_emit_byte (mb, CEE_SHR_UN);
7724 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7725 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7728 // if (ptr < (nursery_start)) goto continue;
7729 mono_mb_emit_ldarg (mb, 0);
7730 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7731 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7733 // if (ptr >= nursery_real_end)) goto continue;
7734 mono_mb_emit_ldarg (mb, 0);
7735 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7736 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7739 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7742 mono_mb_patch_branch (mb, label_continue_1);
7743 mono_mb_patch_branch (mb, label_continue_2);
7745 // Dereference and store in local var
7746 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7747 mono_mb_emit_ldarg (mb, 0);
7748 mono_mb_emit_byte (mb, CEE_LDIND_I);
7749 mono_mb_emit_stloc (mb, dereferenced_var);
7751 // if (*ptr < nursery_start) return;
7752 mono_mb_emit_ldloc (mb, dereferenced_var);
7753 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7754 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7756 // if (*ptr >= nursery_end) return;
7757 mono_mb_emit_ldloc (mb, dereferenced_var);
7758 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7759 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7762 // if (ptr >= stack_end) goto need_wb;
7763 mono_mb_emit_ldarg (mb, 0);
7764 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7765 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7767 // if (ptr >= stack_start) return;
7768 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7769 mono_mb_emit_ldarg (mb, 0);
7770 mono_mb_emit_ldloc_addr (mb, dummy_var);
7771 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7774 mono_mb_patch_branch (mb, label_need_wb);
7776 // buffer = STORE_REMSET_BUFFER;
7777 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7778 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7779 mono_mb_emit_stloc (mb, buffer_var);
7781 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7782 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7783 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7784 mono_mb_emit_stloc (mb, buffer_index_var);
7786 // if (buffer [buffer_index] == ptr) return;
7787 mono_mb_emit_ldloc (mb, buffer_var);
7788 mono_mb_emit_ldloc (mb, buffer_index_var);
7789 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7790 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7791 mono_mb_emit_byte (mb, CEE_SHL);
7792 mono_mb_emit_byte (mb, CEE_ADD);
7793 mono_mb_emit_byte (mb, CEE_LDIND_I);
7794 mono_mb_emit_ldarg (mb, 0);
7795 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7798 mono_mb_emit_ldloc (mb, buffer_index_var);
7799 mono_mb_emit_icon (mb, 1);
7800 mono_mb_emit_byte (mb, CEE_ADD);
7801 mono_mb_emit_stloc (mb, buffer_index_var);
7803 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7804 mono_mb_emit_ldloc (mb, buffer_index_var);
7805 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7806 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7808 // buffer [buffer_index] = ptr;
7809 mono_mb_emit_ldloc (mb, buffer_var);
7810 mono_mb_emit_ldloc (mb, buffer_index_var);
7811 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7812 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7813 mono_mb_emit_byte (mb, CEE_SHL);
7814 mono_mb_emit_byte (mb, CEE_ADD);
7815 mono_mb_emit_ldarg (mb, 0);
7816 mono_mb_emit_byte (mb, CEE_STIND_I);
7818 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7819 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7820 mono_mb_emit_ldloc (mb, buffer_index_var);
7821 mono_mb_emit_byte (mb, CEE_STIND_I);
7824 mono_mb_patch_branch (mb, label_no_wb_1);
7825 mono_mb_patch_branch (mb, label_no_wb_2);
7826 mono_mb_patch_branch (mb, label_no_wb_3);
7827 mono_mb_patch_branch (mb, label_no_wb_4);
7828 #ifndef ALIGN_NURSERY
7829 mono_mb_patch_branch (mb, label_no_wb_5);
7831 mono_mb_emit_byte (mb, CEE_RET);
7834 mono_mb_patch_branch (mb, label_slow_path);
7838 mono_mb_emit_ldarg (mb, 0);
7839 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7840 mono_mb_emit_byte (mb, CEE_RET);
7842 res = mono_mb_create_method (mb, sig, 16);
7845 mono_loader_lock ();
7846 if (write_barrier_method) {
7847 /* Already created */
7848 mono_free_method (res);
7850 /* double-checked locking */
7851 mono_memory_barrier ();
7852 write_barrier_method = res;
7854 mono_loader_unlock ();
7856 return write_barrier_method;
7859 #endif /* HAVE_SGEN_GC */