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_sweep = 0;
321 static long long time_major_fragment_creation = 0;
323 static long long pinned_chunk_bytes_alloced = 0;
324 static long long large_internal_bytes_alloced = 0;
326 /* Keep in sync with internal_mem_names in dump_heap()! */
328 INTERNAL_MEM_PIN_QUEUE,
329 INTERNAL_MEM_FRAGMENT,
330 INTERNAL_MEM_SECTION,
331 INTERNAL_MEM_SCAN_STARTS,
332 INTERNAL_MEM_FIN_TABLE,
333 INTERNAL_MEM_FINALIZE_ENTRY,
334 INTERNAL_MEM_DISLINK_TABLE,
335 INTERNAL_MEM_DISLINK,
336 INTERNAL_MEM_ROOTS_TABLE,
337 INTERNAL_MEM_ROOT_RECORD,
338 INTERNAL_MEM_STATISTICS,
340 INTERNAL_MEM_GRAY_QUEUE,
341 INTERNAL_MEM_STORE_REMSET,
342 INTERNAL_MEM_MS_TABLES,
343 INTERNAL_MEM_MS_BLOCK_INFO,
344 INTERNAL_MEM_EPHEMERON_LINK,
348 static long small_internal_mem_bytes [INTERNAL_MEM_MAX];
352 mono_gc_flush_info (void)
354 fflush (gc_debug_file);
358 #define MAX_DEBUG_LEVEL 2
359 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
361 /* Define this to allow the user to change some of the constants by specifying
362 * their values in the MONO_GC_PARAMS environmental variable. See
363 * mono_gc_base_init for details. */
364 #define USER_CONFIG 1
366 #define TV_DECLARE(name) gint64 name
367 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
368 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
369 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
371 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
373 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
381 typedef struct _Block Block;
387 /* each request from the OS ends up in a GCMemSection */
388 typedef struct _GCMemSection GCMemSection;
389 struct _GCMemSection {
393 /* pointer where more data could be allocated if it fits */
397 * scan starts is an array of pointers to objects equally spaced in the allocation area
398 * They let use quickly find pinned objects from pinning pointers.
401 /* in major collections indexes in the pin_queue for objects that pin this section */
404 unsigned short num_scan_start;
405 gboolean is_to_space;
408 #define SIZEOF_GC_MEM_SECTION ((sizeof (GCMemSection) + 7) & ~7)
410 /* large object space struct: 64+ KB */
411 /* we could make this limit much smaller to avoid memcpy copy
412 * and potentially have more room in the GC descriptor: need to measure
413 * This also means that such small OS objects will need to be
414 * allocated in a different way (using pinned chunks).
415 * We may want to put large but smaller than 64k objects in the fixed space
416 * when we move the object from one generation to another (to limit the
417 * pig in the snake effect).
418 * Note: it may be worth to have an optimized copy function, since we can
419 * assume that objects are aligned and have a multiple of 8 size.
420 * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
421 * true if MONO_ZERO_LEN_ARRAY is nonzero.
423 typedef struct _LOSObject LOSObject;
426 mword size; /* this is the object size */
428 int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN and data starting at same alignment */
429 char data [MONO_ZERO_LEN_ARRAY];
432 /* Pinned objects are allocated in the LOS space if bigger than half a page
433 * or from freelists otherwise. We assume that pinned objects are relatively few
434 * and they have a slow dying speed (like interned strings, thread objects).
435 * As such they will be collected only at major collections.
436 * free lists are not global: when we need memory we allocate a PinnedChunk.
437 * Each pinned chunk is made of several pages, the first of wich is used
438 * internally for bookeeping (here think of a page as 4KB). The bookeeping
439 * includes the freelists vectors and info about the object size of each page
440 * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
441 * a size is assigned to it, the page is divided in the proper chunks and each
442 * chunk is added to the freelist. To not waste space, the remaining space in the
443 * first page is used as objects of size 16 or 32 (need to measure which are more
445 * We use this same structure to allocate memory used internally by the GC, so
446 * we never use malloc/free if we need to alloc during collection: the world is stopped
447 * and malloc/free will deadlock.
448 * When we want to iterate over pinned objects, we just scan a page at a time
449 * linearly according to the size of objects in the page: the next pointer used to link
450 * the items in the freelist uses the same word as the vtable. Since we keep freelists
451 * for each pinned chunk, if the word points outside the pinned chunk it means
453 * We could avoid this expensive scanning in creative ways. We could have a policy
454 * of putting in the pinned space only objects we know about that have no struct fields
455 * with references and we can easily use a even expensive write barrier for them,
456 * since pointer writes on such objects should be rare.
457 * The best compromise is to just alloc interned strings and System.MonoType in them.
458 * It would be nice to allocate MonoThread in it, too: must check that we properly
459 * use write barriers so we don't have to do any expensive scanning of the whole pinned
460 * chunk list during minor collections. We can avoid it now because we alloc in it only
461 * reference-free objects.
463 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
464 #define MAX_FREELIST_SIZE 8192
465 typedef struct _PinnedChunk PinnedChunk;
466 struct _PinnedChunk {
469 int *page_sizes; /* a 0 means the page is still unused */
472 void *data [1]; /* page sizes and free lists are stored here */
475 /* The method used to clear the nursery */
476 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
477 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
482 CLEAR_AT_TLAB_CREATION
483 } NurseryClearPolicy;
485 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
488 * If this is set, the nursery is aligned to an address aligned to its size, ie.
489 * a 1MB nursery will be aligned to an address divisible by 1MB. This allows us to
490 * speed up ptr_in_nursery () checks which are very frequent. This requires the
491 * nursery size to be a compile time constant.
493 #define ALIGN_NURSERY 1
496 * The young generation is divided into fragments. This is because
497 * we can hand one fragments to a thread for lock-less fast alloc and
498 * because the young generation ends up fragmented anyway by pinned objects.
499 * Once a collection is done, a list of fragments is created. When doing
500 * thread local alloc we use smallish nurseries so we allow new threads to
501 * allocate memory from gen0 without triggering a collection. Threads that
502 * are found to allocate lots of memory are given bigger fragments. This
503 * should make the finalizer thread use little nursery memory after a while.
504 * We should start assigning threads very small fragments: if there are many
505 * threads the nursery will be full of reserved space that the threads may not
506 * use at all, slowing down allocation speed.
507 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
508 * Allocation Buffers (TLABs).
510 typedef struct _Fragment Fragment;
514 char *fragment_start;
515 char *fragment_limit; /* the current soft limit for allocation */
519 /* the runtime can register areas of memory as roots: we keep two lists of roots,
520 * a pinned root set for conservatively scanned roots and a normal one for
521 * precisely scanned roots (currently implemented as a single list).
523 typedef struct _RootRecord RootRecord;
531 /* for use with write barriers */
532 typedef struct _RememberedSet RememberedSet;
533 struct _RememberedSet {
537 mword data [MONO_ZERO_LEN_ARRAY];
541 * We're never actually using the first element. It's always set to
542 * NULL to simplify the elimination of consecutive duplicate
545 #define STORE_REMSET_BUFFER_SIZE 1024
547 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
548 struct _GenericStoreRememberedSet {
549 GenericStoreRememberedSet *next;
550 /* We need one entry less because the first entry of store
551 remset buffers is always a dummy and we don't copy it. */
552 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
555 /* we have 4 possible values in the low 2 bits */
557 REMSET_LOCATION, /* just a pointer to the exact location */
558 REMSET_RANGE, /* range of pointer fields */
559 REMSET_OBJECT, /* mark all the object for scanning */
560 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
561 REMSET_TYPE_MASK = 0x3
564 #ifdef HAVE_KW_THREAD
565 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
567 static pthread_key_t remembered_set_key;
568 static RememberedSet *global_remset;
569 static RememberedSet *freed_thread_remsets;
570 static GenericStoreRememberedSet *generic_store_remsets = NULL;
572 /*A two slots cache for recently inserted remsets */
573 static gpointer global_remset_cache [2];
575 /* FIXME: later choose a size that takes into account the RememberedSet struct
576 * and doesn't waste any alloc paddin space.
578 #define DEFAULT_REMSET_SIZE 1024
579 static RememberedSet* alloc_remset (int size, gpointer id);
581 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
582 * no cast from a pointer to an integer
589 /* these bits are set in the object vtable: we could merge them since an object can be
590 * either pinned or forwarded but not both.
591 * We store them in the vtable slot because the bits are used in the sync block for
592 * other purposes: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
593 * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
594 * would be an invalid combination for the monitor and hash code).
595 * The values are already shifted.
596 * The forwarding address is stored in the sync block.
598 #define FORWARDED_BIT 1
600 #define VTABLE_BITS_MASK 0x3
602 /* returns NULL if not forwarded, or the forwarded address */
603 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
604 /* set the forwarded address fw_addr for object obj */
605 #define forward_object(obj,fw_addr) do { \
606 ((mword*)(obj))[0] |= FORWARDED_BIT; \
607 ((mword*)(obj))[1] = (mword)(fw_addr); \
610 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
611 #define pin_object(obj) do { \
612 ((mword*)(obj))[0] |= PINNED_BIT; \
614 #define unpin_object(obj) do { \
615 ((mword*)(obj))[0] &= ~PINNED_BIT; \
619 #define ptr_in_nursery(ptr) (((mword)(ptr) & ~((1 << DEFAULT_NURSERY_BITS) - 1)) == (mword)nursery_start)
621 #define ptr_in_nursery(ptr) ((char*)(ptr) >= nursery_start && (char*)(ptr) < nursery_real_end)
625 * Since we set bits in the vtable, use the macro to load it from the pointer to
626 * an object that is potentially pinned.
628 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
631 safe_name (void* obj)
633 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
634 return vt->klass->name;
638 safe_object_get_size (MonoObject* o)
640 MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
641 if (klass == mono_defaults.string_class) {
642 return sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
643 } else if (klass->rank) {
644 MonoArray *array = (MonoArray*)o;
645 size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length_fast (array);
646 if (G_UNLIKELY (array->bounds)) {
647 size += sizeof (mono_array_size_t) - 1;
648 size &= ~(sizeof (mono_array_size_t) - 1);
649 size += sizeof (MonoArrayBounds) * klass->rank;
653 /* from a created object: the class must be inited already */
654 return klass->instance_size;
659 * ######################################################################
660 * ######## Global data.
661 * ######################################################################
663 static LOCK_DECLARE (gc_mutex);
664 static int gc_disabled = 0;
665 static int num_minor_gcs = 0;
666 static int num_major_gcs = 0;
670 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
671 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
672 static int default_nursery_size = (1 << 22);
674 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
675 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
676 static int default_nursery_bits = 22;
681 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
683 #define DEFAULT_NURSERY_BITS 22
688 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
689 /* to quickly find the head of an object pinned by a conservative address
690 * we keep track of the objects allocated for each SCAN_START_SIZE memory
691 * chunk in the nursery or other memory sections. Larger values have less
692 * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
694 #define SCAN_START_SIZE (4096*2)
695 /* the minimum size of a fragment that we consider useful for allocation */
696 #define FRAGMENT_MIN_SIZE (512)
697 /* This is a fixed value used for pinned chunks, not the system pagesize */
698 #define FREELIST_PAGESIZE (16*1024)
700 static mword pagesize = 4096;
701 static mword nursery_size;
702 static int degraded_mode = 0;
704 static LOSObject *los_object_list = NULL;
705 static mword los_memory_usage = 0;
706 static mword last_los_memory_usage = 0;
707 static mword los_num_objects = 0;
708 static mword total_alloc = 0;
709 /* use this to tune when to do a major/minor collection */
710 static mword memory_pressure = 0;
711 static int minor_collection_allowance;
712 static int minor_collection_sections_alloced = 0;
714 static GCMemSection *nursery_section = NULL;
715 static mword lowest_heap_address = ~(mword)0;
716 static mword highest_heap_address = 0;
718 static LOCK_DECLARE (interruption_mutex);
720 typedef struct _FinalizeEntry FinalizeEntry;
721 struct _FinalizeEntry {
726 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
727 struct _FinalizeEntryHashTable {
728 FinalizeEntry **table;
733 typedef struct _DisappearingLink DisappearingLink;
734 struct _DisappearingLink {
735 DisappearingLink *next;
739 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
740 struct _DisappearingLinkHashTable {
741 DisappearingLink **table;
746 typedef struct _EphemeronLinkNode EphemeronLinkNode;
748 struct _EphemeronLinkNode {
749 EphemeronLinkNode *next;
758 #define LARGE_INTERNAL_MEM_HEADER_MAGIC 0x7d289f3a
760 typedef struct _LargeInternalMemHeader LargeInternalMemHeader;
761 struct _LargeInternalMemHeader {
773 int current_collection_generation = -1;
776 * The link pointer is hidden by negating each bit. We use the lowest
777 * bit of the link (before negation) to store whether it needs
778 * resurrection tracking.
780 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
781 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
783 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
784 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
787 * The finalizable hash has the object as the key, the
788 * disappearing_link hash, has the link address as key.
790 static FinalizeEntryHashTable minor_finalizable_hash;
791 static FinalizeEntryHashTable major_finalizable_hash;
792 /* objects that are ready to be finalized */
793 static FinalizeEntry *fin_ready_list = NULL;
794 static FinalizeEntry *critical_fin_list = NULL;
796 static DisappearingLinkHashTable minor_disappearing_link_hash;
797 static DisappearingLinkHashTable major_disappearing_link_hash;
799 static EphemeronLinkNode *ephemeron_list;
801 static int num_ready_finalizers = 0;
802 static int no_finalize = 0;
804 /* keep each size a multiple of ALLOC_ALIGN */
805 /* on 64 bit systems 8 is likely completely unused. */
806 static const int freelist_sizes [] = {
807 8, 16, 24, 32, 40, 48, 64, 80,
808 96, 128, 160, 192, 224, 256, 320, 384,
809 448, 512, 584, 680, 816, 1024, 1360, 2048,
810 2336, 2728, 3272, 4096, 5456, 8192 };
811 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
813 /* This is also the MAJOR_SECTION_SIZE for the copying major
815 #define PINNED_CHUNK_SIZE (128 * 1024)
817 /* internal_chunk_list is used for allocating structures needed by the GC */
818 static PinnedChunk *internal_chunk_list = NULL;
820 static int slot_for_size (size_t size);
823 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
824 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
825 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
829 /* registered roots: the key to the hash is the root start address */
831 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
833 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
834 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
835 static mword roots_size = 0; /* amount of memory in the root set */
836 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
839 * The current allocation cursors
840 * We allocate objects in the nursery.
841 * The nursery is the area between nursery_start and nursery_real_end.
842 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
843 * from nursery fragments.
844 * tlab_next is the pointer to the space inside the TLAB where the next object will
846 * tlab_temp_end is the pointer to the end of the temporary space reserved for
847 * the allocation: it allows us to set the scan starts at reasonable intervals.
848 * tlab_real_end points to the end of the TLAB.
849 * nursery_frag_real_end points to the end of the currently used nursery fragment.
850 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
851 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
852 * At the next allocation, the area of the nursery where objects can be present is
853 * between MIN(nursery_first_pinned_start, first_fragment_start) and
854 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
856 static char *nursery_start = NULL;
858 /* eventually share with MonoThread? */
859 typedef struct _SgenThreadInfo SgenThreadInfo;
861 struct _SgenThreadInfo {
862 SgenThreadInfo *next;
864 unsigned int stop_count; /* to catch duplicate signals */
867 volatile int in_critical_region;
870 void *stack_start_limit;
871 char **tlab_next_addr;
872 char **tlab_start_addr;
873 char **tlab_temp_end_addr;
874 char **tlab_real_end_addr;
875 gpointer **store_remset_buffer_addr;
876 long *store_remset_buffer_index_addr;
877 RememberedSet *remset;
878 gpointer runtime_data;
879 gpointer stopped_ip; /* only valid if the thread is stopped */
880 MonoDomain *stopped_domain; /* ditto */
881 gpointer *stopped_regs; /* ditto */
882 #ifndef HAVE_KW_THREAD
887 gpointer *store_remset_buffer;
888 long store_remset_buffer_index;
892 #ifdef HAVE_KW_THREAD
893 #define TLAB_ACCESS_INIT
894 #define TLAB_START tlab_start
895 #define TLAB_NEXT tlab_next
896 #define TLAB_TEMP_END tlab_temp_end
897 #define TLAB_REAL_END tlab_real_end
898 #define REMEMBERED_SET remembered_set
899 #define STORE_REMSET_BUFFER store_remset_buffer
900 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
901 #define IN_CRITICAL_REGION thread_info->in_critical_region
903 static pthread_key_t thread_info_key;
904 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
905 #define TLAB_START (__thread_info__->tlab_start)
906 #define TLAB_NEXT (__thread_info__->tlab_next)
907 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
908 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
909 #define REMEMBERED_SET (__thread_info__->remset)
910 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
911 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
912 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
915 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
916 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
917 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
920 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
921 * variables for next+temp_end ?
923 #ifdef HAVE_KW_THREAD
924 static __thread SgenThreadInfo *thread_info;
925 static __thread char *tlab_start;
926 static __thread char *tlab_next;
927 static __thread char *tlab_temp_end;
928 static __thread char *tlab_real_end;
929 static __thread gpointer *store_remset_buffer;
930 static __thread long store_remset_buffer_index;
931 /* Used by the managed allocator/wbarrier */
932 static __thread char **tlab_next_addr;
933 static __thread char *stack_end;
934 static __thread long *store_remset_buffer_index_addr;
936 static char *nursery_next = NULL;
937 static char *nursery_frag_real_end = NULL;
938 static char *nursery_real_end = NULL;
939 static char *nursery_last_pinned_end = NULL;
941 /* The size of a TLAB */
942 /* The bigger the value, the less often we have to go to the slow path to allocate a new
943 * one, but the more space is wasted by threads not allocating much memory.
945 * FIXME: Make this self-tuning for each thread.
947 static guint32 tlab_size = (1024 * 4);
949 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
950 #define MAX_NURSERY_TLAB_WASTE 512
952 /* fragments that are free and ready to be used for allocation */
953 static Fragment *nursery_fragments = NULL;
954 /* freeelist of fragment structures */
955 static Fragment *fragment_freelist = NULL;
958 * Objects bigger then this go into the large object space. This size
959 * has a few constraints. It must fit into the major heap, which in
960 * the case of the copying collector means that it must fit into a
961 * pinned chunk. It must also play well with the GC descriptors, some
962 * of which (DESC_TYPE_RUN_LENGTH, DESC_TYPE_SMALL_BITMAP) encode the
965 #define MAX_SMALL_OBJ_SIZE 8000
967 /* Functions supplied by the runtime to be called by the GC */
968 static MonoGCCallbacks gc_callbacks;
970 #define ALLOC_ALIGN 8
971 #define ALLOC_ALIGN_BITS 3
973 #define MOVED_OBJECTS_NUM 64
974 static void *moved_objects [MOVED_OBJECTS_NUM];
975 static int moved_objects_idx = 0;
978 * ######################################################################
979 * ######## Macros and function declarations.
980 * ######################################################################
983 #define UPDATE_HEAP_BOUNDARIES(low,high) do { \
984 if ((mword)(low) < lowest_heap_address) \
985 lowest_heap_address = (mword)(low); \
986 if ((mword)(high) > highest_heap_address) \
987 highest_heap_address = (mword)(high); \
989 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
992 align_pointer (void *ptr)
994 mword p = (mword)ptr;
995 p += sizeof (gpointer) - 1;
996 p &= ~ (sizeof (gpointer) - 1);
1000 typedef void (*CopyOrMarkObjectFunc) (void**);
1001 typedef char* (*ScanObjectFunc) (char*);
1003 /* forward declarations */
1004 static void* get_internal_mem (size_t size, int type);
1005 static void free_internal_mem (void *addr, int type);
1006 static void* get_os_memory (size_t size, int activate);
1007 static void* get_os_memory_aligned (mword size, mword alignment, gboolean activate);
1008 static void free_os_memory (void *addr, size_t size);
1009 static G_GNUC_UNUSED void report_internal_mem_usage (void);
1011 static int stop_world (void);
1012 static int restart_world (void);
1013 static void add_to_global_remset (gpointer ptr);
1014 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
1015 static void scan_from_remsets (void *start_nursery, void *end_nursery);
1016 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type);
1017 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list);
1018 static void find_pinning_ref_from_thread (char *obj, size_t size);
1019 static void update_current_thread_stack (void *start);
1020 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
1021 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
1022 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
1023 static void null_links_for_domain (MonoDomain *domain, int generation);
1024 static gboolean search_fragment_for_size (size_t size);
1025 static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
1026 static void build_nursery_fragments (int start_pin, int end_pin);
1027 static void clear_nursery_fragments (char *next);
1028 static void pin_from_roots (void *start_nursery, void *end_nursery);
1029 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery);
1030 static void pin_objects_in_section (GCMemSection *section);
1031 static void optimize_pin_queue (int start_slot);
1032 static void clear_remsets (void);
1033 static void clear_tlabs (void);
1034 typedef void (*IterateObjectCallbackFunc) (char*, size_t, void*);
1035 static void scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data);
1036 static void scan_object (char *start);
1037 static void major_scan_object (char *start);
1038 static void* copy_object_no_checks (void *obj);
1039 static void copy_object (void **obj_slot);
1040 static void* get_chunk_freelist (PinnedChunk *chunk, int slot);
1041 static PinnedChunk* alloc_pinned_chunk (void);
1042 static void free_large_object (LOSObject *obj);
1043 static void sort_addresses (void **array, int size);
1044 static void drain_gray_stack (void);
1045 static void finish_gray_stack (char *start_addr, char *end_addr, int generation);
1046 static gboolean need_major_collection (void);
1048 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
1050 void describe_ptr (char *ptr);
1051 static void check_consistency (void);
1052 static void check_major_refs (void);
1053 static void check_section_scan_starts (GCMemSection *section);
1054 static void check_scan_starts (void);
1055 static void check_for_xdomain_refs (void);
1056 static void dump_occupied (char *start, char *end, char *section_start);
1057 static void dump_section (GCMemSection *section, const char *type);
1058 static void dump_heap (const char *type, int num, const char *reason);
1059 static void commit_stats (int generation);
1060 static void report_pinned_chunk (PinnedChunk *chunk, int seq);
1062 void mono_gc_scan_for_specific_ref (MonoObject *key);
1064 static void init_stats (void);
1066 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end);
1067 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end);
1068 static void null_ephemerons_for_domain (MonoDomain *domain);
1070 //#define BINARY_PROTOCOL
1071 #include "sgen-protocol.c"
1072 #include "sgen-pinning.c"
1073 #include "sgen-pinning-stats.c"
1074 #include "sgen-gray.c"
1077 * ######################################################################
1078 * ######## GC descriptors
1079 * ######################################################################
1080 * Used to quickly get the info the GC needs about an object: size and
1081 * where the references are held.
1083 /* objects are aligned to 8 bytes boundaries
1084 * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
1085 * The low 3 bits define the type of the descriptor. The other bits
1086 * depend on the type.
1087 * As a general rule the 13 remaining low bits define the size, either
1088 * of the whole object or of the elements in the arrays. While for objects
1089 * the size is already in bytes, for arrays we need to shift, because
1090 * array elements might be smaller than 8 bytes. In case of arrays, we
1091 * use two bits to describe what the additional high bits represents,
1092 * so the default behaviour can handle element sizes less than 2048 bytes.
1093 * The high 16 bits, if 0 it means the object is pointer-free.
1094 * This design should make it easy and fast to skip over ptr-free data.
1095 * The first 4 types should cover >95% of the objects.
1096 * Note that since the size of objects is limited to 64K, larger objects
1097 * will be allocated in the large object heap.
1098 * If we want 4-bytes alignment, we need to put vector and small bitmap
1103 * We don't use 0 so that 0 isn't a valid GC descriptor. No
1104 * deep reason for this other than to be able to identify a
1105 * non-inited descriptor for debugging.
1107 * If an object contains no references, its GC descriptor is
1108 * always DESC_TYPE_RUN_LENGTH, without a size, no exceptions.
1109 * This is so that we can quickly check for that in
1110 * copy_object_no_checks(), without having to fetch the
1113 DESC_TYPE_RUN_LENGTH = 1, /* 15 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
1114 DESC_TYPE_SMALL_BITMAP, /* 15 bits aligned byte size | 16-48 bit bitmap */
1115 DESC_TYPE_COMPLEX, /* index for bitmap into complex_descriptors */
1116 DESC_TYPE_VECTOR, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1117 DESC_TYPE_ARRAY, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1118 DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
1119 DESC_TYPE_COMPLEX_ARR, /* index for bitmap into complex_descriptors */
1120 /* subtypes for arrays and vectors */
1121 DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value */
1122 DESC_TYPE_V_REFS, /* all the array elements are refs */
1123 DESC_TYPE_V_RUN_LEN, /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
1124 DESC_TYPE_V_BITMAP /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
1127 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
1128 #define LOW_TYPE_BITS 3
1129 #define SMALL_BITMAP_SHIFT 16
1130 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
1131 #define VECTOR_INFO_SHIFT 14
1132 #define VECTOR_ELSIZE_SHIFT 3
1133 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
1134 #define MAX_ELEMENT_SIZE 0x3ff
1135 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
1136 #define VECTOR_SUBTYPE_REFS (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
1137 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
1138 #define VECTOR_SUBTYPE_BITMAP (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
1141 /* Root bitmap descriptors are simpler: the lower three bits describe the type
1142 * and we either have 30/62 bitmap bits or nibble-based run-length,
1143 * or a complex descriptor, or a user defined marker function.
1146 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
1151 ROOT_DESC_TYPE_MASK = 0x7,
1152 ROOT_DESC_TYPE_SHIFT = 3,
1155 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
1157 #define MAX_USER_DESCRIPTORS 16
1159 static gsize* complex_descriptors = NULL;
1160 static int complex_descriptors_size = 0;
1161 static int complex_descriptors_next = 0;
1162 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
1163 static int user_descriptors_next = 0;
1166 alloc_complex_descriptor (gsize *bitmap, int numbits)
1170 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
1171 nwords = numbits / GC_BITS_PER_WORD + 1;
1174 res = complex_descriptors_next;
1175 /* linear search, so we don't have duplicates with domain load/unload
1176 * this should not be performance critical or we'd have bigger issues
1177 * (the number and size of complex descriptors should be small).
1179 for (i = 0; i < complex_descriptors_next; ) {
1180 if (complex_descriptors [i] == nwords) {
1181 int j, found = TRUE;
1182 for (j = 0; j < nwords - 1; ++j) {
1183 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
1193 i += complex_descriptors [i];
1195 if (complex_descriptors_next + nwords > complex_descriptors_size) {
1196 int new_size = complex_descriptors_size * 2 + nwords;
1197 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
1198 complex_descriptors_size = new_size;
1200 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
1201 complex_descriptors_next += nwords;
1202 complex_descriptors [res] = nwords;
1203 for (i = 0; i < nwords - 1; ++i) {
1204 complex_descriptors [res + 1 + i] = bitmap [i];
1205 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
1212 * Descriptor builders.
1215 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
1217 return (void*) DESC_TYPE_RUN_LENGTH;
1221 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
1223 int first_set = -1, num_set = 0, last_set = -1, i;
1225 size_t stored_size = obj_size;
1226 for (i = 0; i < numbits; ++i) {
1227 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1235 * We don't encode the size of types that don't contain
1236 * references because they might not be aligned, i.e. the
1237 * bottom two bits might be set, which would clash with the
1238 * bits we need to encode the descriptor type. Since we don't
1239 * use the encoded size to skip objects, other than for
1240 * processing remsets, in which case only the positions of
1241 * references are relevant, this is not a problem.
1244 return DESC_TYPE_RUN_LENGTH;
1245 g_assert (!(stored_size & 0x3));
1246 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
1247 /* check run-length encoding first: one byte offset, one byte number of pointers
1248 * on 64 bit archs, we can have 3 runs, just one on 32.
1249 * It may be better to use nibbles.
1251 if (first_set < 0) {
1252 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
1253 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1254 return (void*) desc;
1255 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1256 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
1257 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));
1258 return (void*) desc;
1260 /* we know the 2-word header is ptr-free */
1261 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1262 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1263 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1264 return (void*) desc;
1267 /* we know the 2-word header is ptr-free */
1268 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1269 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1270 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1271 return (void*) desc;
1273 /* it's a complex object ... */
1274 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1275 return (void*) desc;
1278 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1280 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1282 int first_set = -1, num_set = 0, last_set = -1, i;
1283 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1284 for (i = 0; i < numbits; ++i) {
1285 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1292 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
1294 return DESC_TYPE_RUN_LENGTH;
1295 if (elem_size <= MAX_ELEMENT_SIZE) {
1296 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1298 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1300 /* Note: we also handle structs with just ref fields */
1301 if (num_set * sizeof (gpointer) == elem_size) {
1302 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1304 /* FIXME: try run-len first */
1305 /* Note: we can't skip the object header here, because it's not present */
1306 if (last_set <= SMALL_BITMAP_SIZE) {
1307 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1310 /* it's am array of complex structs ... */
1311 desc = DESC_TYPE_COMPLEX_ARR;
1312 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1313 return (void*) desc;
1316 /* Return the bitmap encoded by a descriptor */
1318 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1320 mword d = (mword)descr;
1324 case DESC_TYPE_RUN_LENGTH: {
1325 int first_set = (d >> 16) & 0xff;
1326 int num_set = (d >> 24) & 0xff;
1329 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1331 for (i = first_set; i < first_set + num_set; ++i)
1332 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1334 *numbits = first_set + num_set;
1338 case DESC_TYPE_SMALL_BITMAP:
1339 bitmap = g_new0 (gsize, 1);
1341 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1343 *numbits = GC_BITS_PER_WORD;
1347 g_assert_not_reached ();
1351 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
1352 #define STRING_SIZE(size,str) do { \
1353 (size) = sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*)(str)) + 2; \
1354 (size) += (ALLOC_ALIGN - 1); \
1355 (size) &= ~(ALLOC_ALIGN - 1); \
1358 #define OBJ_RUN_LEN_SIZE(size,desc,obj) do { \
1359 (size) = ((desc) & 0xfff8) >> 1; \
1362 #define OBJ_BITMAP_SIZE(size,desc,obj) do { \
1363 (size) = ((desc) & 0xfff8) >> 1; \
1366 //#define PREFETCH(addr) __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(addr)))
1367 #define PREFETCH(addr)
1369 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
1370 #define OBJ_RUN_LEN_FOREACH_PTR(desc,obj) do { \
1371 if ((desc) & 0xffff0000) { \
1372 /* there are pointers */ \
1373 void **_objptr_end; \
1374 void **_objptr = (void**)(obj); \
1375 _objptr += ((desc) >> 16) & 0xff; \
1376 _objptr_end = _objptr + (((desc) >> 24) & 0xff); \
1377 while (_objptr < _objptr_end) { \
1378 HANDLE_PTR (_objptr, (obj)); \
1384 /* a bitmap desc means that there are pointer references or we'd have
1385 * choosen run-length, instead: add an assert to check.
1387 #define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
1388 /* there are pointers */ \
1389 void **_objptr = (void**)(obj); \
1390 gsize _bmap = (desc) >> 16; \
1391 _objptr += OBJECT_HEADER_WORDS; \
1393 if ((_bmap & 1)) { \
1394 HANDLE_PTR (_objptr, (obj)); \
1401 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj) do { \
1402 /* there are pointers */ \
1403 void **_objptr = (void**)(obj); \
1404 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS; \
1405 _objptr += OBJECT_HEADER_WORDS; \
1407 if ((_bmap & 1)) { \
1408 HANDLE_PTR (_objptr, (obj)); \
1415 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do { \
1416 /* there are pointers */ \
1417 void **_objptr = (void**)(obj); \
1418 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1419 int bwords = (*bitmap_data) - 1; \
1420 void **start_run = _objptr; \
1423 MonoObject *myobj = (MonoObject*)obj; \
1424 g_print ("found %d at %p (0x%zx): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1426 while (bwords-- > 0) { \
1427 gsize _bmap = *bitmap_data++; \
1428 _objptr = start_run; \
1429 /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/ \
1431 if ((_bmap & 1)) { \
1432 HANDLE_PTR (_objptr, (obj)); \
1437 start_run += GC_BITS_PER_WORD; \
1441 /* this one is untested */
1442 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj) do { \
1443 /* there are pointers */ \
1444 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1445 int mbwords = (*mbitmap_data++) - 1; \
1446 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass); \
1447 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1448 char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj)); \
1450 MonoObject *myobj = (MonoObject*)start; \
1451 g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1453 while (e_start < e_end) { \
1454 void **_objptr = (void**)e_start; \
1455 gsize *bitmap_data = mbitmap_data; \
1456 unsigned int bwords = mbwords; \
1457 while (bwords-- > 0) { \
1458 gsize _bmap = *bitmap_data++; \
1459 void **start_run = _objptr; \
1460 /*g_print ("bitmap: 0x%x\n", _bmap);*/ \
1462 if ((_bmap & 1)) { \
1463 HANDLE_PTR (_objptr, (obj)); \
1468 _objptr = start_run + GC_BITS_PER_WORD; \
1470 e_start += el_size; \
1474 #define OBJ_VECTOR_FOREACH_PTR(vt,obj) do { \
1475 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */ \
1476 if ((vt)->desc & 0xffffc000) { \
1477 int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE; \
1478 /* there are pointers */ \
1479 int etype = (vt)->desc & 0xc000; \
1480 if (etype == (DESC_TYPE_V_REFS << 14)) { \
1481 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector)); \
1482 void **end_refs = (void**)((char*)p + el_size * mono_array_length_fast ((MonoArray*)(obj))); \
1483 /* Note: this code can handle also arrays of struct with only references in them */ \
1484 while (p < end_refs) { \
1485 HANDLE_PTR (p, (obj)); \
1488 } else if (etype == DESC_TYPE_V_RUN_LEN << 14) { \
1489 int offset = ((vt)->desc >> 16) & 0xff; \
1490 int num_refs = ((vt)->desc >> 24) & 0xff; \
1491 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1492 char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj)); \
1493 while (e_start < e_end) { \
1494 void **p = (void**)e_start; \
1497 for (i = 0; i < num_refs; ++i) { \
1498 HANDLE_PTR (p + i, (obj)); \
1500 e_start += el_size; \
1502 } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
1503 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1504 char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj)); \
1505 while (e_start < e_end) { \
1506 void **p = (void**)e_start; \
1507 gsize _bmap = (vt)->desc >> 16; \
1508 /* Note: there is no object header here to skip */ \
1510 if ((_bmap & 1)) { \
1511 HANDLE_PTR (p, (obj)); \
1516 e_start += el_size; \
1522 //#include "sgen-major-copying.c"
1523 #include "sgen-marksweep.c"
1526 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1528 MonoObject *o = (MonoObject*)(obj);
1529 MonoObject *ref = (MonoObject*)*(ptr);
1530 int offset = (char*)(ptr) - (char*)o;
1532 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1534 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1536 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1537 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1539 /* Thread.cached_culture_info */
1540 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1541 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1542 !strcmp(o->vtable->klass->name_space, "System") &&
1543 !strcmp(o->vtable->klass->name, "Object[]"))
1546 * 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
1547 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1548 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1549 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1550 * 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
1551 * 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
1552 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1553 * 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
1554 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1556 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1557 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1558 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1559 !strcmp (o->vtable->klass->name, "MemoryStream"))
1561 /* append_job() in threadpool.c */
1562 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1563 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1564 !strcmp (o->vtable->klass->name_space, "System") &&
1565 !strcmp (o->vtable->klass->name, "Object[]") &&
1566 mono_thread_pool_is_queue_array ((MonoArray*) o))
1572 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1574 MonoObject *o = (MonoObject*)(obj);
1575 MonoObject *ref = (MonoObject*)*(ptr);
1576 int offset = (char*)(ptr) - (char*)o;
1578 MonoClassField *field;
1581 if (!ref || ref->vtable->domain == domain)
1583 if (is_xdomain_ref_allowed (ptr, obj, domain))
1587 for (class = o->vtable->klass; class; class = class->parent) {
1590 for (i = 0; i < class->field.count; ++i) {
1591 if (class->fields[i].offset == offset) {
1592 field = &class->fields[i];
1600 if (ref->vtable->klass == mono_defaults.string_class)
1601 str = mono_string_to_utf8 ((MonoString*)ref);
1604 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1605 o, o->vtable->klass->name_space, o->vtable->klass->name,
1606 offset, field ? field->name : "",
1607 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1608 mono_gc_scan_for_specific_ref (o);
1614 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1617 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1619 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1621 #include "sgen-scan-object.h"
1625 #define HANDLE_PTR(ptr,obj) do { \
1626 if ((MonoObject*)*(ptr) == key) { \
1627 g_print ("found ref to %p in object %p (%s) at offset %zd\n", \
1628 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1633 scan_object_for_specific_ref (char *start, MonoObject *key)
1635 #include "sgen-scan-object.h"
1639 scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data)
1641 while (start < end) {
1643 if (!*(void**)start) {
1644 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1648 size = safe_object_get_size ((MonoObject*) start);
1649 size += ALLOC_ALIGN - 1;
1650 size &= ~(ALLOC_ALIGN - 1);
1652 callback (start, size, data);
1659 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1661 scan_object_for_specific_ref (obj, key);
1665 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1669 g_print ("found ref to %p in root record %p\n", key, root);
1672 static MonoObject *check_key = NULL;
1673 static RootRecord *check_root = NULL;
1676 check_root_obj_specific_ref_from_marker (void **obj)
1678 check_root_obj_specific_ref (check_root, check_key, *obj);
1682 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1687 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1688 for (root = roots_hash [root_type][i]; root; root = root->next) {
1689 void **start_root = (void**)root->start_root;
1690 mword desc = root->root_desc;
1694 switch (desc & ROOT_DESC_TYPE_MASK) {
1695 case ROOT_DESC_BITMAP:
1696 desc >>= ROOT_DESC_TYPE_SHIFT;
1699 check_root_obj_specific_ref (root, key, *start_root);
1704 case ROOT_DESC_COMPLEX: {
1705 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1706 int bwords = (*bitmap_data) - 1;
1707 void **start_run = start_root;
1709 while (bwords-- > 0) {
1710 gsize bmap = *bitmap_data++;
1711 void **objptr = start_run;
1714 check_root_obj_specific_ref (root, key, *objptr);
1718 start_run += GC_BITS_PER_WORD;
1722 case ROOT_DESC_USER: {
1723 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1724 marker (start_root, check_root_obj_specific_ref_from_marker);
1727 case ROOT_DESC_RUN_LEN:
1728 g_assert_not_reached ();
1730 g_assert_not_reached ();
1739 mono_gc_scan_for_specific_ref (MonoObject *key)
1745 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1746 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1748 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1750 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1751 scan_object_for_specific_ref (bigobj->data, key);
1753 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1754 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1756 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1757 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1758 void **ptr = (void**)root->start_root;
1760 while (ptr < (void**)root->end_root) {
1761 check_root_obj_specific_ref (root, *ptr, key);
1768 /* Clear all remaining nursery fragments */
1770 clear_nursery_fragments (char *next)
1773 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1774 g_assert (next <= nursery_frag_real_end);
1775 memset (next, 0, nursery_frag_real_end - next);
1776 for (frag = nursery_fragments; frag; frag = frag->next) {
1777 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1783 need_remove_object_for_domain (char *start, MonoDomain *domain)
1785 if (mono_object_domain (start) == domain) {
1786 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1787 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1794 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1796 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1797 if (vt->klass == mono_defaults.internal_thread_class)
1798 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1799 /* The object could be a proxy for an object in the domain
1801 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1802 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1804 /* The server could already have been zeroed out, so
1805 we need to check for that, too. */
1806 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1807 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1809 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1814 static MonoDomain *check_domain = NULL;
1817 check_obj_not_in_domain (void **o)
1819 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1823 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1827 check_domain = domain;
1828 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1829 for (root = roots_hash [root_type][i]; root; root = root->next) {
1830 void **start_root = (void**)root->start_root;
1831 mword desc = root->root_desc;
1833 /* The MonoDomain struct is allowed to hold
1834 references to objects in its own domain. */
1835 if (start_root == (void**)domain)
1838 switch (desc & ROOT_DESC_TYPE_MASK) {
1839 case ROOT_DESC_BITMAP:
1840 desc >>= ROOT_DESC_TYPE_SHIFT;
1842 if ((desc & 1) && *start_root)
1843 check_obj_not_in_domain (*start_root);
1848 case ROOT_DESC_COMPLEX: {
1849 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1850 int bwords = (*bitmap_data) - 1;
1851 void **start_run = start_root;
1853 while (bwords-- > 0) {
1854 gsize bmap = *bitmap_data++;
1855 void **objptr = start_run;
1857 if ((bmap & 1) && *objptr)
1858 check_obj_not_in_domain (*objptr);
1862 start_run += GC_BITS_PER_WORD;
1866 case ROOT_DESC_USER: {
1867 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1868 marker (start_root, check_obj_not_in_domain);
1871 case ROOT_DESC_RUN_LEN:
1872 g_assert_not_reached ();
1874 g_assert_not_reached ();
1878 check_domain = NULL;
1882 check_for_xdomain_refs (void)
1886 scan_area_with_callback (nursery_section->data, nursery_section->end_data, scan_object_for_xdomain_refs, NULL);
1888 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1890 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1891 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1895 clear_domain_process_object (char *obj, MonoDomain *domain)
1899 process_object_for_domain_clearing (obj, domain);
1900 remove = need_remove_object_for_domain (obj, domain);
1902 if (remove && ((MonoObject*)obj)->synchronisation) {
1903 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1905 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1912 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1914 if (clear_domain_process_object (obj, domain))
1915 memset (obj, 0, size);
1919 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1921 clear_domain_process_object (obj, domain);
1925 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1927 if (need_remove_object_for_domain (obj, domain))
1928 major_free_non_pinned_object (obj, size);
1932 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1934 if (need_remove_object_for_domain (obj, domain))
1935 free_pinned_object (obj, size);
1939 * When appdomains are unloaded we can easily remove objects that have finalizers,
1940 * but all the others could still be present in random places on the heap.
1941 * We need a sweep to get rid of them even though it's going to be costly
1943 * The reason we need to remove them is because we access the vtable and class
1944 * structures to know the object size and the reference bitmap: once the domain is
1945 * unloaded the point to random memory.
1948 mono_gc_clear_domain (MonoDomain * domain)
1950 LOSObject *bigobj, *prev;
1955 clear_nursery_fragments (nursery_next);
1957 if (xdomain_checks && domain != mono_get_root_domain ()) {
1958 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1959 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1960 check_for_xdomain_refs ();
1963 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1964 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain);
1966 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1967 to memory returned to the OS.*/
1968 null_ephemerons_for_domain (domain);
1970 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1971 null_links_for_domain (domain, i);
1973 /* We need two passes over major and large objects because
1974 freeing such objects might give their memory back to the OS
1975 (in the case of large objects) or obliterate its vtable
1976 (pinned objects with major-copying or pinned and non-pinned
1977 objects with major-mark&sweep), but we might need to
1978 dereference a pointer from an object to another object if
1979 the first object is a proxy. */
1980 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1981 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1982 clear_domain_process_object (bigobj->data, domain);
1985 for (bigobj = los_object_list; bigobj;) {
1986 if (need_remove_object_for_domain (bigobj->data, domain)) {
1987 LOSObject *to_free = bigobj;
1989 prev->next = bigobj->next;
1991 los_object_list = bigobj->next;
1992 bigobj = bigobj->next;
1993 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1995 free_large_object (to_free);
1999 bigobj = bigobj->next;
2001 major_iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
2002 major_iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
2008 global_remset_cache_clear (void)
2010 memset (global_remset_cache, 0, sizeof (global_remset_cache));
2014 * Tries to check if a given remset location was already added to the global remset.
2017 * A 2 entry, LRU cache of recently saw location remsets.
2019 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
2021 * Returns TRUE is the element was added..
2024 global_remset_location_was_not_added (gpointer ptr)
2027 gpointer first = global_remset_cache [0], second;
2029 HEAVY_STAT (++stat_global_remsets_discarded);
2033 second = global_remset_cache [1];
2035 if (second == ptr) {
2036 /*Move the second to the front*/
2037 global_remset_cache [0] = second;
2038 global_remset_cache [1] = first;
2040 HEAVY_STAT (++stat_global_remsets_discarded);
2044 global_remset_cache [0] = second;
2045 global_remset_cache [1] = ptr;
2050 * add_to_global_remset:
2052 * The global remset contains locations which point into newspace after
2053 * a minor collection. This can happen if the objects they point to are pinned.
2056 add_to_global_remset (gpointer ptr)
2060 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
2062 if (!global_remset_location_was_not_added (ptr))
2065 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
2066 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
2068 HEAVY_STAT (++stat_global_remsets_added);
2071 * FIXME: If an object remains pinned, we need to add it at every minor collection.
2072 * To avoid uncontrolled growth of the global remset, only add each pointer once.
2074 if (global_remset->store_next + 3 < global_remset->end_set) {
2075 *(global_remset->store_next++) = (mword)ptr;
2078 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
2079 rs->next = global_remset;
2081 *(global_remset->store_next++) = (mword)ptr;
2084 int global_rs_size = 0;
2086 for (rs = global_remset; rs; rs = rs->next) {
2087 global_rs_size += rs->store_next - rs->data;
2089 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
2094 * FIXME: allocate before calling this function and pass the
2095 * destination address.
2098 copy_object_no_checks (void *obj)
2100 static const void *copy_labels [] = { &&LAB_0, &&LAB_1, &&LAB_2, &&LAB_3, &&LAB_4, &&LAB_5, &&LAB_6, &&LAB_7, &&LAB_8 };
2104 MonoVTable *vt = ((MonoObject*)obj)->vtable;
2105 gboolean has_references = vt->gc_descr != DESC_TYPE_RUN_LENGTH;
2107 objsize = safe_object_get_size ((MonoObject*)obj);
2108 objsize += ALLOC_ALIGN - 1;
2109 objsize &= ~(ALLOC_ALIGN - 1);
2111 DEBUG (9, g_assert (vt->klass->inited));
2112 MAJOR_GET_COPY_OBJECT_SPACE (destination, objsize, has_references);
2114 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %zd)\n", destination, ((MonoObject*)obj)->vtable->klass->name, objsize));
2115 binary_protocol_copy (obj, destination, ((MonoObject*)obj)->vtable, objsize);
2117 if (objsize <= sizeof (gpointer) * 8) {
2118 mword *dest = (mword*)destination;
2119 goto *copy_labels [objsize / sizeof (gpointer)];
2121 (dest) [7] = ((mword*)obj) [7];
2123 (dest) [6] = ((mword*)obj) [6];
2125 (dest) [5] = ((mword*)obj) [5];
2127 (dest) [4] = ((mword*)obj) [4];
2129 (dest) [3] = ((mword*)obj) [3];
2131 (dest) [2] = ((mword*)obj) [2];
2133 (dest) [1] = ((mword*)obj) [1];
2135 (dest) [0] = ((mword*)obj) [0];
2143 char* edi = destination;
2144 __asm__ __volatile__(
2146 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
2147 : "0" (objsize/4), "1" (edi),"2" (esi)
2152 memcpy (destination, obj, objsize);
2155 /* adjust array->bounds */
2156 DEBUG (9, g_assert (vt->gc_descr));
2157 if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
2158 MonoArray *array = (MonoArray*)destination;
2159 array->bounds = (MonoArrayBounds*)((char*)destination + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
2160 DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %zd, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
2162 /* set the forwarding pointer */
2163 forward_object (obj, destination);
2164 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
2165 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2166 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2167 moved_objects_idx = 0;
2169 moved_objects [moved_objects_idx++] = obj;
2170 moved_objects [moved_objects_idx++] = destination;
2173 if (has_references) {
2174 DEBUG (9, fprintf (gc_debug_file, "Enqueuing gray object %p (%s)\n", obj, safe_name (obj)));
2175 GRAY_OBJECT_ENQUEUE (obj);
2181 * This is how the copying happens from the nursery to the old generation.
2182 * We assume that at this time all the pinned objects have been identified and
2184 * We run scan_object() for each pinned object so that each referenced
2185 * objects if possible are copied. The new gray objects created can have
2186 * scan_object() run on them right away, too.
2187 * Then we run copy_object() for the precisely tracked roots. At this point
2188 * all the roots are either gray or black. We run scan_object() on the gray
2189 * objects until no more gray objects are created.
2190 * At the end of the process we walk again the pinned list and we unmark
2191 * the pinned flag. As we go we also create the list of free space for use
2192 * in the next allocation runs.
2194 * We need to remember objects from the old generation that point to the new one
2195 * (or just addresses?).
2197 * copy_object could be made into a macro once debugged (use inline for now).
2200 static void __attribute__((noinline))
2201 copy_object (void **obj_slot)
2204 char *obj = *obj_slot;
2206 DEBUG (9, g_assert (current_collection_generation == GENERATION_NURSERY));
2208 HEAVY_STAT (++stat_copy_object_called_nursery);
2210 if (!ptr_in_nursery (obj)) {
2211 HEAVY_STAT (++stat_nursery_copy_object_failed_from_space);
2215 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
2218 * Before we can copy the object we must make sure that we are
2219 * allowed to, i.e. that the object not pinned or not already
2223 if ((forwarded = object_is_forwarded (obj))) {
2224 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2225 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
2226 HEAVY_STAT (++stat_nursery_copy_object_failed_forwarded);
2227 *obj_slot = forwarded;
2230 if (object_is_pinned (obj)) {
2231 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2232 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
2233 HEAVY_STAT (++stat_nursery_copy_object_failed_pinned);
2237 HEAVY_STAT (++stat_objects_copied_nursery);
2239 *obj_slot = copy_object_no_checks (obj);
2243 #define HANDLE_PTR(ptr,obj) do { \
2244 void *__old = *(ptr); \
2247 copy_object ((ptr)); \
2249 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2250 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2251 add_to_global_remset ((ptr)); \
2256 * Scan the object pointed to by @start for references to
2257 * other objects between @from_start and @from_end and copy
2258 * them to the gray_objects area.
2261 scan_object (char *start)
2263 #include "sgen-scan-object.h"
2265 HEAVY_STAT (++stat_scan_object_called_nursery);
2271 * Scan the valuetype pointed to by START, described by DESC for references to
2272 * other objects between @from_start and @from_end and copy them to the gray_objects area.
2273 * Returns a pointer to the end of the object.
2276 scan_vtype (char *start, mword desc, char* from_start, char* from_end)
2280 /* The descriptors include info about the MonoObject header as well */
2281 start -= sizeof (MonoObject);
2283 switch (desc & 0x7) {
2284 case DESC_TYPE_RUN_LENGTH:
2285 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
2286 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
2287 g_assert (skip_size);
2288 return start + skip_size;
2289 case DESC_TYPE_SMALL_BITMAP:
2290 OBJ_BITMAP_FOREACH_PTR (desc,start);
2291 OBJ_BITMAP_SIZE (skip_size, desc, start);
2292 return start + skip_size;
2293 case DESC_TYPE_LARGE_BITMAP:
2294 case DESC_TYPE_COMPLEX:
2296 g_assert_not_reached ();
2299 // The other descriptors can't happen with vtypes
2300 g_assert_not_reached ();
2307 #define HANDLE_PTR(ptr,obj) do { \
2308 void *__old = *(ptr); \
2311 major_copy_or_mark_object ((ptr)); \
2313 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2314 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2315 add_to_global_remset ((ptr)); \
2320 major_scan_object (char *start)
2322 #include "sgen-scan-object.h"
2324 HEAVY_STAT (++stat_scan_object_called_major);
2330 * Scan objects in the gray stack until the stack is empty. This should be called
2331 * frequently after each object is copied, to achieve better locality and cache
2335 drain_gray_stack (void)
2339 if (current_collection_generation == GENERATION_NURSERY) {
2341 GRAY_OBJECT_DEQUEUE (obj);
2344 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2349 GRAY_OBJECT_DEQUEUE (obj);
2352 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2353 major_scan_object (obj);
2359 * Addresses from start to end are already sorted. This function finds
2360 * the object header for each address and pins the object. The
2361 * addresses must be inside the passed section. The (start of the)
2362 * address array is overwritten with the addresses of the actually
2363 * pinned objects. Return the number of pinned objects.
2366 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
2371 void *last_obj = NULL;
2372 size_t last_obj_size = 0;
2375 void **definitely_pinned = start;
2376 while (start < end) {
2378 /* the range check should be reduntant */
2379 if (addr != last && addr >= start_nursery && addr < end_nursery) {
2380 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
2381 /* multiple pointers to the same object */
2382 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
2386 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
2387 g_assert (idx < section->num_scan_start);
2388 search_start = (void*)section->scan_starts [idx];
2389 if (!search_start || search_start > addr) {
2392 search_start = section->scan_starts [idx];
2393 if (search_start && search_start <= addr)
2396 if (!search_start || search_start > addr)
2397 search_start = start_nursery;
2399 if (search_start < last_obj)
2400 search_start = (char*)last_obj + last_obj_size;
2401 /* now addr should be in an object a short distance from search_start
2402 * Note that search_start must point to zeroed mem or point to an object.
2405 if (!*(void**)search_start) {
2406 mword p = (mword)search_start;
2407 p += sizeof (gpointer);
2408 p += ALLOC_ALIGN - 1;
2409 p &= ~(ALLOC_ALIGN - 1);
2410 search_start = (void*)p;
2413 last_obj = search_start;
2414 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
2415 last_obj_size += ALLOC_ALIGN - 1;
2416 last_obj_size &= ~(ALLOC_ALIGN - 1);
2417 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
2418 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
2419 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));
2420 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
2421 pin_object (search_start);
2422 GRAY_OBJECT_ENQUEUE (search_start);
2424 pin_stats_register_object (search_start, last_obj_size);
2425 definitely_pinned [count] = search_start;
2429 /* skip to the next object */
2430 search_start = (void*)((char*)search_start + last_obj_size);
2431 } while (search_start <= addr);
2432 /* we either pinned the correct object or we ignored the addr because
2433 * it points to unused zeroed memory.
2439 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
2444 pin_objects_in_section (GCMemSection *section)
2446 int start = section->pin_queue_start;
2447 int end = section->pin_queue_end;
2450 reduced_to = pin_objects_from_addresses (section, pin_queue + start, pin_queue + end,
2451 section->data, section->next_data);
2452 section->pin_queue_start = start;
2453 section->pin_queue_end = start + reduced_to;
2460 gap = (gap * 10) / 13;
2461 if (gap == 9 || gap == 10)
2468 /* Sort the addresses in array in increasing order.
2469 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
2472 sort_addresses (void **array, int size)
2477 for (i = 1; i < size; ++i) {
2480 int parent = (child - 1) / 2;
2482 if (array [parent] >= array [child])
2485 tmp = array [parent];
2486 array [parent] = array [child];
2487 array [child] = tmp;
2493 for (i = size - 1; i > 0; --i) {
2496 array [i] = array [0];
2502 while (root * 2 + 1 <= end) {
2503 int child = root * 2 + 1;
2505 if (child < end && array [child] < array [child + 1])
2507 if (array [root] >= array [child])
2511 array [root] = array [child];
2512 array [child] = tmp;
2519 static G_GNUC_UNUSED void
2520 print_nursery_gaps (void* start_nursery, void *end_nursery)
2523 gpointer first = start_nursery;
2525 for (i = 0; i < next_pin_slot; ++i) {
2526 next = pin_queue [i];
2527 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2531 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2534 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
2536 optimize_pin_queue (int start_slot)
2538 void **start, **cur, **end;
2539 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
2540 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
2541 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
2542 if ((next_pin_slot - start_slot) > 1)
2543 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
2544 start = cur = pin_queue + start_slot;
2545 end = pin_queue + next_pin_slot;
2548 while (*start == *cur && cur < end)
2552 next_pin_slot = start - pin_queue;
2553 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2554 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2559 * Scan the memory between start and end and queue values which could be pointers
2560 * to the area between start_nursery and end_nursery for later consideration.
2561 * Typically used for thread stacks.
2564 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2567 while (start < end) {
2568 if (*start >= start_nursery && *start < end_nursery) {
2570 * *start can point to the middle of an object
2571 * note: should we handle pointing at the end of an object?
2572 * pinning in C# code disallows pointing at the end of an object
2573 * but there is some small chance that an optimizing C compiler
2574 * may keep the only reference to an object by pointing
2575 * at the end of it. We ignore this small chance for now.
2576 * Pointers to the end of an object are indistinguishable
2577 * from pointers to the start of the next object in memory
2578 * so if we allow that we'd need to pin two objects...
2579 * We queue the pointer in an array, the
2580 * array will then be sorted and uniqued. This way
2581 * we can coalesce several pinning pointers and it should
2582 * be faster since we'd do a memory scan with increasing
2583 * addresses. Note: we can align the address to the allocation
2584 * alignment, so the unique process is more effective.
2586 mword addr = (mword)*start;
2587 addr &= ~(ALLOC_ALIGN - 1);
2588 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2589 pin_stage_ptr ((void*)addr);
2591 pin_stats_register_address ((char*)addr, pin_type);
2592 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
2597 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2601 * Debugging function: find in the conservative roots where @obj is being pinned.
2603 static G_GNUC_UNUSED void
2604 find_pinning_reference (char *obj, size_t size)
2608 char *endobj = obj + size;
2609 for (i = 0; i < roots_hash_size [0]; ++i) {
2610 for (root = roots_hash [0][i]; root; root = root->next) {
2611 /* if desc is non-null it has precise info */
2612 if (!root->root_desc) {
2613 char ** start = (char**)root->start_root;
2614 while (start < (char**)root->end_root) {
2615 if (*start >= obj && *start < endobj) {
2616 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));
2623 find_pinning_ref_from_thread (obj, size);
2627 * The first thing we do in a collection is to identify pinned objects.
2628 * This function considers all the areas of memory that need to be
2629 * conservatively scanned.
2632 pin_from_roots (void *start_nursery, void *end_nursery)
2636 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]));
2637 /* objects pinned from the API are inside these roots */
2638 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2639 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2640 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2641 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2644 /* now deal with the thread stacks
2645 * in the future we should be able to conservatively scan only:
2646 * *) the cpu registers
2647 * *) the unmanaged stack frames
2648 * *) the _last_ managed stack frame
2649 * *) pointers slots in managed frames
2651 scan_thread_data (start_nursery, end_nursery, FALSE);
2653 evacuate_pin_staging_area ();
2657 * The memory area from start_root to end_root contains pointers to objects.
2658 * Their position is precisely described by @desc (this means that the pointer
2659 * can be either NULL or the pointer to the start of an object).
2660 * This functions copies them to to_space updates them.
2663 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
2665 switch (desc & ROOT_DESC_TYPE_MASK) {
2666 case ROOT_DESC_BITMAP:
2667 desc >>= ROOT_DESC_TYPE_SHIFT;
2669 if ((desc & 1) && *start_root) {
2670 copy_func (start_root);
2671 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2672 drain_gray_stack ();
2678 case ROOT_DESC_COMPLEX: {
2679 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2680 int bwords = (*bitmap_data) - 1;
2681 void **start_run = start_root;
2683 while (bwords-- > 0) {
2684 gsize bmap = *bitmap_data++;
2685 void **objptr = start_run;
2687 if ((bmap & 1) && *objptr) {
2689 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2690 drain_gray_stack ();
2695 start_run += GC_BITS_PER_WORD;
2699 case ROOT_DESC_USER: {
2700 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2701 marker (start_root, copy_func);
2704 case ROOT_DESC_RUN_LEN:
2705 g_assert_not_reached ();
2707 g_assert_not_reached ();
2712 alloc_fragment (void)
2714 Fragment *frag = fragment_freelist;
2716 fragment_freelist = frag->next;
2720 frag = get_internal_mem (sizeof (Fragment), INTERNAL_MEM_FRAGMENT);
2725 /* size must be a power of 2 */
2727 get_os_memory_aligned (mword size, mword alignment, gboolean activate)
2729 /* Allocate twice the memory to be able to put the block on an aligned address */
2730 char *mem = get_os_memory (size + alignment, activate);
2735 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2736 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2739 free_os_memory (mem, aligned - mem);
2740 if (aligned + size < mem + size + alignment)
2741 free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2747 * Allocate and setup the data structures needed to be able to allocate objects
2748 * in the nursery. The nursery is stored in nursery_section.
2751 alloc_nursery (void)
2753 GCMemSection *section;
2759 if (nursery_section)
2761 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %zd\n", nursery_size));
2762 /* later we will alloc a larger area for the nursery but only activate
2763 * what we need. The rest will be used as expansion if we have too many pinned
2764 * objects in the existing nursery.
2766 /* FIXME: handle OOM */
2767 section = get_internal_mem (SIZEOF_GC_MEM_SECTION, INTERNAL_MEM_SECTION);
2769 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2770 alloc_size = nursery_size;
2771 #ifdef ALIGN_NURSERY
2772 data = get_os_memory_aligned (alloc_size, alloc_size, TRUE);
2774 data = get_os_memory (alloc_size, TRUE);
2776 nursery_start = data;
2777 nursery_real_end = nursery_start + nursery_size;
2778 UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
2779 nursery_next = nursery_start;
2780 total_alloc += alloc_size;
2781 DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %zd, total: %zd\n", data, data + alloc_size, nursery_size, total_alloc));
2782 section->data = section->next_data = data;
2783 section->size = alloc_size;
2784 section->end_data = nursery_real_end;
2785 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2786 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2787 section->num_scan_start = scan_starts;
2788 section->block.role = MEMORY_ROLE_GEN0;
2789 section->block.next = NULL;
2791 nursery_section = section;
2793 /* Setup the single first large fragment */
2794 frag = alloc_fragment ();
2795 frag->fragment_start = nursery_start;
2796 frag->fragment_limit = nursery_start;
2797 frag->fragment_end = nursery_real_end;
2798 nursery_frag_real_end = nursery_real_end;
2799 /* FIXME: frag here is lost */
2803 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list) {
2806 for (fin = list; fin; fin = fin->next) {
2809 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2810 copy_func (&fin->object);
2814 static mword fragment_total = 0;
2816 * We found a fragment of free memory in the nursery: memzero it and if
2817 * it is big enough, add it to the list of fragments that can be used for
2821 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2824 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2825 binary_protocol_empty (frag_start, frag_size);
2826 /* memsetting just the first chunk start is bound to provide better cache locality */
2827 if (nursery_clear_policy == CLEAR_AT_GC)
2828 memset (frag_start, 0, frag_size);
2829 /* Not worth dealing with smaller fragments: need to tune */
2830 if (frag_size >= FRAGMENT_MIN_SIZE) {
2831 fragment = alloc_fragment ();
2832 fragment->fragment_start = frag_start;
2833 fragment->fragment_limit = frag_start;
2834 fragment->fragment_end = frag_end;
2835 fragment->next = nursery_fragments;
2836 nursery_fragments = fragment;
2837 fragment_total += frag_size;
2839 /* Clear unused fragments, pinning depends on this */
2840 /*TODO place an int[] here instead of the memset if size justify it*/
2841 memset (frag_start, 0, frag_size);
2846 generation_name (int generation)
2848 switch (generation) {
2849 case GENERATION_NURSERY: return "nursery";
2850 case GENERATION_OLD: return "old";
2851 default: g_assert_not_reached ();
2855 static DisappearingLinkHashTable*
2856 get_dislink_hash_table (int generation)
2858 switch (generation) {
2859 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2860 case GENERATION_OLD: return &major_disappearing_link_hash;
2861 default: g_assert_not_reached ();
2865 static FinalizeEntryHashTable*
2866 get_finalize_entry_hash_table (int generation)
2868 switch (generation) {
2869 case GENERATION_NURSERY: return &minor_finalizable_hash;
2870 case GENERATION_OLD: return &major_finalizable_hash;
2871 default: g_assert_not_reached ();
2876 finish_gray_stack (char *start_addr, char *end_addr, int generation)
2881 int ephemeron_rounds = 0;
2882 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? copy_object : major_copy_or_mark_object;
2885 * We copied all the reachable objects. Now it's the time to copy
2886 * the objects that were not referenced by the roots, but by the copied objects.
2887 * we built a stack of objects pointed to by gray_start: they are
2888 * additional roots and we may add more items as we go.
2889 * We loop until gray_start == gray_objects which means no more objects have
2890 * been added. Note this is iterative: no recursion is involved.
2891 * We need to walk the LO list as well in search of marked big objects
2892 * (use a flag since this is needed only on major collections). We need to loop
2893 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2894 * To achieve better cache locality and cache usage, we drain the gray stack
2895 * frequently, after each object is copied, and just finish the work here.
2897 drain_gray_stack ();
2899 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2900 /* walk the finalization queue and move also the objects that need to be
2901 * finalized: use the finalized objects as new roots so the objects they depend
2902 * on are also not reclaimed. As with the roots above, only objects in the nursery
2903 * are marked/copied.
2904 * We need a loop here, since objects ready for finalizers may reference other objects
2905 * that are fin-ready. Speedup with a flag?
2909 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2910 * before processing finalizable objects to avoid finalizing reachable values.
2912 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2913 * while they are been finalized.
2915 int done_with_ephemerons = 0;
2917 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr);
2918 drain_gray_stack ();
2920 } while (!done_with_ephemerons);
2922 fin_ready = num_ready_finalizers;
2923 finalize_in_range (copy_func, start_addr, end_addr, generation);
2924 if (generation == GENERATION_OLD)
2925 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY);
2927 /* drain the new stack that might have been created */
2928 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2929 drain_gray_stack ();
2930 } while (fin_ready != num_ready_finalizers);
2933 * Clear ephemeron pairs with unreachable keys.
2934 * We pass the copy func so we can figure out if an array was promoted or not.
2936 clear_unreachable_ephemerons (copy_func, start_addr, end_addr);
2939 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));
2942 * handle disappearing links
2943 * Note we do this after checking the finalization queue because if an object
2944 * survives (at least long enough to be finalized) we don't clear the link.
2945 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2946 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2949 g_assert (gray_object_queue_is_empty ());
2951 null_link_in_range (copy_func, start_addr, end_addr, generation);
2952 if (generation == GENERATION_OLD)
2953 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY);
2954 if (gray_object_queue_is_empty ())
2956 drain_gray_stack ();
2959 g_assert (gray_object_queue_is_empty ());
2963 check_section_scan_starts (GCMemSection *section)
2966 for (i = 0; i < section->num_scan_start; ++i) {
2967 if (section->scan_starts [i]) {
2968 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2969 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2975 check_scan_starts (void)
2977 if (!do_scan_starts_check)
2979 check_section_scan_starts (nursery_section);
2980 major_check_scan_starts ();
2983 static int last_num_pinned = 0;
2986 build_nursery_fragments (int start_pin, int end_pin)
2988 char *frag_start, *frag_end;
2992 while (nursery_fragments) {
2993 Fragment *next = nursery_fragments->next;
2994 nursery_fragments->next = fragment_freelist;
2995 fragment_freelist = nursery_fragments;
2996 nursery_fragments = next;
2998 frag_start = nursery_start;
3000 /* clear scan starts */
3001 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
3002 for (i = start_pin; i < end_pin; ++i) {
3003 frag_end = pin_queue [i];
3004 /* remove the pin bit from pinned objects */
3005 unpin_object (frag_end);
3006 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
3007 frag_size = frag_end - frag_start;
3009 add_nursery_frag (frag_size, frag_start, frag_end);
3010 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
3011 frag_size += ALLOC_ALIGN - 1;
3012 frag_size &= ~(ALLOC_ALIGN - 1);
3013 frag_start = (char*)pin_queue [i] + frag_size;
3015 nursery_last_pinned_end = frag_start;
3016 frag_end = nursery_real_end;
3017 frag_size = frag_end - frag_start;
3019 add_nursery_frag (frag_size, frag_start, frag_end);
3020 if (!nursery_fragments) {
3021 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
3022 for (i = start_pin; i < end_pin; ++i) {
3023 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])));
3028 nursery_next = nursery_frag_real_end = NULL;
3030 /* Clear TLABs for all threads */
3035 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type)
3039 for (i = 0; i < roots_hash_size [root_type]; ++i) {
3040 for (root = roots_hash [root_type][i]; root; root = root->next) {
3041 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
3042 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
3048 dump_occupied (char *start, char *end, char *section_start)
3050 fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
3054 dump_section (GCMemSection *section, const char *type)
3056 char *start = section->data;
3057 char *end = section->data + section->size;
3058 char *occ_start = NULL;
3060 char *old_start = NULL; /* just for debugging */
3062 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", type, section->size);
3064 while (start < end) {
3068 if (!*(void**)start) {
3070 dump_occupied (occ_start, start, section->data);
3073 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
3076 g_assert (start < section->next_data);
3081 vt = (GCVTable*)LOAD_VTABLE (start);
3084 size = safe_object_get_size ((MonoObject*) start);
3085 size += ALLOC_ALIGN - 1;
3086 size &= ~(ALLOC_ALIGN - 1);
3089 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
3090 start - section->data,
3091 vt->klass->name_space, vt->klass->name,
3099 dump_occupied (occ_start, start, section->data);
3101 fprintf (heap_dump_file, "</section>\n");
3105 dump_object (MonoObject *obj, gboolean dump_location)
3107 static char class_name [1024];
3109 MonoClass *class = mono_object_class (obj);
3113 * Python's XML parser is too stupid to parse angle brackets
3114 * in strings, so we just ignore them;
3117 while (class->name [i] && j < sizeof (class_name) - 1) {
3118 if (!strchr ("<>\"", class->name [i]))
3119 class_name [j++] = class->name [i];
3122 g_assert (j < sizeof (class_name));
3125 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
3126 class->name_space, class_name,
3127 safe_object_get_size (obj));
3128 if (dump_location) {
3129 const char *location;
3130 if (ptr_in_nursery (obj))
3131 location = "nursery";
3132 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
3136 fprintf (heap_dump_file, " location=\"%s\"", location);
3138 fprintf (heap_dump_file, "/>\n");
3142 dump_heap (const char *type, int num, const char *reason)
3144 static char const *internal_mem_names [] = { "pin-queue", "fragment", "section", "scan-starts",
3145 "fin-table", "finalize-entry", "dislink-table",
3146 "dislink", "roots-table", "root-record", "statistics",
3147 "remset", "gray-queue", "store-remset", "marksweep-tables",
3148 "marksweep-block-info", "ephemeron-link" };
3154 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
3156 fprintf (heap_dump_file, " reason=\"%s\"", reason);
3157 fprintf (heap_dump_file, ">\n");
3158 fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%lld\"/>\n", pinned_chunk_bytes_alloced);
3159 fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%lld\"/>\n", large_internal_bytes_alloced);
3160 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
3161 for (i = 0; i < INTERNAL_MEM_MAX; ++i)
3162 fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n", internal_mem_names [i], small_internal_mem_bytes [i]);
3163 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
3164 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
3165 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
3167 fprintf (heap_dump_file, "<pinned-objects>\n");
3168 for (list = pinned_objects; list; list = list->next)
3169 dump_object (list->obj, TRUE);
3170 fprintf (heap_dump_file, "</pinned-objects>\n");
3172 dump_section (nursery_section, "nursery");
3176 fprintf (heap_dump_file, "<los>\n");
3177 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
3178 dump_object ((MonoObject*)bigobj->data, FALSE);
3179 fprintf (heap_dump_file, "</los>\n");
3181 fprintf (heap_dump_file, "</collection>\n");
3187 static gboolean inited = FALSE;
3192 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
3193 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
3194 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
3195 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
3196 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
3197 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
3198 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3199 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
3201 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
3202 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
3203 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
3204 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
3205 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
3206 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
3207 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
3208 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
3209 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
3210 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
3211 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
3213 #ifdef HEAVY_STATISTICS
3214 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
3215 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
3216 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
3217 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
3218 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
3219 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
3220 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
3221 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
3223 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
3224 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
3225 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
3226 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
3227 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
3229 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
3230 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
3231 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
3232 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
3234 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
3235 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
3237 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
3238 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
3239 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
3241 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
3242 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
3244 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
3245 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
3246 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
3247 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
3248 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
3249 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
3250 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
3251 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
3259 need_major_collection (void)
3261 mword los_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
3262 return minor_collection_sections_alloced * MAJOR_SECTION_SIZE + los_alloced > minor_collection_allowance;
3266 * Collect objects in the nursery. Returns whether to trigger a major
3270 collect_nursery (size_t requested_size)
3272 size_t max_garbage_amount;
3273 char *orig_nursery_next;
3274 TV_DECLARE (all_atv);
3275 TV_DECLARE (all_btv);
3279 current_collection_generation = GENERATION_NURSERY;
3282 binary_protocol_collection (GENERATION_NURSERY);
3283 check_scan_starts ();
3286 orig_nursery_next = nursery_next;
3287 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3288 /* FIXME: optimize later to use the higher address where an object can be present */
3289 nursery_next = MAX (nursery_next, nursery_real_end);
3291 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)));
3292 max_garbage_amount = nursery_next - nursery_start;
3293 g_assert (nursery_section->size >= max_garbage_amount);
3295 /* world must be stopped already */
3296 TV_GETTIME (all_atv);
3299 /* Pinning depends on this */
3300 clear_nursery_fragments (orig_nursery_next);
3303 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3306 check_for_xdomain_refs ();
3308 nursery_section->next_data = nursery_next;
3310 major_start_nursery_collection ();
3312 gray_object_queue_init ();
3315 mono_stats.minor_gc_count ++;
3317 global_remset_cache_clear ();
3319 /* pin from pinned handles */
3321 pin_from_roots (nursery_start, nursery_next);
3322 /* identify pinned objects */
3323 optimize_pin_queue (0);
3324 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
3325 nursery_section->pin_queue_start = 0;
3326 nursery_section->pin_queue_end = next_pin_slot;
3328 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3329 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3330 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3332 if (consistency_check_at_minor_collection)
3333 check_consistency ();
3336 * walk all the roots and copy the young objects to the old generation,
3337 * starting from to_space
3340 scan_from_remsets (nursery_start, nursery_next);
3341 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3343 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3344 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3346 drain_gray_stack ();
3349 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3350 /* registered roots, this includes static fields */
3351 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL);
3352 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER);
3354 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3356 scan_thread_data (nursery_start, nursery_next, TRUE);
3358 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3361 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY);
3363 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3365 /* walk the pin_queue, build up the fragment list of free memory, unmark
3366 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3369 build_nursery_fragments (0, next_pin_slot);
3371 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3372 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %zd bytes available\n", TV_ELAPSED (atv, btv), fragment_total));
3374 if (consistency_check_at_minor_collection)
3375 check_major_refs ();
3377 major_finish_nursery_collection ();
3379 TV_GETTIME (all_btv);
3380 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3383 dump_heap ("minor", num_minor_gcs - 1, NULL);
3385 /* prepare the pin queue for the next collection */
3386 last_num_pinned = next_pin_slot;
3388 if (fin_ready_list || critical_fin_list) {
3389 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3390 mono_gc_finalize_notify ();
3394 g_assert (gray_object_queue_is_empty ());
3396 check_scan_starts ();
3398 current_collection_generation = -1;
3400 return need_major_collection ();
3404 major_do_collection (const char *reason)
3406 LOSObject *bigobj, *prevbo;
3407 TV_DECLARE (all_atv);
3408 TV_DECLARE (all_btv);
3411 /* FIXME: only use these values for the precise scan
3412 * note that to_space pointers should be excluded anyway...
3414 char *heap_start = NULL;
3415 char *heap_end = (char*)-1;
3416 int old_num_major_sections = num_major_sections;
3417 int num_major_sections_saved, save_target, allowance_target;
3418 mword los_memory_saved, los_memory_alloced, old_los_memory_usage;
3421 * A domain could have been freed, resulting in
3422 * los_memory_usage being less than last_los_memory_usage.
3424 los_memory_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
3425 old_los_memory_usage = los_memory_usage;
3427 //count_ref_nonref_objs ();
3428 //consistency_check ();
3431 binary_protocol_collection (GENERATION_OLD);
3432 check_scan_starts ();
3433 gray_object_queue_init ();
3436 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3438 mono_stats.major_gc_count ++;
3440 /* world must be stopped already */
3441 TV_GETTIME (all_atv);
3444 /* Pinning depends on this */
3445 clear_nursery_fragments (nursery_next);
3448 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3451 check_for_xdomain_refs ();
3453 nursery_section->next_data = nursery_real_end;
3454 /* we should also coalesce scanning from sections close to each other
3455 * and deal with pointers outside of the sections later.
3457 /* The remsets are not useful for a major collection */
3459 global_remset_cache_clear ();
3463 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3464 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3465 optimize_pin_queue (0);
3468 * pin_queue now contains all candidate pointers, sorted and
3469 * uniqued. We must do two passes now to figure out which
3470 * objects are pinned.
3472 * The first is to find within the pin_queue the area for each
3473 * section. This requires that the pin_queue be sorted. We
3474 * also process the LOS objects and pinned chunks here.
3476 * The second, destructive, pass is to reduce the section
3477 * areas to pointers to the actually pinned objects.
3479 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3480 /* first pass for the sections */
3481 find_section_pin_queue_start_end (nursery_section);
3482 major_find_pin_queue_start_ends ();
3483 /* identify possible pointers to the insize of large objects */
3484 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3485 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3487 find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &start, &end);
3489 pin_object (bigobj->data);
3490 /* FIXME: only enqueue if object has references */
3491 GRAY_OBJECT_ENQUEUE (bigobj->data);
3493 pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3494 DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
3497 /* second pass for the sections */
3498 pin_objects_in_section (nursery_section);
3499 major_pin_objects ();
3502 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3503 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3504 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3506 major_init_to_space ();
3508 drain_gray_stack ();
3511 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3513 /* registered roots, this includes static fields */
3514 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL);
3515 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER);
3517 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3520 /* FIXME: This is the wrong place for this, because it does
3522 scan_thread_data (heap_start, heap_end, TRUE);
3524 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3527 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3529 /* scan the list of objects ready for finalization */
3530 scan_finalizer_entries (major_copy_or_mark_object, fin_ready_list);
3531 scan_finalizer_entries (major_copy_or_mark_object, critical_fin_list);
3533 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3534 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3537 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3539 /* all the objects in the heap */
3540 finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
3542 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3544 /* sweep the big objects list */
3546 for (bigobj = los_object_list; bigobj;) {
3547 if (object_is_pinned (bigobj->data)) {
3548 unpin_object (bigobj->data);
3551 /* not referenced anywhere, so we can free it */
3553 prevbo->next = bigobj->next;
3555 los_object_list = bigobj->next;
3557 bigobj = bigobj->next;
3558 free_large_object (to_free);
3562 bigobj = bigobj->next;
3568 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3570 /* walk the pin_queue, build up the fragment list of free memory, unmark
3571 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3574 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
3577 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3579 TV_GETTIME (all_btv);
3580 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3583 dump_heap ("major", num_major_gcs - 1, reason);
3585 /* prepare the pin queue for the next collection */
3587 if (fin_ready_list || critical_fin_list) {
3588 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3589 mono_gc_finalize_notify ();
3593 g_assert (gray_object_queue_is_empty ());
3595 num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 0);
3596 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
3598 save_target = ((num_major_sections * MAJOR_SECTION_SIZE) + los_memory_saved) / 2;
3600 * We aim to allow the allocation of as many sections as is
3601 * necessary to reclaim save_target sections in the next
3602 * collection. We assume the collection pattern won't change.
3603 * In the last cycle, we had num_major_sections_saved for
3604 * minor_collection_sections_alloced. Assuming things won't
3605 * change, this must be the same ratio as save_target for
3606 * allowance_target, i.e.
3608 * num_major_sections_saved save_target
3609 * --------------------------------- == ----------------
3610 * minor_collection_sections_alloced allowance_target
3614 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));
3616 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * MAJOR_SECTION_SIZE + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
3618 minor_collection_sections_alloced = 0;
3619 last_los_memory_usage = los_memory_usage;
3621 check_scan_starts ();
3623 //consistency_check ();
3627 major_collection (const char *reason)
3629 if (g_getenv ("MONO_GC_NO_MAJOR")) {
3630 collect_nursery (0);
3634 current_collection_generation = GENERATION_OLD;
3635 major_do_collection (reason);
3636 current_collection_generation = -1;
3640 * When deciding if it's better to collect or to expand, keep track
3641 * of how much garbage was reclaimed with the last collection: if it's too
3643 * This is called when we could not allocate a small object.
3645 static void __attribute__((noinline))
3646 minor_collect_or_expand_inner (size_t size)
3648 int do_minor_collection = 1;
3650 if (!nursery_section) {
3654 if (do_minor_collection) {
3656 if (collect_nursery (size))
3657 major_collection ("minor overflow");
3658 DEBUG (2, fprintf (gc_debug_file, "Heap size: %zd, LOS size: %zd\n", total_alloc, los_memory_usage));
3660 /* this also sets the proper pointers for the next allocation */
3661 if (!search_fragment_for_size (size)) {
3663 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3664 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3665 for (i = 0; i < last_num_pinned; ++i) {
3666 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])));
3671 //report_internal_mem_usage ();
3675 * ######################################################################
3676 * ######## Memory allocation from the OS
3677 * ######################################################################
3678 * This section of code deals with getting memory from the OS and
3679 * allocating memory for GC-internal data structures.
3680 * Internal memory can be handled with a freelist for small objects.
3684 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3685 * This must not require any lock.
3688 get_os_memory (size_t size, int activate)
3691 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3693 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3694 size += pagesize - 1;
3695 size &= ~(pagesize - 1);
3696 ptr = mono_valloc (0, size, prot_flags);
3701 * Free the memory returned by get_os_memory (), returning it to the OS.
3704 free_os_memory (void *addr, size_t size)
3706 mono_vfree (addr, size);
3713 report_pinned_chunk (PinnedChunk *chunk, int seq) {
3715 int i, free_pages, num_free, free_mem;
3717 for (i = 0; i < chunk->num_pages; ++i) {
3718 if (!chunk->page_sizes [i])
3721 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);
3722 free_mem = FREELIST_PAGESIZE * free_pages;
3723 for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
3724 if (!chunk->free_list [i])
3727 p = chunk->free_list [i];
3732 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
3733 free_mem += freelist_sizes [i] * num_free;
3735 printf ("\tfree memory in chunk: %d\n", free_mem);
3741 static G_GNUC_UNUSED void
3742 report_internal_mem_usage (void) {
3745 printf ("Internal memory usage:\n");
3747 for (chunk = internal_chunk_list; chunk; chunk = chunk->block.next) {
3748 report_pinned_chunk (chunk, i++);
3750 printf ("Pinned memory usage:\n");
3751 major_report_pinned_memory_usage ();
3755 * Find the slot number in the freelist for memory chunks that
3756 * can contain @size objects.
3759 slot_for_size (size_t size)
3762 /* do a binary search or lookup table later. */
3763 for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
3764 if (freelist_sizes [slot] >= size)
3767 g_assert_not_reached ();
3772 * Build a free list for @size memory chunks from the memory area between
3773 * start_page and end_page.
3776 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
3780 /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
3781 p = (void**)start_page;
3782 end = (void**)(end_page - size);
3783 g_assert (!chunk->free_list [slot]);
3784 chunk->free_list [slot] = p;
3785 while ((char*)p + size <= (char*)end) {
3787 *p = (void*)((char*)p + size);
3791 /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
3795 alloc_pinned_chunk (void)
3799 int size = PINNED_CHUNK_SIZE;
3801 chunk = get_os_memory_aligned (size, size, TRUE);
3802 chunk->block.role = MEMORY_ROLE_PINNED;
3804 UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
3805 total_alloc += size;
3806 pinned_chunk_bytes_alloced += size;
3808 /* setup the bookeeping fields */
3809 chunk->num_pages = size / FREELIST_PAGESIZE;
3810 offset = G_STRUCT_OFFSET (PinnedChunk, data);
3811 chunk->page_sizes = (void*)((char*)chunk + offset);
3812 offset += sizeof (int) * chunk->num_pages;
3813 offset += ALLOC_ALIGN - 1;
3814 offset &= ~(ALLOC_ALIGN - 1);
3815 chunk->free_list = (void*)((char*)chunk + offset);
3816 offset += sizeof (void*) * FREELIST_NUM_SLOTS;
3817 offset += ALLOC_ALIGN - 1;
3818 offset &= ~(ALLOC_ALIGN - 1);
3819 chunk->start_data = (void*)((char*)chunk + offset);
3821 /* allocate the first page to the freelist */
3822 chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
3823 build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
3824 DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
3828 /* assumes freelist for slot is empty, so try to alloc a new page */
3830 get_chunk_freelist (PinnedChunk *chunk, int slot)
3834 p = chunk->free_list [slot];
3836 chunk->free_list [slot] = *p;
3839 for (i = 0; i < chunk->num_pages; ++i) {
3841 if (chunk->page_sizes [i])
3843 size = freelist_sizes [slot];
3844 chunk->page_sizes [i] = size;
3845 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
3849 p = chunk->free_list [slot];
3851 chunk->free_list [slot] = *p;
3857 /* used for the GC-internal data structures */
3859 get_internal_mem (size_t size, int type)
3863 PinnedChunk *pchunk;
3865 if (size > freelist_sizes [FREELIST_NUM_SLOTS - 1]) {
3866 LargeInternalMemHeader *mh;
3868 size += sizeof (LargeInternalMemHeader);
3869 mh = get_os_memory (size, TRUE);
3870 mh->magic = LARGE_INTERNAL_MEM_HEADER_MAGIC;
3873 large_internal_bytes_alloced += size;
3878 slot = slot_for_size (size);
3879 g_assert (size <= freelist_sizes [slot]);
3881 small_internal_mem_bytes [type] += freelist_sizes [slot];
3883 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3884 void **p = pchunk->free_list [slot];
3886 pchunk->free_list [slot] = *p;
3887 memset (p, 0, size);
3891 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3892 res = get_chunk_freelist (pchunk, slot);
3894 memset (res, 0, size);
3898 pchunk = alloc_pinned_chunk ();
3899 /* FIXME: handle OOM */
3900 pchunk->block.next = internal_chunk_list;
3901 internal_chunk_list = pchunk;
3902 res = get_chunk_freelist (pchunk, slot);
3903 memset (res, 0, size);
3908 free_internal_mem (void *addr, int type)
3910 PinnedChunk *pchunk;
3911 LargeInternalMemHeader *mh;
3914 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3915 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
3916 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
3917 int offset = (char*)addr - (char*)pchunk;
3918 int page = offset / FREELIST_PAGESIZE;
3919 int slot = slot_for_size (pchunk->page_sizes [page]);
3921 *p = pchunk->free_list [slot];
3922 pchunk->free_list [slot] = p;
3924 small_internal_mem_bytes [type] -= freelist_sizes [slot];
3929 mh = (LargeInternalMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargeInternalMemHeader, data));
3930 g_assert (mh->magic == LARGE_INTERNAL_MEM_HEADER_MAGIC);
3931 large_internal_bytes_alloced -= mh->size;
3932 free_os_memory (mh, mh->size);
3936 * ######################################################################
3937 * ######## Object allocation
3938 * ######################################################################
3939 * This section of code deals with allocating memory for objects.
3940 * There are several ways:
3941 * *) allocate large objects
3942 * *) allocate normal objects
3943 * *) fast lock-free allocation
3944 * *) allocation of pinned objects
3948 free_large_object (LOSObject *obj)
3950 size_t size = obj->size;
3951 DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %zd\n", obj->data, obj->size));
3952 binary_protocol_empty (obj->data, obj->size);
3954 los_memory_usage -= size;
3955 size += sizeof (LOSObject);
3956 size += pagesize - 1;
3957 size &= ~(pagesize - 1);
3958 total_alloc -= size;
3960 free_os_memory (obj, size);
3964 * Objects with size >= 64KB are allocated in the large object space.
3965 * They are currently kept track of with a linked list.
3966 * They don't move, so there is no need to pin them during collection
3967 * and we avoid the memcpy overhead.
3969 static void* __attribute__((noinline))
3970 alloc_large_inner (MonoVTable *vtable, size_t size)
3976 g_assert (size > MAX_SMALL_OBJ_SIZE);
3978 if (need_major_collection ()) {
3979 DEBUG (4, fprintf (gc_debug_file, "Should trigger major collection: req size %zd (los already: %zu)\n", size, los_memory_usage));
3981 major_collection ("LOS overflow");
3985 alloc_size += sizeof (LOSObject);
3986 alloc_size += pagesize - 1;
3987 alloc_size &= ~(pagesize - 1);
3988 /* FIXME: handle OOM */
3989 obj = get_os_memory (alloc_size, TRUE);
3990 g_assert (!((mword)obj->data & (ALLOC_ALIGN - 1)));
3992 vtslot = (void**)obj->data;
3994 total_alloc += alloc_size;
3995 UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
3996 obj->next = los_object_list;
3997 los_object_list = obj;
3998 los_memory_usage += size;
4000 DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %zd\n", obj->data, vtable, vtable->klass->name, size));
4001 binary_protocol_alloc (obj->data, vtable, size);
4006 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
4008 /* remove from the list */
4010 prev->next = frag->next;
4012 nursery_fragments = frag->next;
4013 nursery_next = frag->fragment_start;
4014 nursery_frag_real_end = frag->fragment_end;
4016 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));
4017 frag->next = fragment_freelist;
4018 fragment_freelist = frag;
4021 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
4022 * an object of size @size
4023 * Return FALSE if not found (which means we need a collection)
4026 search_fragment_for_size (size_t size)
4028 Fragment *frag, *prev;
4029 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
4031 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4032 /* Clear the remaining space, pinning depends on this */
4033 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
4036 for (frag = nursery_fragments; frag; frag = frag->next) {
4037 if (size <= (frag->fragment_end - frag->fragment_start)) {
4038 setup_fragment (frag, prev, size);
4047 * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
4048 * This improves nursery usage.
4051 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
4053 Fragment *frag, *prev, *min_prev;
4054 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));
4056 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4057 /* Clear the remaining space, pinning depends on this */
4058 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
4060 min_prev = GINT_TO_POINTER (-1);
4063 for (frag = nursery_fragments; frag; frag = frag->next) {
4064 int frag_size = frag->fragment_end - frag->fragment_start;
4065 if (desired_size <= frag_size) {
4066 setup_fragment (frag, prev, desired_size);
4067 return desired_size;
4069 if (minimum_size <= frag_size)
4075 if (min_prev != GINT_TO_POINTER (-1)) {
4078 frag = min_prev->next;
4080 frag = nursery_fragments;
4082 frag_size = frag->fragment_end - frag->fragment_start;
4083 HEAVY_STAT (++stat_wasted_fragments_used);
4084 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
4086 setup_fragment (frag, min_prev, minimum_size);
4094 alloc_degraded (MonoVTable *vtable, size_t size)
4096 if (need_major_collection ()) {
4098 major_collection ("degraded overflow");
4102 return major_alloc_degraded (vtable, size);
4106 * Provide a variant that takes just the vtable for small fixed-size objects.
4107 * The aligned size is already computed and stored in vt->gc_descr.
4108 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
4109 * processing. We can keep track of where objects start, for example,
4110 * so when we scan the thread stacks for pinned objects, we can start
4111 * a search for the pinned object in SCAN_START_SIZE chunks.
4114 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4116 /* FIXME: handle OOM */
4121 HEAVY_STAT (++stat_objects_alloced);
4122 if (size <= MAX_SMALL_OBJ_SIZE)
4123 HEAVY_STAT (stat_bytes_alloced += size);
4125 HEAVY_STAT (stat_bytes_alloced_los += size);
4127 size += ALLOC_ALIGN - 1;
4128 size &= ~(ALLOC_ALIGN - 1);
4130 g_assert (vtable->gc_descr);
4132 if (G_UNLIKELY (collect_before_allocs)) {
4133 if (nursery_section) {
4135 collect_nursery (0);
4137 if (!degraded_mode && !search_fragment_for_size (size)) {
4139 g_assert_not_reached ();
4145 * We must already have the lock here instead of after the
4146 * fast path because we might be interrupted in the fast path
4147 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
4148 * and we'll end up allocating an object in a fragment which
4149 * no longer belongs to us.
4151 * The managed allocator does not do this, but it's treated
4152 * specially by the world-stopping code.
4155 if (size > MAX_SMALL_OBJ_SIZE) {
4156 p = alloc_large_inner (vtable, size);
4158 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4160 p = (void**)TLAB_NEXT;
4161 /* FIXME: handle overflow */
4162 new_next = (char*)p + size;
4163 TLAB_NEXT = new_next;
4165 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4169 * FIXME: We might need a memory barrier here so the change to tlab_next is
4170 * visible before the vtable store.
4173 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4174 binary_protocol_alloc (p , vtable, size);
4175 g_assert (*p == NULL);
4178 g_assert (TLAB_NEXT == new_next);
4185 /* there are two cases: the object is too big or we run out of space in the TLAB */
4186 /* we also reach here when the thread does its first allocation after a minor
4187 * collection, since the tlab_ variables are initialized to NULL.
4188 * there can be another case (from ORP), if we cooperate with the runtime a bit:
4189 * objects that need finalizers can have the high bit set in their size
4190 * so the above check fails and we can readily add the object to the queue.
4191 * This avoids taking again the GC lock when registering, but this is moot when
4192 * doing thread-local allocation, so it may not be a good idea.
4194 g_assert (TLAB_NEXT == new_next);
4195 if (TLAB_NEXT >= TLAB_REAL_END) {
4197 * Run out of space in the TLAB. When this happens, some amount of space
4198 * remains in the TLAB, but not enough to satisfy the current allocation
4199 * request. Currently, we retire the TLAB in all cases, later we could
4200 * keep it if the remaining space is above a treshold, and satisfy the
4201 * allocation directly from the nursery.
4204 /* when running in degraded mode, we continue allocing that way
4205 * for a while, to decrease the number of useless nursery collections.
4207 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
4208 p = alloc_degraded (vtable, size);
4209 binary_protocol_alloc_degraded (p, vtable, size);
4213 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
4214 if (size > tlab_size) {
4215 /* Allocate directly from the nursery */
4216 if (nursery_next + size >= nursery_frag_real_end) {
4217 if (!search_fragment_for_size (size)) {
4218 minor_collect_or_expand_inner (size);
4219 if (degraded_mode) {
4220 p = alloc_degraded (vtable, size);
4221 binary_protocol_alloc_degraded (p, vtable, size);
4227 p = (void*)nursery_next;
4228 nursery_next += size;
4229 if (nursery_next > nursery_frag_real_end) {
4234 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4235 memset (p, 0, size);
4237 int alloc_size = tlab_size;
4238 int available_in_nursery = nursery_frag_real_end - nursery_next;
4240 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
4242 if (alloc_size >= available_in_nursery) {
4243 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
4244 alloc_size = available_in_nursery;
4246 alloc_size = search_fragment_for_size_range (tlab_size, size);
4248 alloc_size = tlab_size;
4249 minor_collect_or_expand_inner (tlab_size);
4250 if (degraded_mode) {
4251 p = alloc_degraded (vtable, size);
4252 binary_protocol_alloc_degraded (p, vtable, size);
4259 /* Allocate a new TLAB from the current nursery fragment */
4260 TLAB_START = nursery_next;
4261 nursery_next += alloc_size;
4262 TLAB_NEXT = TLAB_START;
4263 TLAB_REAL_END = TLAB_START + alloc_size;
4264 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
4266 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4267 memset (TLAB_START, 0, alloc_size);
4269 /* Allocate from the TLAB */
4270 p = (void*)TLAB_NEXT;
4272 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4274 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4277 /* Reached tlab_temp_end */
4279 /* record the scan start so we can find pinned objects more easily */
4280 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4281 /* we just bump tlab_temp_end as well */
4282 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4283 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4287 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4288 binary_protocol_alloc (p, vtable, size);
4295 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4301 size += ALLOC_ALIGN - 1;
4302 size &= ~(ALLOC_ALIGN - 1);
4304 g_assert (vtable->gc_descr);
4305 if (size <= MAX_SMALL_OBJ_SIZE) {
4306 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4308 p = (void**)TLAB_NEXT;
4309 /* FIXME: handle overflow */
4310 new_next = (char*)p + size;
4311 TLAB_NEXT = new_next;
4313 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4317 * FIXME: We might need a memory barrier here so the change to tlab_next is
4318 * visible before the vtable store.
4321 HEAVY_STAT (++stat_objects_alloced);
4322 HEAVY_STAT (stat_bytes_alloced += size);
4324 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4325 binary_protocol_alloc (p, vtable, size);
4326 g_assert (*p == NULL);
4329 g_assert (TLAB_NEXT == new_next);
4338 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4341 #ifndef DISABLE_CRITICAL_REGION
4343 ENTER_CRITICAL_REGION;
4344 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4346 EXIT_CRITICAL_REGION;
4349 EXIT_CRITICAL_REGION;
4352 res = mono_gc_alloc_obj_nolock (vtable, size);
4358 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4361 #ifndef DISABLE_CRITICAL_REGION
4363 ENTER_CRITICAL_REGION;
4364 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4366 arr->max_length = max_length;
4367 EXIT_CRITICAL_REGION;
4370 EXIT_CRITICAL_REGION;
4375 arr = mono_gc_alloc_obj_nolock (vtable, size);
4376 arr->max_length = max_length;
4384 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4387 MonoArrayBounds *bounds;
4391 arr = mono_gc_alloc_obj_nolock (vtable, size);
4392 arr->max_length = max_length;
4394 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4395 arr->bounds = bounds;
4403 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4406 #ifndef DISABLE_CRITICAL_REGION
4408 ENTER_CRITICAL_REGION;
4409 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4412 EXIT_CRITICAL_REGION;
4415 EXIT_CRITICAL_REGION;
4420 str = mono_gc_alloc_obj_nolock (vtable, size);
4429 * To be used for interned strings and possibly MonoThread, reflection handles.
4430 * We may want to explicitly free these objects.
4433 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4435 /* FIXME: handle OOM */
4437 size += ALLOC_ALIGN - 1;
4438 size &= ~(ALLOC_ALIGN - 1);
4440 if (size > MAX_SMALL_OBJ_SIZE) {
4441 /* large objects are always pinned anyway */
4442 p = alloc_large_inner (vtable, size);
4444 DEBUG (9, g_assert (vtable->klass->inited));
4445 p = major_alloc_small_pinned_obj (size, vtable->klass->has_references);
4447 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4448 binary_protocol_alloc_pinned (p, vtable, size);
4455 * ######################################################################
4456 * ######## Finalization support
4457 * ######################################################################
4461 * this is valid for the nursery: if the object has been forwarded it means it's
4462 * still refrenced from a root. If it is pinned it's still alive as well.
4463 * Return TRUE if @obj is ready to be finalized.
4465 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4468 is_critical_finalizer (FinalizeEntry *entry)
4473 if (!mono_defaults.critical_finalizer_object)
4476 obj = entry->object;
4477 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4479 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4483 queue_finalization_entry (FinalizeEntry *entry) {
4484 if (is_critical_finalizer (entry)) {
4485 entry->next = critical_fin_list;
4486 critical_fin_list = entry;
4488 entry->next = fin_ready_list;
4489 fin_ready_list = entry;
4493 /* LOCKING: requires that the GC lock is held */
4495 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4497 FinalizeEntry **finalizable_hash = hash_table->table;
4498 mword finalizable_hash_size = hash_table->size;
4501 FinalizeEntry **new_hash;
4502 FinalizeEntry *entry, *next;
4503 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4505 new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4506 for (i = 0; i < finalizable_hash_size; ++i) {
4507 for (entry = finalizable_hash [i]; entry; entry = next) {
4508 hash = mono_object_hash (entry->object) % new_size;
4510 entry->next = new_hash [hash];
4511 new_hash [hash] = entry;
4514 free_internal_mem (finalizable_hash, INTERNAL_MEM_FIN_TABLE);
4515 hash_table->table = new_hash;
4516 hash_table->size = new_size;
4519 /* LOCKING: requires that the GC lock is held */
4521 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4523 if (hash_table->num_registered >= hash_table->size * 2)
4524 rehash_fin_table (hash_table);
4527 /* LOCKING: requires that the GC lock is held */
4529 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4531 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4532 FinalizeEntry *entry, *prev;
4534 FinalizeEntry **finalizable_hash = hash_table->table;
4535 mword finalizable_hash_size = hash_table->size;
4539 for (i = 0; i < finalizable_hash_size; ++i) {
4541 for (entry = finalizable_hash [i]; entry;) {
4542 if ((char*)entry->object >= start && (char*)entry->object < end && !major_is_object_live (entry->object)) {
4543 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4544 char *copy = entry->object;
4545 copy_func ((void**)©);
4548 FinalizeEntry *next;
4549 /* remove and put in fin_ready_list */
4551 prev->next = entry->next;
4553 finalizable_hash [i] = entry->next;
4555 num_ready_finalizers++;
4556 hash_table->num_registered--;
4557 queue_finalization_entry (entry);
4558 /* Make it survive */
4559 from = entry->object;
4560 entry->object = copy;
4561 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));
4565 char *from = entry->object;
4566 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4567 FinalizeEntry *next = entry->next;
4568 unsigned int major_hash;
4569 /* remove from the list */
4571 prev->next = entry->next;
4573 finalizable_hash [i] = entry->next;
4574 hash_table->num_registered--;
4576 entry->object = copy;
4578 /* insert it into the major hash */
4579 rehash_fin_table_if_necessary (&major_finalizable_hash);
4580 major_hash = mono_object_hash ((MonoObject*) copy) %
4581 major_finalizable_hash.size;
4582 entry->next = major_finalizable_hash.table [major_hash];
4583 major_finalizable_hash.table [major_hash] = entry;
4584 major_finalizable_hash.num_registered++;
4586 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4591 /* update pointer */
4592 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4593 entry->object = copy;
4598 entry = entry->next;
4604 object_is_reachable (char *object, char *start, char *end)
4606 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4607 if (object < start || object >= end)
4609 return !object_is_fin_ready (object) || major_is_object_live (object);
4612 /* LOCKING: requires that the GC lock is held */
4614 null_ephemerons_for_domain (MonoDomain *domain)
4616 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4619 MonoObject *object = (MonoObject*)current->array;
4621 if (object && !object->vtable) {
4622 EphemeronLinkNode *tmp = current;
4625 prev->next = current->next;
4627 ephemeron_list = current->next;
4629 current = current->next;
4630 free_internal_mem (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4633 current = current->next;
4638 /* LOCKING: requires that the GC lock is held */
4640 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end)
4642 int was_in_nursery, was_promoted;
4643 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4645 Ephemeron *cur, *array_end;
4649 char *object = current->array;
4651 if (!object_is_reachable (object, start, end)) {
4652 EphemeronLinkNode *tmp = current;
4654 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4657 prev->next = current->next;
4659 ephemeron_list = current->next;
4661 current = current->next;
4662 free_internal_mem (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4667 was_in_nursery = ptr_in_nursery (object);
4668 copy_func ((void**)&object);
4669 current->array = object;
4671 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4672 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4674 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4676 array = (MonoArray*)object;
4677 cur = mono_array_addr (array, Ephemeron, 0);
4678 array_end = cur + mono_array_length_fast (array);
4679 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4681 for (; cur < array_end; ++cur) {
4682 char *key = (char*)cur->key;
4684 if (!key || key == tombstone)
4687 DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4688 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4689 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4691 if (!object_is_reachable (key, start, end)) {
4692 cur->key = tombstone;
4698 if (ptr_in_nursery (key)) {/*key was not promoted*/
4699 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4700 add_to_global_remset (&cur->key);
4702 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4703 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4704 add_to_global_remset (&cur->value);
4709 current = current->next;
4713 /* LOCKING: requires that the GC lock is held */
4715 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end)
4717 int nothing_marked = 1;
4718 EphemeronLinkNode *current = ephemeron_list;
4720 Ephemeron *cur, *array_end;
4723 for (current = ephemeron_list; current; current = current->next) {
4724 char *object = current->array;
4725 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4727 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4728 if (object < start || object >= end)
4731 /*It has to be alive*/
4732 if (!object_is_reachable (object, start, end)) {
4733 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4737 copy_func ((void**)&object);
4739 array = (MonoArray*)object;
4740 cur = mono_array_addr (array, Ephemeron, 0);
4741 array_end = cur + mono_array_length_fast (array);
4742 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4744 for (; cur < array_end; ++cur) {
4745 char *key = cur->key;
4747 if (!key || key == tombstone)
4750 DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4751 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4752 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4754 if (object_is_reachable (key, start, end)) {
4755 char *value = cur->value;
4757 copy_func ((void**)&cur->key);
4759 if (!object_is_reachable (value, start, end))
4761 copy_func ((void**)&cur->value);
4767 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4768 return nothing_marked;
4771 /* LOCKING: requires that the GC lock is held */
4773 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4775 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4776 DisappearingLink **disappearing_link_hash = hash->table;
4777 int disappearing_link_hash_size = hash->size;
4778 DisappearingLink *entry, *prev;
4780 if (!hash->num_links)
4782 for (i = 0; i < disappearing_link_hash_size; ++i) {
4784 for (entry = disappearing_link_hash [i]; entry;) {
4785 char *object = DISLINK_OBJECT (entry);
4786 if (object >= start && object < end && !major_is_object_live (object)) {
4787 gboolean track = DISLINK_TRACK (entry);
4788 if (!track && object_is_fin_ready (object)) {
4789 void **p = entry->link;
4790 DisappearingLink *old;
4792 /* remove from list */
4794 prev->next = entry->next;
4796 disappearing_link_hash [i] = entry->next;
4797 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4799 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4804 char *copy = object;
4805 copy_func ((void**)©);
4807 /* Update pointer if it's moved. If the object
4808 * has been moved out of the nursery, we need to
4809 * remove the link from the minor hash table to
4812 * FIXME: what if an object is moved earlier?
4815 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4816 void **link = entry->link;
4817 DisappearingLink *old;
4818 /* remove from list */
4820 prev->next = entry->next;
4822 disappearing_link_hash [i] = entry->next;
4824 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4828 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4829 track, GENERATION_OLD);
4831 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4835 /* We set the track resurrection bit to
4836 * FALSE if the object is to be finalized
4837 * so that the object can be collected in
4838 * the next cycle (i.e. after it was
4841 *entry->link = HIDE_POINTER (copy,
4842 object_is_fin_ready (object) ? FALSE : track);
4843 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4848 entry = entry->next;
4853 /* LOCKING: requires that the GC lock is held */
4855 null_links_for_domain (MonoDomain *domain, int generation)
4857 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4858 DisappearingLink **disappearing_link_hash = hash->table;
4859 int disappearing_link_hash_size = hash->size;
4860 DisappearingLink *entry, *prev;
4862 for (i = 0; i < disappearing_link_hash_size; ++i) {
4864 for (entry = disappearing_link_hash [i]; entry; ) {
4865 char *object = DISLINK_OBJECT (entry);
4866 if (object && !((MonoObject*)object)->vtable) {
4867 DisappearingLink *next = entry->next;
4872 disappearing_link_hash [i] = next;
4874 if (*(entry->link)) {
4875 *(entry->link) = NULL;
4876 g_warning ("Disappearing link %p not freed", entry->link);
4878 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4885 entry = entry->next;
4890 /* LOCKING: requires that the GC lock is held */
4892 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4893 FinalizeEntryHashTable *hash_table)
4895 FinalizeEntry **finalizable_hash = hash_table->table;
4896 mword finalizable_hash_size = hash_table->size;
4897 FinalizeEntry *entry, *prev;
4900 if (no_finalize || !out_size || !out_array)
4903 for (i = 0; i < finalizable_hash_size; ++i) {
4905 for (entry = finalizable_hash [i]; entry;) {
4906 if (mono_object_domain (entry->object) == domain) {
4907 FinalizeEntry *next;
4908 /* remove and put in out_array */
4910 prev->next = entry->next;
4912 finalizable_hash [i] = entry->next;
4914 hash_table->num_registered--;
4915 out_array [count ++] = entry->object;
4916 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));
4918 if (count == out_size)
4923 entry = entry->next;
4930 * mono_gc_finalizers_for_domain:
4931 * @domain: the unloading appdomain
4932 * @out_array: output array
4933 * @out_size: size of output array
4935 * Store inside @out_array up to @out_size objects that belong to the unloading
4936 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4937 * until it returns 0.
4938 * The items are removed from the finalizer data structure, so the caller is supposed
4940 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4943 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4948 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4949 if (result < out_size) {
4950 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4951 &major_finalizable_hash);
4959 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4961 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4962 FinalizeEntry **finalizable_hash;
4963 mword finalizable_hash_size;
4964 FinalizeEntry *entry, *prev;
4968 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4969 hash = mono_object_hash (obj);
4971 rehash_fin_table_if_necessary (hash_table);
4972 finalizable_hash = hash_table->table;
4973 finalizable_hash_size = hash_table->size;
4974 hash %= finalizable_hash_size;
4976 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4977 if (entry->object == obj) {
4979 /* remove from the list */
4981 prev->next = entry->next;
4983 finalizable_hash [hash] = entry->next;
4984 hash_table->num_registered--;
4985 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));
4986 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4994 /* request to deregister, but already out of the list */
4998 entry = get_internal_mem (sizeof (FinalizeEntry), INTERNAL_MEM_FINALIZE_ENTRY);
4999 entry->object = obj;
5000 entry->next = finalizable_hash [hash];
5001 finalizable_hash [hash] = entry;
5002 hash_table->num_registered++;
5003 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)));
5008 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
5010 if (ptr_in_nursery (obj))
5011 register_for_finalization (obj, user_data, GENERATION_NURSERY);
5013 register_for_finalization (obj, user_data, GENERATION_OLD);
5017 rehash_dislink (DisappearingLinkHashTable *hash_table)
5019 DisappearingLink **disappearing_link_hash = hash_table->table;
5020 int disappearing_link_hash_size = hash_table->size;
5023 DisappearingLink **new_hash;
5024 DisappearingLink *entry, *next;
5025 int new_size = g_spaced_primes_closest (hash_table->num_links);
5027 new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
5028 for (i = 0; i < disappearing_link_hash_size; ++i) {
5029 for (entry = disappearing_link_hash [i]; entry; entry = next) {
5030 hash = mono_aligned_addr_hash (entry->link) % new_size;
5032 entry->next = new_hash [hash];
5033 new_hash [hash] = entry;
5036 free_internal_mem (disappearing_link_hash, INTERNAL_MEM_DISLINK_TABLE);
5037 hash_table->table = new_hash;
5038 hash_table->size = new_size;
5041 /* LOCKING: assumes the GC lock is held */
5043 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
5045 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
5046 DisappearingLink *entry, *prev;
5048 DisappearingLink **disappearing_link_hash = hash_table->table;
5049 int disappearing_link_hash_size = hash_table->size;
5051 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
5052 rehash_dislink (hash_table);
5053 disappearing_link_hash = hash_table->table;
5054 disappearing_link_hash_size = hash_table->size;
5056 /* FIXME: add check that link is not in the heap */
5057 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
5058 entry = disappearing_link_hash [hash];
5060 for (; entry; entry = entry->next) {
5061 /* link already added */
5062 if (link == entry->link) {
5063 /* NULL obj means remove */
5066 prev->next = entry->next;
5068 disappearing_link_hash [hash] = entry->next;
5069 hash_table->num_links--;
5070 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
5071 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5074 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
5082 entry = get_internal_mem (sizeof (DisappearingLink), INTERNAL_MEM_DISLINK);
5083 *link = HIDE_POINTER (obj, track);
5085 entry->next = disappearing_link_hash [hash];
5086 disappearing_link_hash [hash] = entry;
5087 hash_table->num_links++;
5088 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)));
5091 /* LOCKING: assumes the GC lock is held */
5093 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
5095 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
5096 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
5098 if (ptr_in_nursery (obj))
5099 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
5101 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
5106 mono_gc_invoke_finalizers (void)
5108 FinalizeEntry *entry = NULL;
5109 gboolean entry_is_critical = FALSE;
5112 /* FIXME: batch to reduce lock contention */
5113 while (fin_ready_list || critical_fin_list) {
5117 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
5119 /* We have finalized entry in the last
5120 interation, now we need to remove it from
5123 *list = entry->next;
5125 FinalizeEntry *e = *list;
5126 while (e->next != entry)
5128 e->next = entry->next;
5130 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
5134 /* Now look for the first non-null entry. */
5135 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
5138 entry_is_critical = FALSE;
5140 entry_is_critical = TRUE;
5141 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
5146 g_assert (entry->object);
5147 num_ready_finalizers--;
5148 obj = entry->object;
5149 entry->object = NULL;
5150 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
5158 g_assert (entry->object == NULL);
5160 /* the object is on the stack so it is pinned */
5161 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
5162 mono_gc_run_finalize (obj, NULL);
5169 mono_gc_pending_finalizers (void)
5171 return fin_ready_list || critical_fin_list;
5174 /* Negative value to remove */
5176 mono_gc_add_memory_pressure (gint64 value)
5178 /* FIXME: Use interlocked functions */
5180 memory_pressure += value;
5185 * ######################################################################
5186 * ######## registered roots support
5187 * ######################################################################
5191 rehash_roots (gboolean pinned)
5195 RootRecord **new_hash;
5196 RootRecord *entry, *next;
5199 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
5200 new_hash = get_internal_mem (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5201 for (i = 0; i < roots_hash_size [pinned]; ++i) {
5202 for (entry = roots_hash [pinned][i]; entry; entry = next) {
5203 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5205 entry->next = new_hash [hash];
5206 new_hash [hash] = entry;
5209 free_internal_mem (roots_hash [pinned], INTERNAL_MEM_ROOTS_TABLE);
5210 roots_hash [pinned] = new_hash;
5211 roots_hash_size [pinned] = new_size;
5215 find_root (int root_type, char *start, guint32 addr_hash)
5217 RootRecord *new_root;
5219 guint32 hash = addr_hash % roots_hash_size [root_type];
5220 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5221 /* we allow changing the size and the descriptor (for thread statics etc) */
5222 if (new_root->start_root == start) {
5231 * We do not coalesce roots.
5234 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5236 RootRecord *new_root;
5237 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5240 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5241 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5244 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5245 new_root = find_root (i, start, addr_hash);
5246 /* we allow changing the size and the descriptor (for thread statics etc) */
5248 size_t old_size = new_root->end_root - new_root->start_root;
5249 new_root->end_root = new_root->start_root + size;
5250 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5251 ((new_root->root_desc == 0) && (descr == NULL)));
5252 new_root->root_desc = (mword)descr;
5254 roots_size -= old_size;
5259 new_root = get_internal_mem (sizeof (RootRecord), INTERNAL_MEM_ROOT_RECORD);
5261 new_root->start_root = start;
5262 new_root->end_root = new_root->start_root + size;
5263 new_root->root_desc = (mword)descr;
5265 hash = addr_hash % roots_hash_size [root_type];
5266 num_roots_entries [root_type]++;
5267 new_root->next = roots_hash [root_type] [hash];
5268 roots_hash [root_type][hash] = new_root;
5269 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));
5279 mono_gc_register_root (char *start, size_t size, void *descr)
5281 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5285 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5287 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5291 mono_gc_deregister_root (char* addr)
5293 RootRecord *tmp, *prev;
5294 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5298 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5299 hash = addr_hash % roots_hash_size [root_type];
5300 tmp = roots_hash [root_type][hash];
5303 if (tmp->start_root == (char*)addr) {
5305 prev->next = tmp->next;
5307 roots_hash [root_type][hash] = tmp->next;
5308 roots_size -= (tmp->end_root - tmp->start_root);
5309 num_roots_entries [root_type]--;
5310 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5311 free_internal_mem (tmp, INTERNAL_MEM_ROOT_RECORD);
5322 * ######################################################################
5323 * ######## Thread handling (stop/start code)
5324 * ######################################################################
5327 /* FIXME: handle large/small config */
5328 #define THREAD_HASH_SIZE 11
5329 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5331 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5333 #if USE_SIGNAL_BASED_START_STOP_WORLD
5335 static MonoSemType suspend_ack_semaphore;
5336 static MonoSemType *suspend_ack_semaphore_ptr;
5337 static unsigned int global_stop_count = 0;
5339 static int suspend_signal_num = SIGXFSZ;
5341 static int suspend_signal_num = SIGPWR;
5343 static int restart_signal_num = SIGXCPU;
5344 static sigset_t suspend_signal_mask;
5345 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5347 /* LOCKING: assumes the GC lock is held */
5348 static SgenThreadInfo*
5349 thread_info_lookup (ARCH_THREAD_TYPE id)
5351 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5352 SgenThreadInfo *info;
5354 info = thread_table [hash];
5355 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5362 update_current_thread_stack (void *start)
5364 void *ptr = cur_thread_regs;
5365 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5367 info->stack_start = align_pointer (&ptr);
5368 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5369 ARCH_STORE_REGS (ptr);
5370 info->stopped_regs = ptr;
5371 if (gc_callbacks.thread_suspend_func)
5372 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5376 signal_desc (int signum)
5378 if (signum == suspend_signal_num)
5380 if (signum == restart_signal_num)
5386 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5387 * have cross-domain checks in the write barrier.
5389 //#define XDOMAIN_CHECKS_IN_WBARRIER
5391 #ifndef BINARY_PROTOCOL
5392 #ifndef HEAVY_STATISTICS
5393 #define MANAGED_ALLOCATION
5394 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5395 #define MANAGED_WBARRIER
5401 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5404 wait_for_suspend_ack (int count)
5408 for (i = 0; i < count; ++i) {
5409 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5410 if (errno != EINTR) {
5411 g_error ("sem_wait ()");
5417 /* LOCKING: assumes the GC lock is held */
5419 thread_handshake (int signum)
5421 int count, i, result;
5422 SgenThreadInfo *info;
5423 pthread_t me = pthread_self ();
5426 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5427 for (info = thread_table [i]; info; info = info->next) {
5428 DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
5429 if (ARCH_THREAD_EQUALS (info->id, me)) {
5430 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
5433 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
5435 result = pthread_kill (info->id, signum);
5437 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
5440 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
5446 wait_for_suspend_ack (count);
5452 restart_threads_until_none_in_managed_allocator (void)
5454 SgenThreadInfo *info;
5455 int i, result, num_threads_died = 0;
5456 int sleep_duration = -1;
5459 int restart_count = 0, restarted_count = 0;
5460 /* restart all threads that stopped in the
5462 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5463 for (info = thread_table [i]; info; info = info->next) {
5466 if (!info->stack_start || info->in_critical_region ||
5467 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5468 binary_protocol_thread_restart ((gpointer)info->id);
5469 result = pthread_kill (info->id, restart_signal_num);
5476 /* we set the stopped_ip to
5477 NULL for threads which
5478 we're not restarting so
5479 that we can easily identify
5481 info->stopped_ip = NULL;
5482 info->stopped_domain = NULL;
5486 /* if no threads were restarted, we're done */
5487 if (restart_count == 0)
5490 /* wait for the threads to signal their restart */
5491 wait_for_suspend_ack (restart_count);
5493 if (sleep_duration < 0) {
5497 g_usleep (sleep_duration);
5498 sleep_duration += 10;
5501 /* stop them again */
5502 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5503 for (info = thread_table [i]; info; info = info->next) {
5504 if (info->skip || info->stopped_ip == NULL)
5506 result = pthread_kill (info->id, suspend_signal_num);
5514 /* some threads might have died */
5515 num_threads_died += restart_count - restarted_count;
5516 /* wait for the threads to signal their suspension
5518 wait_for_suspend_ack (restart_count);
5521 return num_threads_died;
5524 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5526 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5528 SgenThreadInfo *info;
5531 int old_errno = errno;
5532 gpointer regs [ARCH_NUM_REGS];
5533 gpointer stack_start;
5535 id = pthread_self ();
5536 info = thread_info_lookup (id);
5537 info->stopped_domain = mono_domain_get ();
5538 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5539 stop_count = global_stop_count;
5540 /* duplicate signal */
5541 if (0 && info->stop_count == stop_count) {
5545 #ifdef HAVE_KW_THREAD
5546 /* update the remset info in the thread data structure */
5547 info->remset = remembered_set;
5549 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5550 /* If stack_start is not within the limits, then don't set it
5551 in info and we will be restarted. */
5552 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5553 info->stack_start = stack_start;
5555 ARCH_COPY_SIGCTX_REGS (regs, context);
5556 info->stopped_regs = regs;
5558 g_assert (!info->stack_start);
5561 /* Notify the JIT */
5562 if (gc_callbacks.thread_suspend_func)
5563 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5565 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5566 /* notify the waiting thread */
5567 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5568 info->stop_count = stop_count;
5570 /* wait until we receive the restart signal */
5573 sigsuspend (&suspend_signal_mask);
5574 } while (info->signal != restart_signal_num);
5576 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5577 /* notify the waiting thread */
5578 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5584 restart_handler (int sig)
5586 SgenThreadInfo *info;
5587 int old_errno = errno;
5589 info = thread_info_lookup (pthread_self ());
5590 info->signal = restart_signal_num;
5591 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5597 acquire_gc_locks (void)
5603 release_gc_locks (void)
5605 UNLOCK_INTERRUPTION;
5608 static TV_DECLARE (stop_world_time);
5609 static unsigned long max_pause_usec = 0;
5611 /* LOCKING: assumes the GC lock is held */
5617 acquire_gc_locks ();
5619 update_current_thread_stack (&count);
5621 global_stop_count++;
5622 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 ()));
5623 TV_GETTIME (stop_world_time);
5624 count = thread_handshake (suspend_signal_num);
5625 count -= restart_threads_until_none_in_managed_allocator ();
5626 g_assert (count >= 0);
5627 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5631 /* LOCKING: assumes the GC lock is held */
5633 restart_world (void)
5636 SgenThreadInfo *info;
5637 TV_DECLARE (end_sw);
5640 /* notify the profiler of the leftovers */
5641 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5642 if (moved_objects_idx) {
5643 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5644 moved_objects_idx = 0;
5647 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5648 for (info = thread_table [i]; info; info = info->next) {
5649 info->stack_start = NULL;
5650 info->stopped_regs = NULL;
5654 release_gc_locks ();
5656 count = thread_handshake (restart_signal_num);
5657 TV_GETTIME (end_sw);
5658 usec = TV_ELAPSED (stop_world_time, end_sw);
5659 max_pause_usec = MAX (usec, max_pause_usec);
5660 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5664 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5667 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5669 gc_callbacks = *callbacks;
5672 /* Variables holding start/end nursery so it won't have to be passed at every call */
5673 static void *scan_area_arg_start, *scan_area_arg_end;
5676 mono_gc_conservatively_scan_area (void *start, void *end)
5678 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5682 mono_gc_scan_object (void *obj)
5684 if (current_collection_generation == GENERATION_NURSERY)
5687 major_copy_or_mark_object (&obj);
5692 * Mark from thread stacks and registers.
5695 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5698 SgenThreadInfo *info;
5700 scan_area_arg_start = start_nursery;
5701 scan_area_arg_end = end_nursery;
5703 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5704 for (info = thread_table [i]; info; info = info->next) {
5706 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));
5709 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));
5710 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
5711 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5713 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5716 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5717 start_nursery, end_nursery, PIN_TYPE_STACK);
5723 find_pinning_ref_from_thread (char *obj, size_t size)
5726 SgenThreadInfo *info;
5727 char *endobj = obj + size;
5729 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5730 for (info = thread_table [i]; info; info = info->next) {
5731 char **start = (char**)info->stack_start;
5734 while (start < (char**)info->stack_end) {
5735 if (*start >= obj && *start < endobj) {
5736 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));
5741 /* FIXME: check info->stopped_regs */
5747 ptr_on_stack (void *ptr)
5749 gpointer stack_start = &stack_start;
5750 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5752 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5758 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
5765 HEAVY_STAT (++stat_global_remsets_processed);
5767 /* FIXME: exclude stack locations */
5768 switch ((*p) & REMSET_TYPE_MASK) {
5769 case REMSET_LOCATION:
5771 //__builtin_prefetch (ptr);
5772 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5773 gpointer old = *ptr;
5775 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5777 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5778 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5780 * If the object is pinned, each reference to it from nonpinned objects
5781 * becomes part of the global remset, which can grow very large.
5783 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5784 add_to_global_remset (ptr);
5787 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5791 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5792 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5795 while (count-- > 0) {
5797 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5798 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5799 add_to_global_remset (ptr);
5804 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5805 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5807 scan_object ((char*)ptr);
5809 case REMSET_VTYPE: {
5810 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5811 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5816 ptr = (void**) scan_vtype ((char*)ptr, desc, start_nursery, end_nursery);
5820 g_assert_not_reached ();
5825 #ifdef HEAVY_STATISTICS
5827 collect_store_remsets (RememberedSet *remset, mword *bumper)
5829 mword *p = remset->data;
5834 while (p < remset->store_next) {
5835 switch ((*p) & REMSET_TYPE_MASK) {
5836 case REMSET_LOCATION:
5839 ++stat_saved_remsets_1;
5841 if (*p == last1 || *p == last2) {
5842 ++stat_saved_remsets_2;
5859 g_assert_not_reached ();
5869 RememberedSet *remset;
5871 SgenThreadInfo *info;
5873 mword *addresses, *bumper, *p, *r;
5875 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5876 for (info = thread_table [i]; info; info = info->next) {
5877 for (remset = info->remset; remset; remset = remset->next)
5878 size += remset->store_next - remset->data;
5881 for (remset = freed_thread_remsets; remset; remset = remset->next)
5882 size += remset->store_next - remset->data;
5883 for (remset = global_remset; remset; remset = remset->next)
5884 size += remset->store_next - remset->data;
5886 bumper = addresses = get_internal_mem (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5888 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5889 for (info = thread_table [i]; info; info = info->next) {
5890 for (remset = info->remset; remset; remset = remset->next)
5891 bumper = collect_store_remsets (remset, bumper);
5894 for (remset = global_remset; remset; remset = remset->next)
5895 bumper = collect_store_remsets (remset, bumper);
5896 for (remset = freed_thread_remsets; remset; remset = remset->next)
5897 bumper = collect_store_remsets (remset, bumper);
5899 g_assert (bumper <= addresses + size);
5901 stat_store_remsets += bumper - addresses;
5903 sort_addresses ((void**)addresses, bumper - addresses);
5906 while (r < bumper) {
5912 stat_store_remsets_unique += p - addresses;
5914 free_internal_mem (addresses, INTERNAL_MEM_STATISTICS);
5919 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5921 *info->store_remset_buffer_index_addr = 0;
5922 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5926 scan_from_remsets (void *start_nursery, void *end_nursery)
5929 SgenThreadInfo *info;
5930 RememberedSet *remset;
5931 GenericStoreRememberedSet *store_remset;
5932 mword *p, *next_p, *store_pos;
5934 #ifdef HEAVY_STATISTICS
5938 /* the global one */
5939 for (remset = global_remset; remset; remset = remset->next) {
5940 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));
5941 store_pos = remset->data;
5942 for (p = remset->data; p < remset->store_next; p = next_p) {
5945 /*Ignore previously processed remset.*/
5946 if (!global_remset_location_was_not_added (ptr)) {
5951 next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
5954 * Clear global remsets of locations which no longer point to the
5955 * nursery. Otherwise, they could grow indefinitely between major
5958 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5960 if (ptr_in_nursery (*ptr)) {
5961 *store_pos ++ = p [0];
5962 HEAVY_STAT (++stat_global_remsets_readded);
5966 /* Truncate the remset */
5967 remset->store_next = store_pos;
5970 /* the generic store ones */
5971 store_remset = generic_store_remsets;
5972 while (store_remset) {
5973 GenericStoreRememberedSet *next = store_remset->next;
5975 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5976 gpointer addr = store_remset->data [i];
5978 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE);
5981 free_internal_mem (store_remset, INTERNAL_MEM_STORE_REMSET);
5983 store_remset = next;
5985 generic_store_remsets = NULL;
5987 /* the per-thread ones */
5988 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5989 for (info = thread_table [i]; info; info = info->next) {
5990 RememberedSet *next;
5992 for (remset = info->remset; remset; remset = next) {
5993 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));
5994 for (p = remset->data; p < remset->store_next;) {
5995 p = handle_remset (p, start_nursery, end_nursery, FALSE);
5997 remset->store_next = remset->data;
5998 next = remset->next;
5999 remset->next = NULL;
6000 if (remset != info->remset) {
6001 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6002 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6005 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
6006 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE);
6007 clear_thread_store_remset_buffer (info);
6011 /* the freed thread ones */
6012 while (freed_thread_remsets) {
6013 RememberedSet *next;
6014 remset = freed_thread_remsets;
6015 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));
6016 for (p = remset->data; p < remset->store_next;) {
6017 p = handle_remset (p, start_nursery, end_nursery, FALSE);
6019 next = remset->next;
6020 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6021 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6022 freed_thread_remsets = next;
6027 * Clear the info in the remembered sets: we're doing a major collection, so
6028 * the per-thread ones are not needed and the global ones will be reconstructed
6032 clear_remsets (void)
6035 SgenThreadInfo *info;
6036 RememberedSet *remset, *next;
6038 /* the global list */
6039 for (remset = global_remset; remset; remset = next) {
6040 remset->store_next = remset->data;
6041 next = remset->next;
6042 remset->next = NULL;
6043 if (remset != global_remset) {
6044 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6045 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6048 /* the generic store ones */
6049 while (generic_store_remsets) {
6050 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
6051 free_internal_mem (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
6052 generic_store_remsets = gs_next;
6054 /* the per-thread ones */
6055 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6056 for (info = thread_table [i]; info; info = info->next) {
6057 for (remset = info->remset; remset; remset = next) {
6058 remset->store_next = remset->data;
6059 next = remset->next;
6060 remset->next = NULL;
6061 if (remset != info->remset) {
6062 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6063 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6066 clear_thread_store_remset_buffer (info);
6070 /* the freed thread ones */
6071 while (freed_thread_remsets) {
6072 next = freed_thread_remsets->next;
6073 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
6074 free_internal_mem (freed_thread_remsets, INTERNAL_MEM_REMSET);
6075 freed_thread_remsets = next;
6080 * Clear the thread local TLAB variables for all threads.
6085 SgenThreadInfo *info;
6088 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6089 for (info = thread_table [i]; info; info = info->next) {
6090 /* A new TLAB will be allocated when the thread does its first allocation */
6091 *info->tlab_start_addr = NULL;
6092 *info->tlab_next_addr = NULL;
6093 *info->tlab_temp_end_addr = NULL;
6094 *info->tlab_real_end_addr = NULL;
6099 /* LOCKING: assumes the GC lock is held */
6100 static SgenThreadInfo*
6101 gc_register_current_thread (void *addr)
6104 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
6105 #ifndef HAVE_KW_THREAD
6106 SgenThreadInfo *__thread_info__ = info;
6112 memset (info, 0, sizeof (SgenThreadInfo));
6113 #ifndef HAVE_KW_THREAD
6114 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
6116 g_assert (!pthread_getspecific (thread_info_key));
6117 pthread_setspecific (thread_info_key, info);
6122 info->id = ARCH_GET_THREAD ();
6123 info->stop_count = -1;
6126 info->stack_start = NULL;
6127 info->tlab_start_addr = &TLAB_START;
6128 info->tlab_next_addr = &TLAB_NEXT;
6129 info->tlab_temp_end_addr = &TLAB_TEMP_END;
6130 info->tlab_real_end_addr = &TLAB_REAL_END;
6131 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
6132 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
6133 info->stopped_ip = NULL;
6134 info->stopped_domain = NULL;
6135 info->stopped_regs = NULL;
6137 binary_protocol_thread_register ((gpointer)info->id);
6139 #ifdef HAVE_KW_THREAD
6140 tlab_next_addr = &tlab_next;
6141 store_remset_buffer_index_addr = &store_remset_buffer_index;
6144 /* try to get it with attributes first */
6145 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
6149 pthread_attr_t attr;
6150 pthread_getattr_np (pthread_self (), &attr);
6151 pthread_attr_getstack (&attr, &sstart, &size);
6152 info->stack_start_limit = sstart;
6153 info->stack_end = (char*)sstart + size;
6154 pthread_attr_destroy (&attr);
6156 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
6157 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
6158 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
6161 /* FIXME: we assume the stack grows down */
6162 gsize stack_bottom = (gsize)addr;
6163 stack_bottom += 4095;
6164 stack_bottom &= ~4095;
6165 info->stack_end = (char*)stack_bottom;
6169 #ifdef HAVE_KW_THREAD
6170 stack_end = info->stack_end;
6173 /* hash into the table */
6174 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
6175 info->next = thread_table [hash];
6176 thread_table [hash] = info;
6178 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
6179 pthread_setspecific (remembered_set_key, info->remset);
6180 #ifdef HAVE_KW_THREAD
6181 remembered_set = info->remset;
6184 STORE_REMSET_BUFFER = get_internal_mem (sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE, INTERNAL_MEM_STORE_REMSET);
6185 STORE_REMSET_BUFFER_INDEX = 0;
6187 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
6189 if (gc_callbacks.thread_attach_func)
6190 info->runtime_data = gc_callbacks.thread_attach_func ();
6196 add_generic_store_remset_from_buffer (gpointer *buffer)
6198 GenericStoreRememberedSet *remset = get_internal_mem (sizeof (GenericStoreRememberedSet), INTERNAL_MEM_STORE_REMSET);
6199 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6200 remset->next = generic_store_remsets;
6201 generic_store_remsets = remset;
6205 unregister_current_thread (void)
6208 SgenThreadInfo *prev = NULL;
6210 RememberedSet *rset;
6211 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6213 binary_protocol_thread_unregister ((gpointer)id);
6215 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6216 p = thread_table [hash];
6218 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6219 while (!ARCH_THREAD_EQUALS (p->id, id)) {
6224 thread_table [hash] = p->next;
6226 prev->next = p->next;
6229 if (freed_thread_remsets) {
6230 for (rset = p->remset; rset->next; rset = rset->next)
6232 rset->next = freed_thread_remsets;
6233 freed_thread_remsets = p->remset;
6235 freed_thread_remsets = p->remset;
6238 if (*p->store_remset_buffer_index_addr)
6239 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6240 free_internal_mem (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6245 unregister_thread (void *k)
6247 g_assert (!mono_domain_get ());
6249 unregister_current_thread ();
6254 mono_gc_register_thread (void *baseptr)
6256 SgenThreadInfo *info;
6260 info = thread_info_lookup (ARCH_GET_THREAD ());
6262 info = gc_register_current_thread (baseptr);
6264 return info != NULL;
6267 #if USE_PTHREAD_INTERCEPT
6269 #undef pthread_create
6271 #undef pthread_detach
6274 void *(*start_routine) (void *);
6277 MonoSemType registered;
6278 } SgenThreadStartInfo;
6281 gc_start_thread (void *arg)
6283 SgenThreadStartInfo *start_info = arg;
6284 SgenThreadInfo* info;
6285 void *t_arg = start_info->arg;
6286 void *(*start_func) (void*) = start_info->start_routine;
6291 info = gc_register_current_thread (&result);
6293 post_result = MONO_SEM_POST (&(start_info->registered));
6294 g_assert (!post_result);
6295 result = start_func (t_arg);
6296 g_assert (!mono_domain_get ());
6298 * this is done by the pthread key dtor
6300 unregister_current_thread ();
6308 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6310 SgenThreadStartInfo *start_info;
6313 start_info = malloc (sizeof (SgenThreadStartInfo));
6316 result = MONO_SEM_INIT (&(start_info->registered), 0);
6318 start_info->arg = arg;
6319 start_info->start_routine = start_routine;
6321 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6323 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6324 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6327 MONO_SEM_DESTROY (&(start_info->registered));
6333 mono_gc_pthread_join (pthread_t thread, void **retval)
6335 return pthread_join (thread, retval);
6339 mono_gc_pthread_detach (pthread_t thread)
6341 return pthread_detach (thread);
6344 #endif /* USE_PTHREAD_INTERCEPT */
6347 * ######################################################################
6348 * ######## Write barriers
6349 * ######################################################################
6352 static RememberedSet*
6353 alloc_remset (int size, gpointer id) {
6354 RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6355 res->store_next = res->data;
6356 res->end_set = res->data + size;
6358 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6363 * Note: the write barriers first do the needed GC work and then do the actual store:
6364 * this way the value is visible to the conservative GC scan after the write barrier
6365 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6366 * the conservative scan, otherwise by the remembered set scan.
6369 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6373 HEAVY_STAT (++stat_wbarrier_set_field);
6374 if (ptr_in_nursery (field_ptr)) {
6375 *(void**)field_ptr = value;
6378 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6380 rs = REMEMBERED_SET;
6381 if (rs->store_next < rs->end_set) {
6382 *(rs->store_next++) = (mword)field_ptr;
6383 *(void**)field_ptr = value;
6387 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6388 rs->next = REMEMBERED_SET;
6389 REMEMBERED_SET = rs;
6390 #ifdef HAVE_KW_THREAD
6391 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6393 *(rs->store_next++) = (mword)field_ptr;
6394 *(void**)field_ptr = value;
6399 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6403 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6404 if (ptr_in_nursery (slot_ptr)) {
6405 *(void**)slot_ptr = value;
6408 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6410 rs = REMEMBERED_SET;
6411 if (rs->store_next < rs->end_set) {
6412 *(rs->store_next++) = (mword)slot_ptr;
6413 *(void**)slot_ptr = value;
6417 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6418 rs->next = REMEMBERED_SET;
6419 REMEMBERED_SET = rs;
6420 #ifdef HAVE_KW_THREAD
6421 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6423 *(rs->store_next++) = (mword)slot_ptr;
6424 *(void**)slot_ptr = value;
6429 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6433 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6435 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6436 if (ptr_in_nursery (dest_ptr)) {
6440 rs = REMEMBERED_SET;
6441 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6442 if (rs->store_next + 1 < rs->end_set) {
6443 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6444 *(rs->store_next++) = count;
6448 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6449 rs->next = REMEMBERED_SET;
6450 REMEMBERED_SET = rs;
6451 #ifdef HAVE_KW_THREAD
6452 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6454 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6455 *(rs->store_next++) = count;
6459 static char *found_obj;
6462 find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
6464 if (ptr >= obj && ptr < obj + size) {
6465 g_assert (!found_obj);
6470 /* for use in the debugger */
6471 char* find_object_for_ptr (char *ptr);
6473 find_object_for_ptr (char *ptr)
6477 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6479 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6480 (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
6485 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
6486 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
6487 return bigobj->data;
6491 * Very inefficient, but this is debugging code, supposed to
6492 * be called from gdb, so we don't care.
6495 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
6500 evacuate_remset_buffer (void)
6505 buffer = STORE_REMSET_BUFFER;
6507 add_generic_store_remset_from_buffer (buffer);
6508 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6510 STORE_REMSET_BUFFER_INDEX = 0;
6514 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6520 HEAVY_STAT (++stat_wbarrier_generic_store);
6522 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6523 /* FIXME: ptr_in_heap must be called with the GC lock held */
6524 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6525 char *start = find_object_for_ptr (ptr);
6526 MonoObject *value = *(MonoObject**)ptr;
6530 MonoObject *obj = (MonoObject*)start;
6531 if (obj->vtable->domain != value->vtable->domain)
6532 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6540 if (*(gpointer*)ptr)
6541 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6543 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6544 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6549 buffer = STORE_REMSET_BUFFER;
6550 index = STORE_REMSET_BUFFER_INDEX;
6551 /* This simple optimization eliminates a sizable portion of
6552 entries. Comparing it to the last but one entry as well
6553 doesn't eliminate significantly more entries. */
6554 if (buffer [index] == ptr) {
6559 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6560 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6563 if (index >= STORE_REMSET_BUFFER_SIZE) {
6564 evacuate_remset_buffer ();
6565 index = STORE_REMSET_BUFFER_INDEX;
6566 g_assert (index == 0);
6569 buffer [index] = ptr;
6570 STORE_REMSET_BUFFER_INDEX = index;
6576 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6578 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6579 *(void**)ptr = value;
6580 if (ptr_in_nursery (value))
6581 mono_gc_wbarrier_generic_nostore (ptr);
6585 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6589 HEAVY_STAT (++stat_wbarrier_value_copy);
6590 g_assert (klass->valuetype);
6592 memmove (dest, src, count * mono_class_value_size (klass, NULL));
6593 rs = REMEMBERED_SET;
6594 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !klass->has_references) {
6598 g_assert (klass->gc_descr_inited);
6599 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));
6601 if (rs->store_next + 3 < rs->end_set) {
6602 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6603 *(rs->store_next++) = (mword)klass->gc_descr;
6604 *(rs->store_next++) = (mword)count;
6608 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6609 rs->next = REMEMBERED_SET;
6610 REMEMBERED_SET = rs;
6611 #ifdef HAVE_KW_THREAD
6612 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6614 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6615 *(rs->store_next++) = (mword)klass->gc_descr;
6616 *(rs->store_next++) = (mword)count;
6621 * mono_gc_wbarrier_object_copy:
6623 * Write barrier to call when obj is the result of a clone or copy of an object.
6626 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6632 HEAVY_STAT (++stat_wbarrier_object_copy);
6633 rs = REMEMBERED_SET;
6634 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6635 size = mono_object_class (obj)->instance_size;
6637 /* do not copy the sync state */
6638 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6639 size - sizeof (MonoObject));
6640 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6644 if (rs->store_next < rs->end_set) {
6645 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6649 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6650 rs->next = REMEMBERED_SET;
6651 REMEMBERED_SET = rs;
6652 #ifdef HAVE_KW_THREAD
6653 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6655 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6660 * ######################################################################
6661 * ######## Collector debugging
6662 * ######################################################################
6665 const char*descriptor_types [] = {
6677 describe_ptr (char *ptr)
6683 if (ptr_in_nursery (ptr)) {
6684 printf ("Pointer inside nursery.\n");
6686 if (major_ptr_is_in_non_pinned_space (ptr)) {
6687 printf ("Pointer inside oldspace.\n");
6688 } else if (obj_is_from_pinned_alloc (ptr)) {
6689 printf ("Pointer is inside a pinned chunk.\n");
6691 printf ("Pointer unknown.\n");
6696 if (object_is_pinned (ptr))
6697 printf ("Object is pinned.\n");
6699 if (object_is_forwarded (ptr))
6700 printf ("Object is forwared.\n");
6702 // FIXME: Handle pointers to the inside of objects
6703 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6705 printf ("VTable: %p\n", vtable);
6706 if (vtable == NULL) {
6707 printf ("VTable is invalid (empty).\n");
6710 if (ptr_in_nursery (vtable)) {
6711 printf ("VTable is invalid (points inside nursery).\n");
6714 printf ("Class: %s\n", vtable->klass->name);
6716 desc = ((GCVTable*)vtable)->desc;
6717 printf ("Descriptor: %lx\n", (long)desc);
6720 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6724 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6730 switch ((*p) & REMSET_TYPE_MASK) {
6731 case REMSET_LOCATION:
6732 if (*p == (mword)addr)
6736 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6738 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6742 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6743 count = safe_object_get_size ((MonoObject*)ptr);
6744 count += (ALLOC_ALIGN - 1);
6745 count &= (ALLOC_ALIGN - 1);
6746 count /= sizeof (mword);
6747 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6751 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6755 switch (desc & 0x7) {
6756 case DESC_TYPE_RUN_LENGTH:
6757 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6759 case DESC_TYPE_SMALL_BITMAP:
6760 OBJ_BITMAP_SIZE (skip_size, desc, start);
6764 g_assert_not_reached ();
6767 /* The descriptor includes the size of MonoObject */
6768 skip_size -= sizeof (MonoObject);
6770 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6775 g_assert_not_reached ();
6781 * Return whenever ADDR occurs in the remembered sets
6784 find_in_remsets (char *addr)
6787 SgenThreadInfo *info;
6788 RememberedSet *remset;
6789 GenericStoreRememberedSet *store_remset;
6791 gboolean found = FALSE;
6793 /* the global one */
6794 for (remset = global_remset; remset; remset = remset->next) {
6795 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));
6796 for (p = remset->data; p < remset->store_next;) {
6797 p = find_in_remset_loc (p, addr, &found);
6803 /* the generic store ones */
6804 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6805 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6806 if (store_remset->data [i] == addr)
6811 /* the per-thread ones */
6812 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6813 for (info = thread_table [i]; info; info = info->next) {
6815 for (remset = info->remset; remset; remset = remset->next) {
6816 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));
6817 for (p = remset->data; p < remset->store_next;) {
6818 p = find_in_remset_loc (p, addr, &found);
6823 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6824 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6830 /* the freed thread ones */
6831 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6832 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));
6833 for (p = remset->data; p < remset->store_next;) {
6834 p = find_in_remset_loc (p, addr, &found);
6843 static gboolean missing_remsets;
6846 * We let a missing remset slide if the target object is pinned,
6847 * because the store might have happened but the remset not yet added,
6848 * but in that case the target must be pinned. We might theoretically
6849 * miss some missing remsets this way, but it's very unlikely.
6852 #define HANDLE_PTR(ptr,obj) do { \
6853 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6854 if (!find_in_remsets ((char*)(ptr))) { \
6855 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); \
6856 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6857 if (!object_is_pinned (*(ptr))) \
6858 missing_remsets = TRUE; \
6864 * Check that each object reference which points into the nursery can
6865 * be found in the remembered sets.
6868 check_consistency_callback (char *start, size_t size, void *dummy)
6870 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6871 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6873 #define SCAN_OBJECT_ACTION
6874 #include "sgen-scan-object.h"
6878 * Perform consistency check of the heap.
6880 * Assumes the world is stopped.
6883 check_consistency (void)
6887 // Need to add more checks
6889 missing_remsets = FALSE;
6891 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6893 // Check that oldspace->newspace pointers are registered with the collector
6894 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6896 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6897 check_consistency_callback (bigobj->data, bigobj->size, NULL);
6899 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6901 #ifdef BINARY_PROTOCOL
6902 if (!binary_protocol_file)
6904 g_assert (!missing_remsets);
6909 #define HANDLE_PTR(ptr,obj) do { \
6911 g_assert (LOAD_VTABLE (*(ptr))); \
6915 check_major_refs_callback (char *start, size_t size, void *dummy)
6917 #define SCAN_OBJECT_ACTION
6918 #include "sgen-scan-object.h"
6922 check_major_refs (void)
6926 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6928 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6929 check_major_refs_callback (bigobj->data, bigobj->size, NULL);
6932 /* Check that the reference is valid */
6934 #define HANDLE_PTR(ptr,obj) do { \
6936 g_assert (safe_name (*(ptr)) != NULL); \
6943 * Perform consistency check on an object. Currently we only check that the
6944 * reference fields are valid.
6947 check_object (char *start)
6952 #include "sgen-scan-object.h"
6956 * ######################################################################
6957 * ######## Other mono public interface functions.
6958 * ######################################################################
6962 mono_gc_collect (int generation)
6966 if (generation == 0) {
6967 collect_nursery (0);
6969 major_collection ("user request");
6976 mono_gc_max_generation (void)
6982 mono_gc_collection_count (int generation)
6984 if (generation == 0)
6985 return num_minor_gcs;
6986 return num_major_gcs;
6990 mono_gc_get_used_size (void)
6994 tot = los_memory_usage;
6995 tot += nursery_section->next_data - nursery_section->data;
6996 tot += major_get_used_size ();
6997 /* FIXME: account for pinned objects */
7003 mono_gc_get_heap_size (void)
7009 mono_gc_disable (void)
7017 mono_gc_enable (void)
7025 mono_gc_get_los_limit (void)
7027 return MAX_SMALL_OBJ_SIZE;
7031 mono_object_is_alive (MonoObject* o)
7037 mono_gc_get_generation (MonoObject *obj)
7039 if (ptr_in_nursery (obj))
7045 mono_gc_enable_events (void)
7050 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7053 mono_gc_register_disappearing_link (obj, link_addr, track);
7058 mono_gc_weak_link_remove (void **link_addr)
7061 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7066 mono_gc_weak_link_get (void **link_addr)
7070 return (MonoObject*) REVEAL_POINTER (*link_addr);
7074 mono_gc_ephemeron_array_add (MonoObject *obj)
7076 EphemeronLinkNode *node;
7080 node = get_internal_mem (sizeof (EphemeronLinkNode), INTERNAL_MEM_EPHEMERON_LINK);
7085 node->array = (char*)obj;
7086 node->next = ephemeron_list;
7087 ephemeron_list = node;
7089 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
7096 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7098 if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7099 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7101 mword complex = alloc_complex_descriptor (bitmap, numbits);
7102 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7107 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7111 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7112 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7113 user_descriptors [user_descriptors_next ++] = marker;
7119 mono_gc_alloc_fixed (size_t size, void *descr)
7121 /* FIXME: do a single allocation */
7122 void *res = calloc (1, size);
7125 if (!mono_gc_register_root (res, size, descr)) {
7133 mono_gc_free_fixed (void* addr)
7135 mono_gc_deregister_root (addr);
7140 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7144 result = func (data);
7145 UNLOCK_INTERRUPTION;
7150 mono_gc_is_gc_thread (void)
7154 result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
7161 /* Tries to extract a number from the passed string, taking in to account m, k
7164 parse_environment_string_extract_number (gchar *str, glong *out)
7167 int len = strlen (str), shift = 0;
7169 gboolean is_suffix = FALSE;
7172 switch (str [len - 1]) {
7183 suffix = str [len - 1];
7188 val = strtol (str, &endptr, 10);
7190 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
7191 || (errno != 0 && val == 0) || (endptr == str))
7195 if (*(endptr + 1)) /* Invalid string. */
7207 mono_gc_base_init (void)
7211 struct sigaction sinfo;
7213 LOCK_INIT (gc_mutex);
7215 if (gc_initialized) {
7219 pagesize = mono_pagesize ();
7220 gc_debug_file = stderr;
7224 if ((env = getenv ("MONO_GC_PARAMS"))) {
7225 if (g_str_has_prefix (env, "nursery-size")) {
7228 while (env [index] && env [index++] != '=')
7230 if (env [index] && parse_environment_string_extract_number (env
7232 default_nursery_size = val;
7233 #ifdef ALIGN_NURSERY
7234 if ((val & (val - 1))) {
7235 fprintf (stderr, "The nursery size must be a power of two.\n");
7239 default_nursery_bits = 0;
7240 while (1 << (++ default_nursery_bits) != default_nursery_size)
7244 fprintf (stderr, "nursery-size must be an integer.\n");
7248 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");
7255 nursery_size = DEFAULT_NURSERY_SIZE;
7256 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7260 if ((env = getenv ("MONO_GC_DEBUG"))) {
7261 opts = g_strsplit (env, ",", -1);
7262 for (ptr = opts; ptr && *ptr; ptr ++) {
7264 if (opt [0] >= '0' && opt [0] <= '9') {
7265 gc_debug_level = atoi (opt);
7270 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7271 gc_debug_file = fopen (rf, "wb");
7273 gc_debug_file = stderr;
7276 } else if (!strcmp (opt, "collect-before-allocs")) {
7277 collect_before_allocs = TRUE;
7278 } else if (!strcmp (opt, "check-at-minor-collections")) {
7279 consistency_check_at_minor_collection = TRUE;
7280 nursery_clear_policy = CLEAR_AT_GC;
7281 } else if (!strcmp (opt, "xdomain-checks")) {
7282 xdomain_checks = TRUE;
7283 } else if (!strcmp (opt, "clear-at-gc")) {
7284 nursery_clear_policy = CLEAR_AT_GC;
7285 } else if (!strcmp (opt, "conservative-stack-mark")) {
7286 conservative_stack_mark = TRUE;
7287 } else if (!strcmp (opt, "check-scan-starts")) {
7288 do_scan_starts_check = TRUE;
7289 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7290 char *filename = strchr (opt, '=') + 1;
7291 nursery_clear_policy = CLEAR_AT_GC;
7292 heap_dump_file = fopen (filename, "w");
7294 fprintf (heap_dump_file, "<sgen-dump>\n");
7295 #ifdef BINARY_PROTOCOL
7296 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7297 char *filename = strchr (opt, '=') + 1;
7298 binary_protocol_file = fopen (filename, "w");
7301 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7302 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7303 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
7310 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7311 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7313 sigfillset (&sinfo.sa_mask);
7314 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7315 sinfo.sa_sigaction = suspend_handler;
7316 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7317 g_error ("failed sigaction");
7320 sinfo.sa_handler = restart_handler;
7321 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7322 g_error ("failed sigaction");
7325 sigfillset (&suspend_signal_mask);
7326 sigdelset (&suspend_signal_mask, restart_signal_num);
7328 global_remset = alloc_remset (1024, NULL);
7329 global_remset->next = NULL;
7331 pthread_key_create (&remembered_set_key, unregister_thread);
7333 #ifndef HAVE_KW_THREAD
7334 pthread_key_create (&thread_info_key, NULL);
7337 gc_initialized = TRUE;
7339 mono_gc_register_thread (&sinfo);
7343 mono_gc_get_suspend_signal (void)
7345 return suspend_signal_num;
7355 #ifdef HAVE_KW_THREAD
7356 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7357 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7358 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7359 mono_mb_emit_i4 ((mb), (offset)); \
7362 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7363 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7364 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7365 mono_mb_emit_i4 ((mb), thread_info_key); \
7366 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7367 mono_mb_emit_byte ((mb), CEE_ADD); \
7368 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7372 #ifdef MANAGED_ALLOCATION
7373 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7374 * for each class. This is currently not easy to do, as it is hard to generate basic
7375 * blocks + branches, but it is easy with the linear IL codebase.
7377 * For this to work we'd need to solve the TLAB race, first. Now we
7378 * require the allocator to be in a few known methods to make sure
7379 * that they are executed atomically via the restart mechanism.
7382 create_allocator (int atype)
7384 int p_var, size_var;
7385 guint32 slowpath_branch, max_size_branch;
7386 MonoMethodBuilder *mb;
7388 MonoMethodSignature *csig;
7389 static gboolean registered = FALSE;
7390 int tlab_next_addr_var, new_next_var;
7392 const char *name = NULL;
7393 AllocatorWrapperInfo *info;
7395 #ifdef HAVE_KW_THREAD
7396 int tlab_next_addr_offset = -1;
7397 int tlab_temp_end_offset = -1;
7399 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7400 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7402 g_assert (tlab_next_addr_offset != -1);
7403 g_assert (tlab_temp_end_offset != -1);
7407 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7408 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7412 if (atype == ATYPE_SMALL) {
7414 name = "AllocSmall";
7415 } else if (atype == ATYPE_NORMAL) {
7418 } else if (atype == ATYPE_VECTOR) {
7420 name = "AllocVector";
7422 g_assert_not_reached ();
7425 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7426 csig->ret = &mono_defaults.object_class->byval_arg;
7427 for (i = 0; i < num_params; ++i)
7428 csig->params [i] = &mono_defaults.int_class->byval_arg;
7430 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7431 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7432 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7433 /* size = vtable->klass->instance_size; */
7434 mono_mb_emit_ldarg (mb, 0);
7435 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7436 mono_mb_emit_byte (mb, CEE_ADD);
7437 mono_mb_emit_byte (mb, CEE_LDIND_I);
7438 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7439 mono_mb_emit_byte (mb, CEE_ADD);
7440 /* FIXME: assert instance_size stays a 4 byte integer */
7441 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7442 mono_mb_emit_stloc (mb, size_var);
7443 } else if (atype == ATYPE_VECTOR) {
7444 MonoExceptionClause *clause;
7446 MonoClass *oom_exc_class;
7449 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7450 mono_mb_emit_ldarg (mb, 1);
7451 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7452 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7453 mono_mb_emit_exception (mb, "OverflowException", NULL);
7454 mono_mb_patch_short_branch (mb, pos);
7456 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7457 clause->try_offset = mono_mb_get_label (mb);
7459 /* vtable->klass->sizes.element_size */
7460 mono_mb_emit_ldarg (mb, 0);
7461 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7462 mono_mb_emit_byte (mb, CEE_ADD);
7463 mono_mb_emit_byte (mb, CEE_LDIND_I);
7464 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7465 mono_mb_emit_byte (mb, CEE_ADD);
7466 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7469 mono_mb_emit_ldarg (mb, 1);
7470 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7471 /* + sizeof (MonoArray) */
7472 mono_mb_emit_icon (mb, sizeof (MonoArray));
7473 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7474 mono_mb_emit_stloc (mb, size_var);
7476 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7479 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7480 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7481 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7482 "System", "OverflowException");
7483 g_assert (clause->data.catch_class);
7484 clause->handler_offset = mono_mb_get_label (mb);
7486 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7487 "System", "OutOfMemoryException");
7488 g_assert (oom_exc_class);
7489 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7492 mono_mb_emit_byte (mb, CEE_POP);
7493 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7494 mono_mb_emit_byte (mb, CEE_THROW);
7496 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7497 mono_mb_set_clauses (mb, 1, clause);
7498 mono_mb_patch_branch (mb, pos_leave);
7501 g_assert_not_reached ();
7504 /* size += ALLOC_ALIGN - 1; */
7505 mono_mb_emit_ldloc (mb, size_var);
7506 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7507 mono_mb_emit_byte (mb, CEE_ADD);
7508 /* size &= ~(ALLOC_ALIGN - 1); */
7509 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7510 mono_mb_emit_byte (mb, CEE_AND);
7511 mono_mb_emit_stloc (mb, size_var);
7513 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7514 if (atype != ATYPE_SMALL) {
7515 mono_mb_emit_ldloc (mb, size_var);
7516 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7517 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7521 * We need to modify tlab_next, but the JIT only supports reading, so we read
7522 * another tls var holding its address instead.
7525 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7526 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7527 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7528 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7530 /* p = (void**)tlab_next; */
7531 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7532 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7533 mono_mb_emit_byte (mb, CEE_LDIND_I);
7534 mono_mb_emit_stloc (mb, p_var);
7536 /* new_next = (char*)p + size; */
7537 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7538 mono_mb_emit_ldloc (mb, p_var);
7539 mono_mb_emit_ldloc (mb, size_var);
7540 mono_mb_emit_byte (mb, CEE_CONV_I);
7541 mono_mb_emit_byte (mb, CEE_ADD);
7542 mono_mb_emit_stloc (mb, new_next_var);
7544 /* tlab_next = new_next */
7545 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7546 mono_mb_emit_ldloc (mb, new_next_var);
7547 mono_mb_emit_byte (mb, CEE_STIND_I);
7549 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7550 mono_mb_emit_ldloc (mb, new_next_var);
7551 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7552 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7555 if (atype != ATYPE_SMALL)
7556 mono_mb_patch_short_branch (mb, max_size_branch);
7558 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7559 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7561 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7562 mono_mb_emit_ldarg (mb, 0);
7563 mono_mb_emit_ldloc (mb, size_var);
7564 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7565 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7566 } else if (atype == ATYPE_VECTOR) {
7567 mono_mb_emit_ldarg (mb, 1);
7568 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7570 g_assert_not_reached ();
7572 mono_mb_emit_byte (mb, CEE_RET);
7575 mono_mb_patch_short_branch (mb, slowpath_branch);
7577 /* FIXME: Memory barrier */
7580 mono_mb_emit_ldloc (mb, p_var);
7581 mono_mb_emit_ldarg (mb, 0);
7582 mono_mb_emit_byte (mb, CEE_STIND_I);
7584 if (atype == ATYPE_VECTOR) {
7585 /* arr->max_length = max_length; */
7586 mono_mb_emit_ldloc (mb, p_var);
7587 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7588 mono_mb_emit_ldarg (mb, 1);
7589 mono_mb_emit_byte (mb, CEE_STIND_I);
7593 mono_mb_emit_ldloc (mb, p_var);
7594 mono_mb_emit_byte (mb, CEE_RET);
7596 res = mono_mb_create_method (mb, csig, 8);
7598 mono_method_get_header (res)->init_locals = FALSE;
7600 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7601 info->alloc_type = atype;
7602 mono_marshal_set_wrapper_info (res, info);
7608 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7609 static MonoMethod *write_barrier_method;
7612 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7620 ji = mono_jit_info_table_find (domain, ip);
7623 method = ji->method;
7625 if (method == write_barrier_method)
7627 for (i = 0; i < ATYPE_NUM; ++i)
7628 if (method == alloc_method_cache [i])
7634 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7635 * The signature of the called method is:
7636 * object allocate (MonoVTable *vtable)
7639 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7641 #ifdef MANAGED_ALLOCATION
7642 MonoClass *klass = vtable->klass;
7644 #ifdef HAVE_KW_THREAD
7645 int tlab_next_offset = -1;
7646 int tlab_temp_end_offset = -1;
7647 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7648 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7650 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7654 if (!mono_runtime_has_tls_get ())
7656 if (klass->instance_size > tlab_size)
7658 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7662 if (klass->byval_arg.type == MONO_TYPE_STRING)
7664 if (collect_before_allocs)
7667 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7668 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7670 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7677 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7679 #ifdef MANAGED_ALLOCATION
7680 MonoClass *klass = vtable->klass;
7682 #ifdef HAVE_KW_THREAD
7683 int tlab_next_offset = -1;
7684 int tlab_temp_end_offset = -1;
7685 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7686 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7688 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7694 if (!mono_runtime_has_tls_get ())
7696 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7698 if (collect_before_allocs)
7700 g_assert (!klass->has_finalize && !klass->marshalbyref);
7702 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7709 mono_gc_get_managed_allocator_by_type (int atype)
7711 #ifdef MANAGED_ALLOCATION
7714 if (!mono_runtime_has_tls_get ())
7717 mono_loader_lock ();
7718 res = alloc_method_cache [atype];
7720 res = alloc_method_cache [atype] = create_allocator (atype);
7721 mono_loader_unlock ();
7729 mono_gc_get_managed_allocator_types (void)
7736 mono_gc_get_write_barrier (void)
7739 MonoMethodBuilder *mb;
7740 MonoMethodSignature *sig;
7741 #ifdef MANAGED_WBARRIER
7742 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7743 #ifndef ALIGN_NURSERY
7744 int label_continue_1, label_continue_2, label_no_wb_5;
7745 int dereferenced_var;
7747 int buffer_var, buffer_index_var, dummy_var;
7749 #ifdef HAVE_KW_THREAD
7750 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7751 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7753 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7754 g_assert (stack_end_offset != -1);
7755 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7756 g_assert (store_remset_buffer_offset != -1);
7757 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7758 g_assert (store_remset_buffer_index_offset != -1);
7759 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7760 g_assert (store_remset_buffer_index_addr_offset != -1);
7764 // FIXME: Maybe create a separate version for ctors (the branch would be
7765 // correctly predicted more times)
7766 if (write_barrier_method)
7767 return write_barrier_method;
7769 /* Create the IL version of mono_gc_barrier_generic_store () */
7770 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7771 sig->ret = &mono_defaults.void_class->byval_arg;
7772 sig->params [0] = &mono_defaults.int_class->byval_arg;
7774 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7776 #ifdef MANAGED_WBARRIER
7777 if (mono_runtime_has_tls_get ()) {
7778 #ifdef ALIGN_NURSERY
7779 // if (ptr_in_nursery (ptr)) return;
7781 * Masking out the bits might be faster, but we would have to use 64 bit
7782 * immediates, which might be slower.
7784 mono_mb_emit_ldarg (mb, 0);
7785 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7786 mono_mb_emit_byte (mb, CEE_SHR_UN);
7787 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7788 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7790 // if (!ptr_in_nursery (*ptr)) return;
7791 mono_mb_emit_ldarg (mb, 0);
7792 mono_mb_emit_byte (mb, CEE_LDIND_I);
7793 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7794 mono_mb_emit_byte (mb, CEE_SHR_UN);
7795 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7796 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7799 // if (ptr < (nursery_start)) goto continue;
7800 mono_mb_emit_ldarg (mb, 0);
7801 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7802 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7804 // if (ptr >= nursery_real_end)) goto continue;
7805 mono_mb_emit_ldarg (mb, 0);
7806 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7807 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7810 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7813 mono_mb_patch_branch (mb, label_continue_1);
7814 mono_mb_patch_branch (mb, label_continue_2);
7816 // Dereference and store in local var
7817 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7818 mono_mb_emit_ldarg (mb, 0);
7819 mono_mb_emit_byte (mb, CEE_LDIND_I);
7820 mono_mb_emit_stloc (mb, dereferenced_var);
7822 // if (*ptr < nursery_start) return;
7823 mono_mb_emit_ldloc (mb, dereferenced_var);
7824 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7825 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7827 // if (*ptr >= nursery_end) return;
7828 mono_mb_emit_ldloc (mb, dereferenced_var);
7829 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7830 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7833 // if (ptr >= stack_end) goto need_wb;
7834 mono_mb_emit_ldarg (mb, 0);
7835 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7836 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7838 // if (ptr >= stack_start) return;
7839 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7840 mono_mb_emit_ldarg (mb, 0);
7841 mono_mb_emit_ldloc_addr (mb, dummy_var);
7842 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7845 mono_mb_patch_branch (mb, label_need_wb);
7847 // buffer = STORE_REMSET_BUFFER;
7848 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7849 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7850 mono_mb_emit_stloc (mb, buffer_var);
7852 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7853 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7854 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7855 mono_mb_emit_stloc (mb, buffer_index_var);
7857 // if (buffer [buffer_index] == ptr) return;
7858 mono_mb_emit_ldloc (mb, buffer_var);
7859 mono_mb_emit_ldloc (mb, buffer_index_var);
7860 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7861 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7862 mono_mb_emit_byte (mb, CEE_SHL);
7863 mono_mb_emit_byte (mb, CEE_ADD);
7864 mono_mb_emit_byte (mb, CEE_LDIND_I);
7865 mono_mb_emit_ldarg (mb, 0);
7866 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7869 mono_mb_emit_ldloc (mb, buffer_index_var);
7870 mono_mb_emit_icon (mb, 1);
7871 mono_mb_emit_byte (mb, CEE_ADD);
7872 mono_mb_emit_stloc (mb, buffer_index_var);
7874 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7875 mono_mb_emit_ldloc (mb, buffer_index_var);
7876 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7877 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7879 // buffer [buffer_index] = ptr;
7880 mono_mb_emit_ldloc (mb, buffer_var);
7881 mono_mb_emit_ldloc (mb, buffer_index_var);
7882 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7883 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7884 mono_mb_emit_byte (mb, CEE_SHL);
7885 mono_mb_emit_byte (mb, CEE_ADD);
7886 mono_mb_emit_ldarg (mb, 0);
7887 mono_mb_emit_byte (mb, CEE_STIND_I);
7889 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7890 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7891 mono_mb_emit_ldloc (mb, buffer_index_var);
7892 mono_mb_emit_byte (mb, CEE_STIND_I);
7895 mono_mb_patch_branch (mb, label_no_wb_1);
7896 mono_mb_patch_branch (mb, label_no_wb_2);
7897 mono_mb_patch_branch (mb, label_no_wb_3);
7898 mono_mb_patch_branch (mb, label_no_wb_4);
7899 #ifndef ALIGN_NURSERY
7900 mono_mb_patch_branch (mb, label_no_wb_5);
7902 mono_mb_emit_byte (mb, CEE_RET);
7905 mono_mb_patch_branch (mb, label_slow_path);
7909 mono_mb_emit_ldarg (mb, 0);
7910 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7911 mono_mb_emit_byte (mb, CEE_RET);
7913 res = mono_mb_create_method (mb, sig, 16);
7916 mono_loader_lock ();
7917 if (write_barrier_method) {
7918 /* Already created */
7919 mono_free_method (res);
7921 /* double-checked locking */
7922 mono_memory_barrier ();
7923 write_barrier_method = res;
7925 mono_loader_unlock ();
7927 return write_barrier_method;
7930 #endif /* HAVE_SGEN_GC */