2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
15 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
18 * Permission is hereby granted to use or copy this program
19 * for any purpose, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
25 * Copyright 2001-2003 Ximian, Inc
26 * Copyright 2003-2010 Novell, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining
29 * a copy of this software and associated documentation files (the
30 * "Software"), to deal in the Software without restriction, including
31 * without limitation the rights to use, copy, modify, merge, publish,
32 * distribute, sublicense, and/or sell copies of the Software, and to
33 * permit persons to whom the Software is furnished to do so, subject to
34 * the following conditions:
36 * The above copyright notice and this permission notice shall be
37 * included in all copies or substantial portions of the Software.
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 * Important: allocation provides always zeroed memory, having to do
49 * a memset after allocation is deadly for performance.
50 * Memory usage at startup is currently as follows:
52 * 64 KB internal space
54 * We should provide a small memory config with half the sizes
56 * We currently try to make as few mono assumptions as possible:
57 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
59 * 2) gc descriptor is the second word in the vtable (first word in the class)
60 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
61 * 4) there is a function to get an object's size and the number of
62 * elements in an array.
63 * 5) we know the special way bounds are allocated for complex arrays
64 * 6) we know about proxies and how to treat them when domains are unloaded
66 * Always try to keep stack usage to a minimum: no recursive behaviour
67 * and no large stack allocs.
69 * General description.
70 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
71 * When the nursery is full we start a nursery collection: this is performed with a
73 * When the old generation is full we start a copying GC of the old generation as well:
74 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
75 * in the future. Maybe we'll even do both during the same collection like IMMIX.
77 * The things that complicate this description are:
78 * *) pinned objects: we can't move them so we need to keep track of them
79 * *) no precise info of the thread stacks and registers: we need to be able to
80 * quickly find the objects that may be referenced conservatively and pin them
81 * (this makes the first issues more important)
82 * *) large objects are too expensive to be dealt with using copying GC: we handle them
83 * with mark/sweep during major collections
84 * *) some objects need to not move even if they are small (interned strings, Type handles):
85 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
86 * PinnedChunks regions
92 *) we could have a function pointer in MonoClass to implement
93 customized write barriers for value types
95 *) investigate the stuff needed to advance a thread to a GC-safe
96 point (single-stepping, read from unmapped memory etc) and implement it.
97 This would enable us to inline allocations and write barriers, for example,
98 or at least parts of them, like the write barrier checks.
99 We may need this also for handling precise info on stacks, even simple things
100 as having uninitialized data on the stack and having to wait for the prolog
101 to zero it. Not an issue for the last frame that we scan conservatively.
102 We could always not trust the value in the slots anyway.
104 *) modify the jit to save info about references in stack locations:
105 this can be done just for locals as a start, so that at least
106 part of the stack is handled precisely.
108 *) test/fix endianess issues
110 *) Implement a card table as the write barrier instead of remembered
111 sets? Card tables are not easy to implement with our current
112 memory layout. We have several different kinds of major heap
113 objects: Small objects in regular blocks, small objects in pinned
114 chunks and LOS objects. If we just have a pointer we have no way
115 to tell which kind of object it points into, therefore we cannot
116 know where its card table is. The least we have to do to make
117 this happen is to get rid of write barriers for indirect stores.
120 *) Get rid of write barriers for indirect stores. We can do this by
121 telling the GC to wbarrier-register an object once we do an ldloca
122 or ldelema on it, and to unregister it once it's not used anymore
123 (it can only travel downwards on the stack). The problem with
124 unregistering is that it needs to happen eventually no matter
125 what, even if exceptions are thrown, the thread aborts, etc.
126 Rodrigo suggested that we could do only the registering part and
127 let the collector find out (pessimistically) when it's safe to
128 unregister, namely when the stack pointer of the thread that
129 registered the object is higher than it was when the registering
130 happened. This might make for a good first implementation to get
131 some data on performance.
133 *) Some sort of blacklist support? Blacklists is a concept from the
134 Boehm GC: if during a conservative scan we find pointers to an
135 area which we might use as heap, we mark that area as unusable, so
136 pointer retention by random pinning pointers is reduced.
138 *) experiment with max small object size (very small right now - 2kb,
139 because it's tied to the max freelist size)
141 *) add an option to mmap the whole heap in one chunk: it makes for many
142 simplifications in the checks (put the nursery at the top and just use a single
143 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
144 not flexible (too much of the address space may be used by default or we can't
145 increase the heap as needed) and we'd need a race-free mechanism to return memory
146 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
147 was written to, munmap is needed, but the following mmap may not find the same segment
150 *) memzero the major fragments after restarting the world and optionally a smaller
153 *) investigate having fragment zeroing threads
155 *) separate locks for finalization and other minor stuff to reduce
158 *) try a different copying order to improve memory locality
160 *) a thread abort after a store but before the write barrier will
161 prevent the write barrier from executing
163 *) specialized dynamically generated markers/copiers
165 *) Dynamically adjust TLAB size to the number of threads. If we have
166 too many threads that do allocation, we might need smaller TLABs,
167 and we might get better performance with larger TLABs if we only
168 have a handful of threads. We could sum up the space left in all
169 assigned TLABs and if that's more than some percentage of the
170 nursery size, reduce the TLAB size.
179 #include <semaphore.h>
184 #include "metadata/metadata-internals.h"
185 #include "metadata/class-internals.h"
186 #include "metadata/gc-internal.h"
187 #include "metadata/object-internals.h"
188 #include "metadata/threads.h"
189 #include "metadata/sgen-gc.h"
190 #include "metadata/sgen-archdep.h"
191 #include "metadata/mono-gc.h"
192 #include "metadata/method-builder.h"
193 #include "metadata/profiler-private.h"
194 #include "metadata/monitor.h"
195 #include "metadata/threadpool-internals.h"
196 #include "metadata/mempool-internals.h"
197 #include "metadata/marshal.h"
198 #include "utils/mono-mmap.h"
199 #include "utils/mono-time.h"
200 #include "utils/mono-semaphore.h"
201 #include "utils/mono-counters.h"
203 #include <mono/utils/memcheck.h>
205 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
209 #include "mono/cil/opcode.def"
216 * ######################################################################
217 * ######## Types and constants used by the GC.
218 * ######################################################################
220 #if SIZEOF_VOID_P == 4
221 typedef guint32 mword;
223 typedef guint64 mword;
226 static int gc_initialized = 0;
227 static int gc_debug_level = 0;
228 static FILE* gc_debug_file;
229 /* If set, do a minor collection before every allocation */
230 static gboolean collect_before_allocs = FALSE;
231 /* If set, do a heap consistency check before each minor collection */
232 static gboolean consistency_check_at_minor_collection = FALSE;
233 /* If set, check that there are no references to the domain left at domain unload */
234 static gboolean xdomain_checks = FALSE;
235 /* If not null, dump the heap after each collection into this file */
236 static FILE *heap_dump_file = NULL;
237 /* If set, mark stacks conservatively, even if precise marking is possible */
238 static gboolean conservative_stack_mark = TRUE;
239 /* If set, do a plausibility check on the scan_starts before and after
241 static gboolean do_scan_starts_check = FALSE;
244 * Turning on heavy statistics will turn off the managed allocator and
245 * the managed write barrier.
247 //#define HEAVY_STATISTICS
249 #ifdef HEAVY_STATISTICS
250 #define HEAVY_STAT(x) x
252 #define HEAVY_STAT(x)
255 #ifdef HEAVY_STATISTICS
256 static long stat_objects_alloced = 0;
257 static long stat_bytes_alloced = 0;
258 static long stat_objects_alloced_degraded = 0;
259 static long stat_bytes_alloced_degraded = 0;
260 static long stat_bytes_alloced_los = 0;
262 static long stat_copy_object_called_nursery = 0;
263 static long stat_objects_copied_nursery = 0;
264 static long stat_copy_object_called_major = 0;
265 static long stat_objects_copied_major = 0;
267 static long stat_scan_object_called_nursery = 0;
268 static long stat_scan_object_called_major = 0;
270 static long stat_nursery_copy_object_failed_from_space = 0;
271 static long stat_nursery_copy_object_failed_forwarded = 0;
272 static long stat_nursery_copy_object_failed_pinned = 0;
274 static long stat_store_remsets = 0;
275 static long stat_store_remsets_unique = 0;
276 static long stat_saved_remsets_1 = 0;
277 static long stat_saved_remsets_2 = 0;
278 static long stat_global_remsets_added = 0;
279 static long stat_global_remsets_readded = 0;
280 static long stat_global_remsets_processed = 0;
282 static int stat_wbarrier_set_field = 0;
283 static int stat_wbarrier_set_arrayref = 0;
284 static int stat_wbarrier_arrayref_copy = 0;
285 static int stat_wbarrier_generic_store = 0;
286 static int stat_wbarrier_generic_store_remset = 0;
287 static int stat_wbarrier_set_root = 0;
288 static int stat_wbarrier_value_copy = 0;
289 static int stat_wbarrier_object_copy = 0;
292 static long time_minor_pre_collection_fragment_clear = 0;
293 static long time_minor_pinning = 0;
294 static long time_minor_scan_remsets = 0;
295 static long time_minor_scan_pinned = 0;
296 static long time_minor_scan_registered_roots = 0;
297 static long time_minor_scan_thread_data = 0;
298 static long time_minor_finish_gray_stack = 0;
299 static long time_minor_fragment_creation = 0;
301 static long time_major_pre_collection_fragment_clear = 0;
302 static long time_major_pinning = 0;
303 static long time_major_scan_pinned = 0;
304 static long time_major_scan_registered_roots = 0;
305 static long time_major_scan_thread_data = 0;
306 static long time_major_scan_alloc_pinned = 0;
307 static long time_major_scan_finalized = 0;
308 static long time_major_scan_big_objects = 0;
309 static long time_major_finish_gray_stack = 0;
310 static long time_major_sweep = 0;
311 static long time_major_fragment_creation = 0;
313 static long pinned_chunk_bytes_alloced = 0;
314 static long large_internal_bytes_alloced = 0;
316 /* Keep in sync with internal_mem_names in dump_heap()! */
318 INTERNAL_MEM_PIN_QUEUE,
319 INTERNAL_MEM_FRAGMENT,
320 INTERNAL_MEM_SECTION,
321 INTERNAL_MEM_SCAN_STARTS,
322 INTERNAL_MEM_FIN_TABLE,
323 INTERNAL_MEM_FINALIZE_ENTRY,
324 INTERNAL_MEM_DISLINK_TABLE,
325 INTERNAL_MEM_DISLINK,
326 INTERNAL_MEM_ROOTS_TABLE,
327 INTERNAL_MEM_ROOT_RECORD,
328 INTERNAL_MEM_STATISTICS,
330 INTERNAL_MEM_GRAY_QUEUE,
331 INTERNAL_MEM_STORE_REMSET,
332 INTERNAL_MEM_MS_TABLES,
333 INTERNAL_MEM_MS_BLOCK_INFO,
337 static long small_internal_mem_bytes [INTERNAL_MEM_MAX];
341 mono_gc_flush_info (void)
343 fflush (gc_debug_file);
347 #define MAX_DEBUG_LEVEL 2
348 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
350 /* Define this to allow the user to change some of the constants by specifying
351 * their values in the MONO_GC_PARAMS environmental variable. See
352 * mono_gc_base_init for details. */
353 #define USER_CONFIG 1
355 #define TV_DECLARE(name) gint64 name
356 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
357 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
358 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
360 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
362 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
370 typedef struct _Block Block;
376 /* each request from the OS ends up in a GCMemSection */
377 typedef struct _GCMemSection GCMemSection;
378 struct _GCMemSection {
382 /* pointer where more data could be allocated if it fits */
386 * scan starts is an array of pointers to objects equally spaced in the allocation area
387 * They let use quickly find pinned objects from pinning pointers.
390 /* in major collections indexes in the pin_queue for objects that pin this section */
393 unsigned short num_scan_start;
394 gboolean is_to_space;
397 #define SIZEOF_GC_MEM_SECTION ((sizeof (GCMemSection) + 7) & ~7)
399 /* large object space struct: 64+ KB */
400 /* we could make this limit much smaller to avoid memcpy copy
401 * and potentially have more room in the GC descriptor: need to measure
402 * This also means that such small OS objects will need to be
403 * allocated in a different way (using pinned chunks).
404 * We may want to put large but smaller than 64k objects in the fixed space
405 * when we move the object from one generation to another (to limit the
406 * pig in the snake effect).
407 * Note: it may be worth to have an optimized copy function, since we can
408 * assume that objects are aligned and have a multiple of 8 size.
409 * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
410 * true if MONO_ZERO_LEN_ARRAY is nonzero.
412 typedef struct _LOSObject LOSObject;
415 mword size; /* this is the object size */
417 int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN and data starting at same alignment */
418 char data [MONO_ZERO_LEN_ARRAY];
421 /* Pinned objects are allocated in the LOS space if bigger than half a page
422 * or from freelists otherwise. We assume that pinned objects are relatively few
423 * and they have a slow dying speed (like interned strings, thread objects).
424 * As such they will be collected only at major collections.
425 * free lists are not global: when we need memory we allocate a PinnedChunk.
426 * Each pinned chunk is made of several pages, the first of wich is used
427 * internally for bookeeping (here think of a page as 4KB). The bookeeping
428 * includes the freelists vectors and info about the object size of each page
429 * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
430 * a size is assigned to it, the page is divided in the proper chunks and each
431 * chunk is added to the freelist. To not waste space, the remaining space in the
432 * first page is used as objects of size 16 or 32 (need to measure which are more
434 * We use this same structure to allocate memory used internally by the GC, so
435 * we never use malloc/free if we need to alloc during collection: the world is stopped
436 * and malloc/free will deadlock.
437 * When we want to iterate over pinned objects, we just scan a page at a time
438 * linearly according to the size of objects in the page: the next pointer used to link
439 * the items in the freelist uses the same word as the vtable. Since we keep freelists
440 * for each pinned chunk, if the word points outside the pinned chunk it means
442 * We could avoid this expensive scanning in creative ways. We could have a policy
443 * of putting in the pinned space only objects we know about that have no struct fields
444 * with references and we can easily use a even expensive write barrier for them,
445 * since pointer writes on such objects should be rare.
446 * The best compromise is to just alloc interned strings and System.MonoType in them.
447 * It would be nice to allocate MonoThread in it, too: must check that we properly
448 * use write barriers so we don't have to do any expensive scanning of the whole pinned
449 * chunk list during minor collections. We can avoid it now because we alloc in it only
450 * reference-free objects.
452 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
453 #define MAX_FREELIST_SIZE 2048
454 #define PINNED_PAGE_SIZE (4096)
455 #define PINNED_CHUNK_MIN_SIZE (4096*8)
456 typedef struct _PinnedChunk PinnedChunk;
457 struct _PinnedChunk {
460 int *page_sizes; /* a 0 means the page is still unused */
463 void *data [1]; /* page sizes and free lists are stored here */
466 /* The method used to clear the nursery */
467 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
468 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
473 CLEAR_AT_TLAB_CREATION
474 } NurseryClearPolicy;
476 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
479 * If this is set, the nursery is aligned to an address aligned to its size, ie.
480 * a 1MB nursery will be aligned to an address divisible by 1MB. This allows us to
481 * speed up ptr_in_nursery () checks which are very frequent. This requires the
482 * nursery size to be a compile time constant.
484 #define ALIGN_NURSERY 1
487 * The young generation is divided into fragments. This is because
488 * we can hand one fragments to a thread for lock-less fast alloc and
489 * because the young generation ends up fragmented anyway by pinned objects.
490 * Once a collection is done, a list of fragments is created. When doing
491 * thread local alloc we use smallish nurseries so we allow new threads to
492 * allocate memory from gen0 without triggering a collection. Threads that
493 * are found to allocate lots of memory are given bigger fragments. This
494 * should make the finalizer thread use little nursery memory after a while.
495 * We should start assigning threads very small fragments: if there are many
496 * threads the nursery will be full of reserved space that the threads may not
497 * use at all, slowing down allocation speed.
498 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
499 * Allocation Buffers (TLABs).
501 typedef struct _Fragment Fragment;
505 char *fragment_start;
506 char *fragment_limit; /* the current soft limit for allocation */
510 /* the runtime can register areas of memory as roots: we keep two lists of roots,
511 * a pinned root set for conservatively scanned roots and a normal one for
512 * precisely scanned roots (currently implemented as a single list).
514 typedef struct _RootRecord RootRecord;
522 /* for use with write barriers */
523 typedef struct _RememberedSet RememberedSet;
524 struct _RememberedSet {
528 mword data [MONO_ZERO_LEN_ARRAY];
532 * We're never actually using the first element. It's always set to
533 * NULL to simplify the elimination of consecutive duplicate
536 #define STORE_REMSET_BUFFER_SIZE 1024
538 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
539 struct _GenericStoreRememberedSet {
540 GenericStoreRememberedSet *next;
541 /* We need one entry less because the first entry of store
542 remset buffers is always a dummy and we don't copy it. */
543 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
546 /* we have 4 possible values in the low 2 bits */
548 REMSET_LOCATION, /* just a pointer to the exact location */
549 REMSET_RANGE, /* range of pointer fields */
550 REMSET_OBJECT, /* mark all the object for scanning */
551 REMSET_OTHER, /* all others */
552 REMSET_TYPE_MASK = 0x3
555 /* Subtypes of REMSET_OTHER */
557 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
558 REMSET_ROOT_LOCATION, /* a location inside a root */
561 #ifdef HAVE_KW_THREAD
562 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
564 static pthread_key_t remembered_set_key;
565 static RememberedSet *global_remset;
566 static RememberedSet *freed_thread_remsets;
567 static GenericStoreRememberedSet *generic_store_remsets = NULL;
569 /* FIXME: later choose a size that takes into account the RememberedSet struct
570 * and doesn't waste any alloc paddin space.
572 #define DEFAULT_REMSET_SIZE 1024
573 static RememberedSet* alloc_remset (int size, gpointer id);
575 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
576 * no cast from a pointer to an integer
583 /* these bits are set in the object vtable: we could merge them since an object can be
584 * either pinned or forwarded but not both.
585 * We store them in the vtable slot because the bits are used in the sync block for
586 * other purposes: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
587 * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
588 * would be an invalid combination for the monitor and hash code).
589 * The values are already shifted.
590 * The forwarding address is stored in the sync block.
592 #define FORWARDED_BIT 1
594 #define VTABLE_BITS_MASK 0x3
596 /* returns NULL if not forwarded, or the forwarded address */
597 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
598 /* set the forwarded address fw_addr for object obj */
599 #define forward_object(obj,fw_addr) do { \
600 ((mword*)(obj))[0] |= FORWARDED_BIT; \
601 ((mword*)(obj))[1] = (mword)(fw_addr); \
604 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
605 #define pin_object(obj) do { \
606 ((mword*)(obj))[0] |= PINNED_BIT; \
608 #define unpin_object(obj) do { \
609 ((mword*)(obj))[0] &= ~PINNED_BIT; \
613 #define ptr_in_nursery(ptr) (((mword)(ptr) & ~((1 << DEFAULT_NURSERY_BITS) - 1)) == (mword)nursery_start)
615 #define ptr_in_nursery(ptr) ((char*)(ptr) >= nursery_start && (char*)(ptr) < nursery_real_end)
619 * Since we set bits in the vtable, use the macro to load it from the pointer to
620 * an object that is potentially pinned.
622 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
625 safe_name (void* obj)
627 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
628 return vt->klass->name;
632 safe_object_get_size (MonoObject* o)
634 MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
635 if (klass == mono_defaults.string_class) {
636 return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
637 } else if (klass->rank) {
638 MonoArray *array = (MonoArray*)o;
639 size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length (array);
640 if (G_UNLIKELY (array->bounds)) {
641 size += sizeof (mono_array_size_t) - 1;
642 size &= ~(sizeof (mono_array_size_t) - 1);
643 size += sizeof (MonoArrayBounds) * klass->rank;
647 /* from a created object: the class must be inited already */
648 return klass->instance_size;
653 * ######################################################################
654 * ######## Global data.
655 * ######################################################################
657 static LOCK_DECLARE (gc_mutex);
658 static int gc_disabled = 0;
659 static int num_minor_gcs = 0;
660 static int num_major_gcs = 0;
664 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
665 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
666 static int default_nursery_size = (1 << 20);
668 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
669 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
670 static int default_nursery_bits = 20;
675 #define DEFAULT_NURSERY_SIZE (1024*512*2)
677 #define DEFAULT_NURSERY_BITS 20
682 #define MIN_LOS_ALLOWANCE (DEFAULT_NURSERY_SIZE * 2)
683 /* to quickly find the head of an object pinned by a conservative address
684 * we keep track of the objects allocated for each SCAN_START_SIZE memory
685 * chunk in the nursery or other memory sections. Larger values have less
686 * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
688 #define SCAN_START_SIZE (4096*2)
689 /* the minimum size of a fragment that we consider useful for allocation */
690 #define FRAGMENT_MIN_SIZE (512)
691 /* This is a fixed value used for pinned chunks, not the system pagesize */
692 #define FREELIST_PAGESIZE 4096
694 static mword pagesize = 4096;
695 static mword nursery_size;
696 static int degraded_mode = 0;
698 static LOSObject *los_object_list = NULL;
699 static mword los_memory_usage = 0;
700 static mword los_num_objects = 0;
701 static mword next_los_collection = 2*1024*1024; /* 2 MB, need to tune */
702 static mword total_alloc = 0;
703 /* use this to tune when to do a major/minor collection */
704 static mword memory_pressure = 0;
706 static GCMemSection *nursery_section = NULL;
707 static mword lowest_heap_address = ~(mword)0;
708 static mword highest_heap_address = 0;
710 static LOCK_DECLARE (interruption_mutex);
712 typedef struct _FinalizeEntry FinalizeEntry;
713 struct _FinalizeEntry {
718 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
719 struct _FinalizeEntryHashTable {
720 FinalizeEntry **table;
725 typedef struct _DisappearingLink DisappearingLink;
726 struct _DisappearingLink {
727 DisappearingLink *next;
731 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
732 struct _DisappearingLinkHashTable {
733 DisappearingLink **table;
738 #define LARGE_INTERNAL_MEM_HEADER_MAGIC 0x7d289f3a
740 typedef struct _LargeInternalMemHeader LargeInternalMemHeader;
741 struct _LargeInternalMemHeader {
753 int current_collection_generation = -1;
756 * The link pointer is hidden by negating each bit. We use the lowest
757 * bit of the link (before negation) to store whether it needs
758 * resurrection tracking.
760 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
761 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
763 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
764 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
767 * The finalizable hash has the object as the key, the
768 * disappearing_link hash, has the link address as key.
770 static FinalizeEntryHashTable minor_finalizable_hash;
771 static FinalizeEntryHashTable major_finalizable_hash;
772 /* objects that are ready to be finalized */
773 static FinalizeEntry *fin_ready_list = NULL;
774 static FinalizeEntry *critical_fin_list = NULL;
776 static DisappearingLinkHashTable minor_disappearing_link_hash;
777 static DisappearingLinkHashTable major_disappearing_link_hash;
779 static int num_ready_finalizers = 0;
780 static int no_finalize = 0;
782 /* keep each size a multiple of ALLOC_ALIGN */
783 /* on 64 bit systems 8 is likely completely unused. */
784 static const int freelist_sizes [] = {
785 8, 16, 24, 32, 40, 48, 64, 80,
786 96, 128, 160, 192, 224, 256, 320, 384,
787 448, 512, 584, 680, 816, 1024, 1360, 2048};
788 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
790 /* This is also the MAJOR_SECTION_SIZE for the copying major
792 #define PINNED_CHUNK_SIZE (128 * 1024)
794 /* internal_chunk_list is used for allocating structures needed by the GC */
795 static PinnedChunk *internal_chunk_list = NULL;
797 static int slot_for_size (size_t size);
800 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
801 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
802 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
806 /* registered roots: the key to the hash is the root start address */
808 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
810 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
811 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
812 static mword roots_size = 0; /* amount of memory in the root set */
813 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
816 * The current allocation cursors
817 * We allocate objects in the nursery.
818 * The nursery is the area between nursery_start and nursery_real_end.
819 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
820 * from nursery fragments.
821 * tlab_next is the pointer to the space inside the TLAB where the next object will
823 * tlab_temp_end is the pointer to the end of the temporary space reserved for
824 * the allocation: it allows us to set the scan starts at reasonable intervals.
825 * tlab_real_end points to the end of the TLAB.
826 * nursery_frag_real_end points to the end of the currently used nursery fragment.
827 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
828 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
829 * At the next allocation, the area of the nursery where objects can be present is
830 * between MIN(nursery_first_pinned_start, first_fragment_start) and
831 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
833 static char *nursery_start = NULL;
835 /* eventually share with MonoThread? */
836 typedef struct _SgenThreadInfo SgenThreadInfo;
838 struct _SgenThreadInfo {
839 SgenThreadInfo *next;
841 unsigned int stop_count; /* to catch duplicate signals */
844 volatile int in_critical_region;
847 void *stack_start_limit;
848 char **tlab_next_addr;
849 char **tlab_start_addr;
850 char **tlab_temp_end_addr;
851 char **tlab_real_end_addr;
852 gpointer **store_remset_buffer_addr;
853 long *store_remset_buffer_index_addr;
854 RememberedSet *remset;
855 gpointer runtime_data;
856 gpointer stopped_ip; /* only valid if the thread is stopped */
857 MonoDomain *stopped_domain; /* ditto */
858 gpointer *stopped_regs; /* ditto */
859 #ifndef HAVE_KW_THREAD
864 gpointer *store_remset_buffer;
865 long store_remset_buffer_index;
869 #ifdef HAVE_KW_THREAD
870 #define TLAB_ACCESS_INIT
871 #define TLAB_START tlab_start
872 #define TLAB_NEXT tlab_next
873 #define TLAB_TEMP_END tlab_temp_end
874 #define TLAB_REAL_END tlab_real_end
875 #define REMEMBERED_SET remembered_set
876 #define STORE_REMSET_BUFFER store_remset_buffer
877 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
878 #define IN_CRITICAL_REGION thread_info->in_critical_region
880 static pthread_key_t thread_info_key;
881 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
882 #define TLAB_START (__thread_info__->tlab_start)
883 #define TLAB_NEXT (__thread_info__->tlab_next)
884 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
885 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
886 #define REMEMBERED_SET (__thread_info__->remset)
887 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
888 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
889 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
892 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
893 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
894 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
897 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
898 * variables for next+temp_end ?
900 #ifdef HAVE_KW_THREAD
901 static __thread SgenThreadInfo *thread_info;
902 static __thread char *tlab_start;
903 static __thread char *tlab_next;
904 static __thread char *tlab_temp_end;
905 static __thread char *tlab_real_end;
906 static __thread gpointer *store_remset_buffer;
907 static __thread long store_remset_buffer_index;
908 /* Used by the managed allocator/wbarrier */
909 static __thread char **tlab_next_addr;
910 static __thread char *stack_end;
911 static __thread long *store_remset_buffer_index_addr;
913 static char *nursery_next = NULL;
914 static char *nursery_frag_real_end = NULL;
915 static char *nursery_real_end = NULL;
916 static char *nursery_last_pinned_end = NULL;
918 /* The size of a TLAB */
919 /* The bigger the value, the less often we have to go to the slow path to allocate a new
920 * one, but the more space is wasted by threads not allocating much memory.
922 * FIXME: Make this self-tuning for each thread.
924 static guint32 tlab_size = (1024 * 4);
926 /* fragments that are free and ready to be used for allocation */
927 static Fragment *nursery_fragments = NULL;
928 /* freeelist of fragment structures */
929 static Fragment *fragment_freelist = NULL;
931 /* objects bigger then this go into the large object space */
932 #define MAX_SMALL_OBJ_SIZE 2040
934 /* Functions supplied by the runtime to be called by the GC */
935 static MonoGCCallbacks gc_callbacks;
937 #define ALLOC_ALIGN 8
938 #define ALLOC_ALIGN_BITS 3
940 #define MOVED_OBJECTS_NUM 64
941 static void *moved_objects [MOVED_OBJECTS_NUM];
942 static int moved_objects_idx = 0;
945 * ######################################################################
946 * ######## Macros and function declarations.
947 * ######################################################################
950 #define UPDATE_HEAP_BOUNDARIES(low,high) do { \
951 if ((mword)(low) < lowest_heap_address) \
952 lowest_heap_address = (mword)(low); \
953 if ((mword)(high) > highest_heap_address) \
954 highest_heap_address = (mword)(high); \
956 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
959 align_pointer (void *ptr)
961 mword p = (mword)ptr;
962 p += sizeof (gpointer) - 1;
963 p &= ~ (sizeof (gpointer) - 1);
967 typedef void (*CopyOrMarkObjectFunc) (void**);
968 typedef char* (*ScanObjectFunc) (char*);
970 /* forward declarations */
971 static void* get_internal_mem (size_t size, int type);
972 static void free_internal_mem (void *addr, int type);
973 static void* get_os_memory (size_t size, int activate);
974 static void* get_os_memory_aligned (mword size, mword alignment, gboolean activate);
975 static void free_os_memory (void *addr, size_t size);
976 static G_GNUC_UNUSED void report_internal_mem_usage (void);
978 static int stop_world (void);
979 static int restart_world (void);
980 static void add_to_global_remset (gpointer ptr, gboolean root);
981 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
982 static void scan_from_remsets (void *start_nursery, void *end_nursery);
983 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type);
984 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list);
985 static void find_pinning_ref_from_thread (char *obj, size_t size);
986 static void update_current_thread_stack (void *start);
987 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
988 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
989 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
990 static void null_links_for_domain (MonoDomain *domain, int generation);
991 static gboolean search_fragment_for_size (size_t size);
992 static void build_nursery_fragments (int start_pin, int end_pin);
993 static void clear_nursery_fragments (char *next);
994 static void pin_from_roots (void *start_nursery, void *end_nursery);
995 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery);
996 static void pin_objects_in_section (GCMemSection *section);
997 static void optimize_pin_queue (int start_slot);
998 static void clear_remsets (void);
999 static void clear_tlabs (void);
1000 typedef void (*IterateObjectCallbackFunc) (char*, size_t, void*);
1001 static void scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data);
1002 static char* scan_object (char *start);
1003 static char* major_scan_object (char *start);
1004 static void* copy_object_no_checks (void *obj);
1005 static void copy_object (void **obj_slot);
1006 static void* get_chunk_freelist (PinnedChunk *chunk, int slot);
1007 static PinnedChunk* alloc_pinned_chunk (void);
1008 static void free_large_object (LOSObject *obj);
1009 static void sort_addresses (void **array, int size);
1010 static void drain_gray_stack (void);
1011 static void finish_gray_stack (char *start_addr, char *end_addr, int generation);
1013 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
1015 void describe_ptr (char *ptr);
1016 static void check_consistency (void);
1017 static void check_section_scan_starts (GCMemSection *section);
1018 static void check_scan_starts (void);
1019 static void check_for_xdomain_refs (void);
1020 static void dump_occupied (char *start, char *end, char *section_start);
1021 static void dump_section (GCMemSection *section, const char *type);
1022 static void dump_heap (const char *type, int num, const char *reason);
1023 static void commit_stats (int generation);
1024 static void report_pinned_chunk (PinnedChunk *chunk, int seq);
1026 void mono_gc_scan_for_specific_ref (MonoObject *key);
1028 static void init_stats (void);
1030 //#define BINARY_PROTOCOL
1031 #include "sgen-protocol.c"
1032 #include "sgen-pinning.c"
1033 #include "sgen-pinning-stats.c"
1034 #include "sgen-gray.c"
1037 * ######################################################################
1038 * ######## GC descriptors
1039 * ######################################################################
1040 * Used to quickly get the info the GC needs about an object: size and
1041 * where the references are held.
1043 /* objects are aligned to 8 bytes boundaries
1044 * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
1045 * The low 3 bits define the type of the descriptor. The other bits
1046 * depend on the type.
1047 * As a general rule the 13 remaining low bits define the size, either
1048 * of the whole object or of the elements in the arrays. While for objects
1049 * the size is already in bytes, for arrays we need to shift, because
1050 * array elements might be smaller than 8 bytes. In case of arrays, we
1051 * use two bits to describe what the additional high bits represents,
1052 * so the default behaviour can handle element sizes less than 2048 bytes.
1053 * The high 16 bits, if 0 it means the object is pointer-free.
1054 * This design should make it easy and fast to skip over ptr-free data.
1055 * The first 4 types should cover >95% of the objects.
1056 * Note that since the size of objects is limited to 64K, larger objects
1057 * will be allocated in the large object heap.
1058 * If we want 4-bytes alignment, we need to put vector and small bitmap
1062 DESC_TYPE_RUN_LENGTH, /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
1063 DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
1064 DESC_TYPE_STRING, /* nothing */
1065 DESC_TYPE_COMPLEX, /* index for bitmap into complex_descriptors */
1066 DESC_TYPE_VECTOR, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1067 DESC_TYPE_ARRAY, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1068 DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
1069 DESC_TYPE_COMPLEX_ARR, /* index for bitmap into complex_descriptors */
1070 /* subtypes for arrays and vectors */
1071 DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value */
1072 DESC_TYPE_V_REFS, /* all the array elements are refs */
1073 DESC_TYPE_V_RUN_LEN, /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
1074 DESC_TYPE_V_BITMAP /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
1077 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
1078 #define LOW_TYPE_BITS 3
1079 #define SMALL_BITMAP_SHIFT 16
1080 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
1081 #define VECTOR_INFO_SHIFT 14
1082 #define VECTOR_ELSIZE_SHIFT 3
1083 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
1084 #define MAX_ELEMENT_SIZE 0x3ff
1085 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
1086 #define VECTOR_SUBTYPE_REFS (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
1087 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
1088 #define VECTOR_SUBTYPE_BITMAP (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
1091 /* Root bitmap descriptors are simpler: the lower three bits describe the type
1092 * and we either have 30/62 bitmap bits or nibble-based run-length,
1093 * or a complex descriptor, or a user defined marker function.
1096 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
1101 ROOT_DESC_TYPE_MASK = 0x7,
1102 ROOT_DESC_TYPE_SHIFT = 3,
1105 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
1107 #define MAX_USER_DESCRIPTORS 16
1109 static gsize* complex_descriptors = NULL;
1110 static int complex_descriptors_size = 0;
1111 static int complex_descriptors_next = 0;
1112 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
1113 static int user_descriptors_next = 0;
1116 alloc_complex_descriptor (gsize *bitmap, int numbits)
1120 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
1121 nwords = numbits / GC_BITS_PER_WORD + 1;
1124 res = complex_descriptors_next;
1125 /* linear search, so we don't have duplicates with domain load/unload
1126 * this should not be performance critical or we'd have bigger issues
1127 * (the number and size of complex descriptors should be small).
1129 for (i = 0; i < complex_descriptors_next; ) {
1130 if (complex_descriptors [i] == nwords) {
1131 int j, found = TRUE;
1132 for (j = 0; j < nwords - 1; ++j) {
1133 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
1143 i += complex_descriptors [i];
1145 if (complex_descriptors_next + nwords > complex_descriptors_size) {
1146 int new_size = complex_descriptors_size * 2 + nwords;
1147 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
1148 complex_descriptors_size = new_size;
1150 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
1151 complex_descriptors_next += nwords;
1152 complex_descriptors [res] = nwords;
1153 for (i = 0; i < nwords - 1; ++i) {
1154 complex_descriptors [res + 1 + i] = bitmap [i];
1155 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
1162 * Descriptor builders.
1165 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
1167 return (void*) DESC_TYPE_STRING;
1171 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
1173 int first_set = -1, num_set = 0, last_set = -1, i;
1175 size_t stored_size = obj_size;
1176 stored_size += ALLOC_ALIGN - 1;
1177 stored_size &= ~(ALLOC_ALIGN - 1);
1178 for (i = 0; i < numbits; ++i) {
1179 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1186 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
1187 /* check run-length encoding first: one byte offset, one byte number of pointers
1188 * on 64 bit archs, we can have 3 runs, just one on 32.
1189 * It may be better to use nibbles.
1191 if (first_set < 0) {
1192 desc = DESC_TYPE_RUN_LENGTH | stored_size;
1193 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1194 return (void*) desc;
1195 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1196 desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
1197 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));
1198 return (void*) desc;
1200 /* we know the 2-word header is ptr-free */
1201 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1202 desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1203 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1204 return (void*) desc;
1207 /* we know the 2-word header is ptr-free */
1208 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1209 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1210 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1211 return (void*) desc;
1213 /* it's a complex object ... */
1214 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1215 return (void*) desc;
1218 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1220 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1222 int first_set = -1, num_set = 0, last_set = -1, i;
1223 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1224 for (i = 0; i < numbits; ++i) {
1225 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1232 if (elem_size <= MAX_ELEMENT_SIZE) {
1233 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1235 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1237 /* Note: we also handle structs with just ref fields */
1238 if (num_set * sizeof (gpointer) == elem_size) {
1239 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1241 /* FIXME: try run-len first */
1242 /* Note: we can't skip the object header here, because it's not present */
1243 if (last_set <= SMALL_BITMAP_SIZE) {
1244 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1247 /* it's am array of complex structs ... */
1248 desc = DESC_TYPE_COMPLEX_ARR;
1249 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1250 return (void*) desc;
1253 /* Return the bitmap encoded by a descriptor */
1255 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1257 mword d = (mword)descr;
1261 case DESC_TYPE_RUN_LENGTH: {
1262 int first_set = (d >> 16) & 0xff;
1263 int num_set = (d >> 24) & 0xff;
1266 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1268 for (i = first_set; i < first_set + num_set; ++i)
1269 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1271 *numbits = first_set + num_set;
1275 case DESC_TYPE_SMALL_BITMAP:
1276 bitmap = g_new0 (gsize, 1);
1278 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1280 *numbits = GC_BITS_PER_WORD;
1284 g_assert_not_reached ();
1288 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
1289 #define STRING_SIZE(size,str) do { \
1290 (size) = sizeof (MonoString) + 2 * mono_string_length ((MonoString*)(str)) + 2; \
1291 (size) += (ALLOC_ALIGN - 1); \
1292 (size) &= ~(ALLOC_ALIGN - 1); \
1295 #define OBJ_RUN_LEN_SIZE(size,desc,obj) do { \
1296 (size) = (desc) & 0xfff8; \
1299 #define OBJ_BITMAP_SIZE(size,desc,obj) do { \
1300 (size) = (desc) & 0xfff8; \
1303 //#define PREFETCH(addr) __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(addr)))
1304 #define PREFETCH(addr)
1306 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
1307 #define OBJ_RUN_LEN_FOREACH_PTR(desc,obj) do { \
1308 if ((desc) & 0xffff0000) { \
1309 /* there are pointers */ \
1310 void **_objptr_end; \
1311 void **_objptr = (void**)(obj); \
1312 _objptr += ((desc) >> 16) & 0xff; \
1313 _objptr_end = _objptr + (((desc) >> 24) & 0xff); \
1314 while (_objptr < _objptr_end) { \
1315 HANDLE_PTR (_objptr, (obj)); \
1321 /* a bitmap desc means that there are pointer references or we'd have
1322 * choosen run-length, instead: add an assert to check.
1324 #define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
1325 /* there are pointers */ \
1326 void **_objptr = (void**)(obj); \
1327 gsize _bmap = (desc) >> 16; \
1328 _objptr += OBJECT_HEADER_WORDS; \
1330 if ((_bmap & 1)) { \
1331 HANDLE_PTR (_objptr, (obj)); \
1338 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj) do { \
1339 /* there are pointers */ \
1340 void **_objptr = (void**)(obj); \
1341 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS; \
1342 _objptr += OBJECT_HEADER_WORDS; \
1344 if ((_bmap & 1)) { \
1345 HANDLE_PTR (_objptr, (obj)); \
1352 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do { \
1353 /* there are pointers */ \
1354 void **_objptr = (void**)(obj); \
1355 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1356 int bwords = (*bitmap_data) - 1; \
1357 void **start_run = _objptr; \
1360 MonoObject *myobj = (MonoObject*)obj; \
1361 g_print ("found %d at %p (0x%zx): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1363 while (bwords-- > 0) { \
1364 gsize _bmap = *bitmap_data++; \
1365 _objptr = start_run; \
1366 /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/ \
1368 if ((_bmap & 1)) { \
1369 HANDLE_PTR (_objptr, (obj)); \
1374 start_run += GC_BITS_PER_WORD; \
1378 /* this one is untested */
1379 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj) do { \
1380 /* there are pointers */ \
1381 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1382 int mbwords = (*mbitmap_data++) - 1; \
1383 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass); \
1384 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1385 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1387 MonoObject *myobj = (MonoObject*)start; \
1388 g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1390 while (e_start < e_end) { \
1391 void **_objptr = (void**)e_start; \
1392 gsize *bitmap_data = mbitmap_data; \
1393 unsigned int bwords = mbwords; \
1394 while (bwords-- > 0) { \
1395 gsize _bmap = *bitmap_data++; \
1396 void **start_run = _objptr; \
1397 /*g_print ("bitmap: 0x%x\n", _bmap);*/ \
1399 if ((_bmap & 1)) { \
1400 HANDLE_PTR (_objptr, (obj)); \
1405 _objptr = start_run + GC_BITS_PER_WORD; \
1407 e_start += el_size; \
1411 #define OBJ_VECTOR_FOREACH_PTR(vt,obj) do { \
1412 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */ \
1413 if ((vt)->desc & 0xffffc000) { \
1414 int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE; \
1415 /* there are pointers */ \
1416 int etype = (vt)->desc & 0xc000; \
1417 if (etype == (DESC_TYPE_V_REFS << 14)) { \
1418 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector)); \
1419 void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
1420 /* Note: this code can handle also arrays of struct with only references in them */ \
1421 while (p < end_refs) { \
1422 HANDLE_PTR (p, (obj)); \
1425 } else if (etype == DESC_TYPE_V_RUN_LEN << 14) { \
1426 int offset = ((vt)->desc >> 16) & 0xff; \
1427 int num_refs = ((vt)->desc >> 24) & 0xff; \
1428 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1429 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1430 while (e_start < e_end) { \
1431 void **p = (void**)e_start; \
1434 for (i = 0; i < num_refs; ++i) { \
1435 HANDLE_PTR (p + i, (obj)); \
1437 e_start += el_size; \
1439 } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
1440 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1441 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1442 while (e_start < e_end) { \
1443 void **p = (void**)e_start; \
1444 gsize _bmap = (vt)->desc >> 16; \
1445 /* Note: there is no object header here to skip */ \
1447 if ((_bmap & 1)) { \
1448 HANDLE_PTR (p, (obj)); \
1453 e_start += el_size; \
1459 //#include "sgen-major-copying.c"
1460 #include "sgen-marksweep.c"
1463 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1465 MonoObject *o = (MonoObject*)(obj);
1466 MonoObject *ref = (MonoObject*)*(ptr);
1467 int offset = (char*)(ptr) - (char*)o;
1469 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1471 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1473 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1474 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1476 /* Thread.cached_culture_info */
1477 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1478 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1479 !strcmp(o->vtable->klass->name_space, "System") &&
1480 !strcmp(o->vtable->klass->name, "Object[]"))
1483 * 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
1484 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1485 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1486 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1487 * 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
1488 * 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
1489 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1490 * 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
1491 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1493 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1494 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1495 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1496 !strcmp (o->vtable->klass->name, "MemoryStream"))
1498 /* append_job() in threadpool.c */
1499 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1500 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1501 !strcmp (o->vtable->klass->name_space, "System") &&
1502 !strcmp (o->vtable->klass->name, "Object[]") &&
1503 mono_thread_pool_is_queue_array ((MonoArray*) o))
1509 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1511 MonoObject *o = (MonoObject*)(obj);
1512 MonoObject *ref = (MonoObject*)*(ptr);
1513 int offset = (char*)(ptr) - (char*)o;
1515 MonoClassField *field;
1518 if (!ref || ref->vtable->domain == domain)
1520 if (is_xdomain_ref_allowed (ptr, obj, domain))
1524 for (class = o->vtable->klass; class; class = class->parent) {
1527 for (i = 0; i < class->field.count; ++i) {
1528 if (class->fields[i].offset == offset) {
1529 field = &class->fields[i];
1537 if (ref->vtable->klass == mono_defaults.string_class)
1538 str = mono_string_to_utf8 ((MonoString*)ref);
1541 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1542 o, o->vtable->klass->name_space, o->vtable->klass->name,
1543 offset, field ? field->name : "",
1544 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1545 mono_gc_scan_for_specific_ref (o);
1551 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1554 scan_object_for_xdomain_refs (char *start)
1556 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1558 #include "sgen-scan-object.h"
1564 scan_area_for_xdomain_refs (char *start, char *end)
1566 while (start < end) {
1567 if (!*(void**)start) {
1568 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1572 start = scan_object_for_xdomain_refs (start);
1577 #define HANDLE_PTR(ptr,obj) do { \
1578 if ((MonoObject*)*(ptr) == key) { \
1579 g_print ("found ref to %p in object %p (%s) at offset %zd\n", \
1580 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1585 scan_object_for_specific_ref (char *start, MonoObject *key)
1587 #include "sgen-scan-object.h"
1593 scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data)
1595 while (start < end) {
1597 if (!*(void**)start) {
1598 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1602 size = safe_object_get_size ((MonoObject*) start);
1603 size += ALLOC_ALIGN - 1;
1604 size &= ~(ALLOC_ALIGN - 1);
1606 callback (start, size, data);
1613 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1615 scan_object_for_specific_ref (obj, key);
1619 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1623 g_print ("found ref to %p in root record %p\n", key, root);
1626 static MonoObject *check_key = NULL;
1627 static RootRecord *check_root = NULL;
1630 check_root_obj_specific_ref_from_marker (void **obj)
1632 check_root_obj_specific_ref (check_root, check_key, *obj);
1636 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1641 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1642 for (root = roots_hash [root_type][i]; root; root = root->next) {
1643 void **start_root = (void**)root->start_root;
1644 mword desc = root->root_desc;
1648 switch (desc & ROOT_DESC_TYPE_MASK) {
1649 case ROOT_DESC_BITMAP:
1650 desc >>= ROOT_DESC_TYPE_SHIFT;
1653 check_root_obj_specific_ref (root, key, *start_root);
1658 case ROOT_DESC_COMPLEX: {
1659 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1660 int bwords = (*bitmap_data) - 1;
1661 void **start_run = start_root;
1663 while (bwords-- > 0) {
1664 gsize bmap = *bitmap_data++;
1665 void **objptr = start_run;
1668 check_root_obj_specific_ref (root, key, *objptr);
1672 start_run += GC_BITS_PER_WORD;
1676 case ROOT_DESC_USER: {
1677 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1678 marker (start_root, check_root_obj_specific_ref_from_marker);
1681 case ROOT_DESC_RUN_LEN:
1682 g_assert_not_reached ();
1684 g_assert_not_reached ();
1693 mono_gc_scan_for_specific_ref (MonoObject *key)
1699 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1700 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1702 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1704 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1705 scan_object_for_specific_ref (bigobj->data, key);
1707 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1708 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1710 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1711 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1712 void **ptr = (void**)root->start_root;
1714 while (ptr < (void**)root->end_root) {
1715 check_root_obj_specific_ref (root, *ptr, key);
1722 /* Clear all remaining nursery fragments */
1724 clear_nursery_fragments (char *next)
1727 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1728 g_assert (next <= nursery_frag_real_end);
1729 memset (next, 0, nursery_frag_real_end - next);
1730 for (frag = nursery_fragments; frag; frag = frag->next) {
1731 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1737 need_remove_object_for_domain (char *start, MonoDomain *domain)
1739 if (mono_object_domain (start) == domain) {
1740 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1741 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1748 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1750 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1751 if (vt->klass == mono_defaults.internal_thread_class)
1752 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1753 /* The object could be a proxy for an object in the domain
1755 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1756 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1758 /* The server could already have been zeroed out, so
1759 we need to check for that, too. */
1760 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1761 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1763 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1768 static MonoDomain *check_domain = NULL;
1771 check_obj_not_in_domain (void **o)
1773 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1777 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1781 check_domain = domain;
1782 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1783 for (root = roots_hash [root_type][i]; root; root = root->next) {
1784 void **start_root = (void**)root->start_root;
1785 mword desc = root->root_desc;
1787 /* The MonoDomain struct is allowed to hold
1788 references to objects in its own domain. */
1789 if (start_root == (void**)domain)
1792 switch (desc & ROOT_DESC_TYPE_MASK) {
1793 case ROOT_DESC_BITMAP:
1794 desc >>= ROOT_DESC_TYPE_SHIFT;
1796 if ((desc & 1) && *start_root)
1797 check_obj_not_in_domain (*start_root);
1802 case ROOT_DESC_COMPLEX: {
1803 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1804 int bwords = (*bitmap_data) - 1;
1805 void **start_run = start_root;
1807 while (bwords-- > 0) {
1808 gsize bmap = *bitmap_data++;
1809 void **objptr = start_run;
1811 if ((bmap & 1) && *objptr)
1812 check_obj_not_in_domain (*objptr);
1816 start_run += GC_BITS_PER_WORD;
1820 case ROOT_DESC_USER: {
1821 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1822 marker (start_root, check_obj_not_in_domain);
1825 case ROOT_DESC_RUN_LEN:
1826 g_assert_not_reached ();
1828 g_assert_not_reached ();
1832 check_domain = NULL;
1836 scan_pinned_object_for_xdomain_refs_callback (char *obj, size_t size, gpointer dummy)
1838 scan_object_for_xdomain_refs (obj);
1842 check_for_xdomain_refs (void)
1846 scan_area_for_xdomain_refs (nursery_section->data, nursery_section->end_data);
1848 major_iterate_objects (TRUE, TRUE, scan_pinned_object_for_xdomain_refs_callback, NULL);
1850 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1851 scan_object_for_xdomain_refs (bigobj->data);
1855 clear_domain_process_object (char *obj, MonoDomain *domain)
1859 process_object_for_domain_clearing (obj, domain);
1860 remove = need_remove_object_for_domain (obj, domain);
1862 if (remove && ((MonoObject*)obj)->synchronisation) {
1863 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1865 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1872 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1874 if (clear_domain_process_object (obj, domain))
1875 memset (obj, 0, size);
1879 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1881 clear_domain_process_object (obj, domain);
1885 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1887 if (need_remove_object_for_domain (obj, domain))
1888 major_free_non_pinned_object (obj, size);
1892 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1894 if (need_remove_object_for_domain (obj, domain))
1895 free_pinned_object (obj, size);
1899 * When appdomains are unloaded we can easily remove objects that have finalizers,
1900 * but all the others could still be present in random places on the heap.
1901 * We need a sweep to get rid of them even though it's going to be costly
1903 * The reason we need to remove them is because we access the vtable and class
1904 * structures to know the object size and the reference bitmap: once the domain is
1905 * unloaded the point to random memory.
1908 mono_gc_clear_domain (MonoDomain * domain)
1910 LOSObject *bigobj, *prev;
1915 clear_nursery_fragments (nursery_next);
1917 if (xdomain_checks && domain != mono_get_root_domain ()) {
1918 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1919 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1920 check_for_xdomain_refs ();
1923 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1924 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain);
1926 /* We need two passes over major and large objects because
1927 freeing such objects might give their memory back to the OS
1928 (in the case of large objects) or obliterate its vtable
1929 (pinned objects with major-copying or pinned and non-pinned
1930 objects with major-mark&sweep), but we might need to
1931 dereference a pointer from an object to another object if
1932 the first object is a proxy. */
1933 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1934 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1935 clear_domain_process_object (bigobj->data, domain);
1938 for (bigobj = los_object_list; bigobj;) {
1939 if (need_remove_object_for_domain (bigobj->data, domain)) {
1940 LOSObject *to_free = bigobj;
1942 prev->next = bigobj->next;
1944 los_object_list = bigobj->next;
1945 bigobj = bigobj->next;
1946 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1948 free_large_object (to_free);
1952 bigobj = bigobj->next;
1954 major_iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1955 major_iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1957 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1958 null_links_for_domain (domain, i);
1964 * add_to_global_remset:
1966 * The global remset contains locations which point into newspace after
1967 * a minor collection. This can happen if the objects they point to are pinned.
1970 add_to_global_remset (gpointer ptr, gboolean root)
1974 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1975 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1978 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1980 HEAVY_STAT (++stat_global_remsets_added);
1983 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1984 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1986 if (global_remset->store_next + 3 < global_remset->end_set) {
1988 *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
1989 *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
1991 *(global_remset->store_next++) = (mword)ptr;
1995 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1996 rs->next = global_remset;
1999 *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
2000 *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
2002 *(global_remset->store_next++) = (mword)ptr;
2006 int global_rs_size = 0;
2008 for (rs = global_remset; rs; rs = rs->next) {
2009 global_rs_size += rs->store_next - rs->data;
2011 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
2016 * FIXME: allocate before calling this function and pass the
2017 * destination address.
2020 copy_object_no_checks (void *obj)
2022 static const void *copy_labels [] = { &&LAB_0, &&LAB_1, &&LAB_2, &&LAB_3, &&LAB_4, &&LAB_5, &&LAB_6, &&LAB_7, &&LAB_8 };
2026 MonoVTable *vt = ((MonoObject*)obj)->vtable;
2027 gboolean has_references = vt->klass->has_references;
2029 objsize = safe_object_get_size ((MonoObject*)obj);
2030 objsize += ALLOC_ALIGN - 1;
2031 objsize &= ~(ALLOC_ALIGN - 1);
2033 DEBUG (9, g_assert (vt->klass->inited));
2034 MAJOR_GET_COPY_OBJECT_SPACE (destination, objsize, has_references);
2036 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %zd)\n", destination, ((MonoObject*)obj)->vtable->klass->name, objsize));
2037 binary_protocol_copy (obj, destination, ((MonoObject*)obj)->vtable, objsize);
2039 if (objsize <= sizeof (gpointer) * 8) {
2040 mword *dest = (mword*)destination;
2041 goto *copy_labels [objsize / sizeof (gpointer)];
2043 (dest) [7] = ((mword*)obj) [7];
2045 (dest) [6] = ((mword*)obj) [6];
2047 (dest) [5] = ((mword*)obj) [5];
2049 (dest) [4] = ((mword*)obj) [4];
2051 (dest) [3] = ((mword*)obj) [3];
2053 (dest) [2] = ((mword*)obj) [2];
2055 (dest) [1] = ((mword*)obj) [1];
2057 (dest) [0] = ((mword*)obj) [0];
2065 char* edi = destination;
2066 __asm__ __volatile__(
2068 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
2069 : "0" (objsize/4), "1" (edi),"2" (esi)
2074 memcpy (destination, obj, objsize);
2077 /* adjust array->bounds */
2078 DEBUG (9, g_assert (vt->gc_descr));
2079 if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
2080 MonoArray *array = (MonoArray*)destination;
2081 array->bounds = (MonoArrayBounds*)((char*)destination + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
2082 DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %zd, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
2084 /* set the forwarding pointer */
2085 forward_object (obj, destination);
2086 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
2087 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2088 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2089 moved_objects_idx = 0;
2091 moved_objects [moved_objects_idx++] = obj;
2092 moved_objects [moved_objects_idx++] = destination;
2095 if (has_references) {
2096 DEBUG (9, fprintf (gc_debug_file, "Enqueuing gray object %p (%s)\n", obj, safe_name (obj)));
2097 GRAY_OBJECT_ENQUEUE (obj);
2103 * This is how the copying happens from the nursery to the old generation.
2104 * We assume that at this time all the pinned objects have been identified and
2106 * We run scan_object() for each pinned object so that each referenced
2107 * objects if possible are copied. The new gray objects created can have
2108 * scan_object() run on them right away, too.
2109 * Then we run copy_object() for the precisely tracked roots. At this point
2110 * all the roots are either gray or black. We run scan_object() on the gray
2111 * objects until no more gray objects are created.
2112 * At the end of the process we walk again the pinned list and we unmark
2113 * the pinned flag. As we go we also create the list of free space for use
2114 * in the next allocation runs.
2116 * We need to remember objects from the old generation that point to the new one
2117 * (or just addresses?).
2119 * copy_object could be made into a macro once debugged (use inline for now).
2122 static void __attribute__((noinline))
2123 copy_object (void **obj_slot)
2126 char *obj = *obj_slot;
2128 DEBUG (9, g_assert (current_collection_generation == GENERATION_NURSERY));
2130 HEAVY_STAT (++stat_copy_object_called_nursery);
2132 if (!ptr_in_nursery (obj)) {
2133 HEAVY_STAT (++stat_nursery_copy_object_failed_from_space);
2137 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
2140 * Before we can copy the object we must make sure that we are
2141 * allowed to, i.e. that the object not pinned or not already
2145 if ((forwarded = object_is_forwarded (obj))) {
2146 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2147 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
2148 HEAVY_STAT (++stat_nursery_copy_object_failed_forwarded);
2149 *obj_slot = forwarded;
2152 if (object_is_pinned (obj)) {
2153 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2154 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
2155 HEAVY_STAT (++stat_nursery_copy_object_failed_pinned);
2159 HEAVY_STAT (++stat_objects_copied_nursery);
2161 *obj_slot = copy_object_no_checks (obj);
2165 #define HANDLE_PTR(ptr,obj) do { \
2166 void *__old = *(ptr); \
2169 copy_object ((ptr)); \
2171 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2172 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2173 add_to_global_remset ((ptr), FALSE); \
2178 * Scan the object pointed to by @start for references to
2179 * other objects between @from_start and @from_end and copy
2180 * them to the gray_objects area.
2181 * Returns a pointer to the end of the object.
2184 scan_object (char *start)
2186 #include "sgen-scan-object.h"
2188 HEAVY_STAT (++stat_scan_object_called_nursery);
2196 * Scan the valuetype pointed to by START, described by DESC for references to
2197 * other objects between @from_start and @from_end and copy them to the gray_objects area.
2198 * Returns a pointer to the end of the object.
2201 scan_vtype (char *start, mword desc, char* from_start, char* from_end)
2205 /* The descriptors include info about the MonoObject header as well */
2206 start -= sizeof (MonoObject);
2208 switch (desc & 0x7) {
2209 case DESC_TYPE_RUN_LENGTH:
2210 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
2211 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
2212 g_assert (skip_size);
2213 return start + skip_size;
2214 case DESC_TYPE_SMALL_BITMAP:
2215 OBJ_BITMAP_FOREACH_PTR (desc,start);
2216 OBJ_BITMAP_SIZE (skip_size, desc, start);
2217 return start + skip_size;
2218 case DESC_TYPE_LARGE_BITMAP:
2219 case DESC_TYPE_COMPLEX:
2221 g_assert_not_reached ();
2224 // The other descriptors can't happen with vtypes
2225 g_assert_not_reached ();
2232 #define HANDLE_PTR(ptr,obj) do { \
2233 void *__old = *(ptr); \
2236 major_copy_or_mark_object ((ptr)); \
2238 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2239 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2240 add_to_global_remset ((ptr), FALSE); \
2245 major_scan_object (char *start)
2247 #include "sgen-scan-object.h"
2249 HEAVY_STAT (++stat_scan_object_called_major);
2257 * Scan objects in the gray stack until the stack is empty. This should be called
2258 * frequently after each object is copied, to achieve better locality and cache
2262 drain_gray_stack (void)
2266 if (current_collection_generation == GENERATION_NURSERY) {
2268 GRAY_OBJECT_DEQUEUE (obj);
2271 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2276 GRAY_OBJECT_DEQUEUE (obj);
2279 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2280 major_scan_object (obj);
2286 * Addresses from start to end are already sorted. This function finds
2287 * the object header for each address and pins the object. The
2288 * addresses must be inside the passed section. The (start of the)
2289 * address array is overwritten with the addresses of the actually
2290 * pinned objects. Return the number of pinned objects.
2293 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
2298 void *last_obj = NULL;
2299 size_t last_obj_size = 0;
2302 void **definitely_pinned = start;
2303 while (start < end) {
2305 /* the range check should be reduntant */
2306 if (addr != last && addr >= start_nursery && addr < end_nursery) {
2307 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
2308 /* multiple pointers to the same object */
2309 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
2313 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
2314 g_assert (idx < section->num_scan_start);
2315 search_start = (void*)section->scan_starts [idx];
2316 if (!search_start || search_start > addr) {
2319 search_start = section->scan_starts [idx];
2320 if (search_start && search_start <= addr)
2323 if (!search_start || search_start > addr)
2324 search_start = start_nursery;
2326 if (search_start < last_obj)
2327 search_start = (char*)last_obj + last_obj_size;
2328 /* now addr should be in an object a short distance from search_start
2329 * Note that search_start must point to zeroed mem or point to an object.
2332 if (!*(void**)search_start) {
2333 mword p = (mword)search_start;
2334 p += sizeof (gpointer);
2335 p += ALLOC_ALIGN - 1;
2336 p &= ~(ALLOC_ALIGN - 1);
2337 search_start = (void*)p;
2340 last_obj = search_start;
2341 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
2342 last_obj_size += ALLOC_ALIGN - 1;
2343 last_obj_size &= ~(ALLOC_ALIGN - 1);
2344 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
2345 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
2346 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));
2347 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
2348 pin_object (search_start);
2349 GRAY_OBJECT_ENQUEUE (search_start);
2351 pin_stats_register_object (search_start, last_obj_size);
2352 definitely_pinned [count] = search_start;
2356 /* skip to the next object */
2357 search_start = (void*)((char*)search_start + last_obj_size);
2358 } while (search_start <= addr);
2359 /* we either pinned the correct object or we ignored the addr because
2360 * it points to unused zeroed memory.
2366 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
2371 pin_objects_in_section (GCMemSection *section)
2373 int start = section->pin_queue_start;
2374 int end = section->pin_queue_end;
2377 reduced_to = pin_objects_from_addresses (section, pin_queue + start, pin_queue + end,
2378 section->data, section->next_data);
2379 section->pin_queue_start = start;
2380 section->pin_queue_end = start + reduced_to;
2387 gap = (gap * 10) / 13;
2388 if (gap == 9 || gap == 10)
2397 compare_addr (const void *a, const void *b)
2399 return *(const void **)a - *(const void **)b;
2403 /* sort the addresses in array in increasing order */
2405 sort_addresses (void **array, int size)
2408 * qsort is slower as predicted.
2409 * qsort (array, size, sizeof (gpointer), compare_addr);
2416 gap = new_gap (gap);
2419 for (i = 0; i < end; i++) {
2421 if (array [i] > array [j]) {
2422 void* val = array [i];
2423 array [i] = array [j];
2428 if (gap == 1 && !swapped)
2433 static G_GNUC_UNUSED void
2434 print_nursery_gaps (void* start_nursery, void *end_nursery)
2437 gpointer first = start_nursery;
2439 for (i = 0; i < next_pin_slot; ++i) {
2440 next = pin_queue [i];
2441 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2445 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2448 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
2450 optimize_pin_queue (int start_slot)
2452 void **start, **cur, **end;
2453 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
2454 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
2455 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
2456 if ((next_pin_slot - start_slot) > 1)
2457 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
2458 start = cur = pin_queue + start_slot;
2459 end = pin_queue + next_pin_slot;
2462 while (*start == *cur && cur < end)
2466 next_pin_slot = start - pin_queue;
2467 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2468 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2473 * Scan the memory between start and end and queue values which could be pointers
2474 * to the area between start_nursery and end_nursery for later consideration.
2475 * Typically used for thread stacks.
2478 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2481 while (start < end) {
2482 if (*start >= start_nursery && *start < end_nursery) {
2484 * *start can point to the middle of an object
2485 * note: should we handle pointing at the end of an object?
2486 * pinning in C# code disallows pointing at the end of an object
2487 * but there is some small chance that an optimizing C compiler
2488 * may keep the only reference to an object by pointing
2489 * at the end of it. We ignore this small chance for now.
2490 * Pointers to the end of an object are indistinguishable
2491 * from pointers to the start of the next object in memory
2492 * so if we allow that we'd need to pin two objects...
2493 * We queue the pointer in an array, the
2494 * array will then be sorted and uniqued. This way
2495 * we can coalesce several pinning pointers and it should
2496 * be faster since we'd do a memory scan with increasing
2497 * addresses. Note: we can align the address to the allocation
2498 * alignment, so the unique process is more effective.
2500 mword addr = (mword)*start;
2501 addr &= ~(ALLOC_ALIGN - 1);
2502 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2503 pin_stage_ptr ((void*)addr);
2505 pin_stats_register_address ((char*)addr, pin_type);
2506 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
2511 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2515 * Debugging function: find in the conservative roots where @obj is being pinned.
2517 static G_GNUC_UNUSED void
2518 find_pinning_reference (char *obj, size_t size)
2522 char *endobj = obj + size;
2523 for (i = 0; i < roots_hash_size [0]; ++i) {
2524 for (root = roots_hash [0][i]; root; root = root->next) {
2525 /* if desc is non-null it has precise info */
2526 if (!root->root_desc) {
2527 char ** start = (char**)root->start_root;
2528 while (start < (char**)root->end_root) {
2529 if (*start >= obj && *start < endobj) {
2530 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));
2537 find_pinning_ref_from_thread (obj, size);
2541 * The first thing we do in a collection is to identify pinned objects.
2542 * This function considers all the areas of memory that need to be
2543 * conservatively scanned.
2546 pin_from_roots (void *start_nursery, void *end_nursery)
2550 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]));
2551 /* objects pinned from the API are inside these roots */
2552 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2553 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2554 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2555 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2558 /* now deal with the thread stacks
2559 * in the future we should be able to conservatively scan only:
2560 * *) the cpu registers
2561 * *) the unmanaged stack frames
2562 * *) the _last_ managed stack frame
2563 * *) pointers slots in managed frames
2565 scan_thread_data (start_nursery, end_nursery, FALSE);
2567 evacuate_pin_staging_area ();
2571 * The memory area from start_root to end_root contains pointers to objects.
2572 * Their position is precisely described by @desc (this means that the pointer
2573 * can be either NULL or the pointer to the start of an object).
2574 * This functions copies them to to_space updates them.
2577 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
2579 switch (desc & ROOT_DESC_TYPE_MASK) {
2580 case ROOT_DESC_BITMAP:
2581 desc >>= ROOT_DESC_TYPE_SHIFT;
2583 if ((desc & 1) && *start_root) {
2584 copy_func (start_root);
2585 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2586 drain_gray_stack ();
2592 case ROOT_DESC_COMPLEX: {
2593 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2594 int bwords = (*bitmap_data) - 1;
2595 void **start_run = start_root;
2597 while (bwords-- > 0) {
2598 gsize bmap = *bitmap_data++;
2599 void **objptr = start_run;
2601 if ((bmap & 1) && *objptr) {
2603 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2604 drain_gray_stack ();
2609 start_run += GC_BITS_PER_WORD;
2613 case ROOT_DESC_USER: {
2614 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2615 marker (start_root, copy_func);
2618 case ROOT_DESC_RUN_LEN:
2619 g_assert_not_reached ();
2621 g_assert_not_reached ();
2626 alloc_fragment (void)
2628 Fragment *frag = fragment_freelist;
2630 fragment_freelist = frag->next;
2634 frag = get_internal_mem (sizeof (Fragment), INTERNAL_MEM_FRAGMENT);
2639 /* size must be a power of 2 */
2641 get_os_memory_aligned (mword size, mword alignment, gboolean activate)
2643 /* Allocate twice the memory to be able to put the block on an aligned address */
2644 char *mem = get_os_memory (size + alignment, activate);
2649 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2650 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2653 free_os_memory (mem, aligned - mem);
2654 if (aligned + size < mem + size + alignment)
2655 free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2661 * Allocate and setup the data structures needed to be able to allocate objects
2662 * in the nursery. The nursery is stored in nursery_section.
2665 alloc_nursery (void)
2667 GCMemSection *section;
2673 if (nursery_section)
2675 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %zd\n", nursery_size));
2676 /* later we will alloc a larger area for the nursery but only activate
2677 * what we need. The rest will be used as expansion if we have too many pinned
2678 * objects in the existing nursery.
2680 /* FIXME: handle OOM */
2681 section = get_internal_mem (SIZEOF_GC_MEM_SECTION, INTERNAL_MEM_SECTION);
2683 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2684 alloc_size = nursery_size;
2685 #ifdef ALIGN_NURSERY
2686 data = get_os_memory_aligned (alloc_size, alloc_size, TRUE);
2688 data = get_os_memory (alloc_size, TRUE);
2690 nursery_start = data;
2691 nursery_real_end = nursery_start + nursery_size;
2692 UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
2693 nursery_next = nursery_start;
2694 total_alloc += alloc_size;
2695 DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %zd, total: %zd\n", data, data + alloc_size, nursery_size, total_alloc));
2696 section->data = section->next_data = data;
2697 section->size = alloc_size;
2698 section->end_data = nursery_real_end;
2699 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2700 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2701 section->num_scan_start = scan_starts;
2702 section->block.role = MEMORY_ROLE_GEN0;
2703 section->block.next = NULL;
2705 nursery_section = section;
2707 /* Setup the single first large fragment */
2708 frag = alloc_fragment ();
2709 frag->fragment_start = nursery_start;
2710 frag->fragment_limit = nursery_start;
2711 frag->fragment_end = nursery_real_end;
2712 nursery_frag_real_end = nursery_real_end;
2713 /* FIXME: frag here is lost */
2717 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list) {
2720 for (fin = list; fin; fin = fin->next) {
2723 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2724 copy_func (&fin->object);
2728 static mword fragment_total = 0;
2730 * We found a fragment of free memory in the nursery: memzero it and if
2731 * it is big enough, add it to the list of fragments that can be used for
2735 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2738 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2739 binary_protocol_empty (frag_start, frag_size);
2740 /* memsetting just the first chunk start is bound to provide better cache locality */
2741 if (nursery_clear_policy == CLEAR_AT_GC)
2742 memset (frag_start, 0, frag_size);
2743 /* Not worth dealing with smaller fragments: need to tune */
2744 if (frag_size >= FRAGMENT_MIN_SIZE) {
2745 fragment = alloc_fragment ();
2746 fragment->fragment_start = frag_start;
2747 fragment->fragment_limit = frag_start;
2748 fragment->fragment_end = frag_end;
2749 fragment->next = nursery_fragments;
2750 nursery_fragments = fragment;
2751 fragment_total += frag_size;
2753 /* Clear unused fragments, pinning depends on this */
2754 memset (frag_start, 0, frag_size);
2759 generation_name (int generation)
2761 switch (generation) {
2762 case GENERATION_NURSERY: return "nursery";
2763 case GENERATION_OLD: return "old";
2764 default: g_assert_not_reached ();
2768 static DisappearingLinkHashTable*
2769 get_dislink_hash_table (int generation)
2771 switch (generation) {
2772 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2773 case GENERATION_OLD: return &major_disappearing_link_hash;
2774 default: g_assert_not_reached ();
2778 static FinalizeEntryHashTable*
2779 get_finalize_entry_hash_table (int generation)
2781 switch (generation) {
2782 case GENERATION_NURSERY: return &minor_finalizable_hash;
2783 case GENERATION_OLD: return &major_finalizable_hash;
2784 default: g_assert_not_reached ();
2789 finish_gray_stack (char *start_addr, char *end_addr, int generation)
2794 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? copy_object : major_copy_or_mark_object;
2797 * We copied all the reachable objects. Now it's the time to copy
2798 * the objects that were not referenced by the roots, but by the copied objects.
2799 * we built a stack of objects pointed to by gray_start: they are
2800 * additional roots and we may add more items as we go.
2801 * We loop until gray_start == gray_objects which means no more objects have
2802 * been added. Note this is iterative: no recursion is involved.
2803 * We need to walk the LO list as well in search of marked big objects
2804 * (use a flag since this is needed only on major collections). We need to loop
2805 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2806 * To achieve better cache locality and cache usage, we drain the gray stack
2807 * frequently, after each object is copied, and just finish the work here.
2809 drain_gray_stack ();
2811 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2812 /* walk the finalization queue and move also the objects that need to be
2813 * finalized: use the finalized objects as new roots so the objects they depend
2814 * on are also not reclaimed. As with the roots above, only objects in the nursery
2815 * are marked/copied.
2816 * We need a loop here, since objects ready for finalizers may reference other objects
2817 * that are fin-ready. Speedup with a flag?
2820 fin_ready = num_ready_finalizers;
2821 finalize_in_range (copy_func, start_addr, end_addr, generation);
2822 if (generation == GENERATION_OLD)
2823 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY);
2825 /* drain the new stack that might have been created */
2826 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2827 drain_gray_stack ();
2828 } while (fin_ready != num_ready_finalizers);
2830 DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan for %s generation: %d usecs\n", generation_name (generation), TV_ELAPSED (atv, btv)));
2833 * handle disappearing links
2834 * Note we do this after checking the finalization queue because if an object
2835 * survives (at least long enough to be finalized) we don't clear the link.
2836 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2837 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2840 g_assert (gray_object_queue_is_empty ());
2842 null_link_in_range (copy_func, start_addr, end_addr, generation);
2843 if (generation == GENERATION_OLD)
2844 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY);
2845 if (gray_object_queue_is_empty ())
2847 drain_gray_stack ();
2850 g_assert (gray_object_queue_is_empty ());
2854 check_section_scan_starts (GCMemSection *section)
2857 for (i = 0; i < section->num_scan_start; ++i) {
2858 if (section->scan_starts [i]) {
2859 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2860 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2866 check_scan_starts (void)
2868 if (!do_scan_starts_check)
2870 check_section_scan_starts (nursery_section);
2871 major_check_scan_starts ();
2874 static int last_num_pinned = 0;
2877 build_nursery_fragments (int start_pin, int end_pin)
2879 char *frag_start, *frag_end;
2883 while (nursery_fragments) {
2884 Fragment *next = nursery_fragments->next;
2885 nursery_fragments->next = fragment_freelist;
2886 fragment_freelist = nursery_fragments;
2887 nursery_fragments = next;
2889 frag_start = nursery_start;
2891 /* clear scan starts */
2892 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2893 for (i = start_pin; i < end_pin; ++i) {
2894 frag_end = pin_queue [i];
2895 /* remove the pin bit from pinned objects */
2896 unpin_object (frag_end);
2897 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2898 frag_size = frag_end - frag_start;
2900 add_nursery_frag (frag_size, frag_start, frag_end);
2901 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
2902 frag_size += ALLOC_ALIGN - 1;
2903 frag_size &= ~(ALLOC_ALIGN - 1);
2904 frag_start = (char*)pin_queue [i] + frag_size;
2906 nursery_last_pinned_end = frag_start;
2907 frag_end = nursery_real_end;
2908 frag_size = frag_end - frag_start;
2910 add_nursery_frag (frag_size, frag_start, frag_end);
2911 if (!nursery_fragments) {
2912 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
2913 for (i = start_pin; i < end_pin; ++i) {
2914 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])));
2919 nursery_next = nursery_frag_real_end = NULL;
2921 /* Clear TLABs for all threads */
2926 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type)
2930 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2931 for (root = roots_hash [root_type][i]; root; root = root->next) {
2932 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2933 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
2939 dump_occupied (char *start, char *end, char *section_start)
2941 fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
2945 dump_section (GCMemSection *section, const char *type)
2947 char *start = section->data;
2948 char *end = section->data + section->size;
2949 char *occ_start = NULL;
2951 char *old_start = NULL; /* just for debugging */
2953 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", type, section->size);
2955 while (start < end) {
2959 if (!*(void**)start) {
2961 dump_occupied (occ_start, start, section->data);
2964 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2967 g_assert (start < section->next_data);
2972 vt = (GCVTable*)LOAD_VTABLE (start);
2975 size = safe_object_get_size ((MonoObject*) start);
2976 size += ALLOC_ALIGN - 1;
2977 size &= ~(ALLOC_ALIGN - 1);
2980 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2981 start - section->data,
2982 vt->klass->name_space, vt->klass->name,
2990 dump_occupied (occ_start, start, section->data);
2992 fprintf (heap_dump_file, "</section>\n");
2996 dump_object (MonoObject *obj, gboolean dump_location)
2998 static char class_name [1024];
3000 MonoClass *class = mono_object_class (obj);
3004 * Python's XML parser is too stupid to parse angle brackets
3005 * in strings, so we just ignore them;
3008 while (class->name [i] && j < sizeof (class_name) - 1) {
3009 if (!strchr ("<>\"", class->name [i]))
3010 class_name [j++] = class->name [i];
3013 g_assert (j < sizeof (class_name));
3016 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
3017 class->name_space, class_name,
3018 safe_object_get_size (obj));
3019 if (dump_location) {
3020 const char *location;
3021 if (ptr_in_nursery (obj))
3022 location = "nursery";
3023 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
3027 fprintf (heap_dump_file, " location=\"%s\"", location);
3029 fprintf (heap_dump_file, "/>\n");
3033 dump_heap (const char *type, int num, const char *reason)
3035 static char const *internal_mem_names [] = { "pin-queue", "fragment", "section", "scan-starts",
3036 "fin-table", "finalize-entry", "dislink-table",
3037 "dislink", "roots-table", "root-record", "statistics",
3038 "remset", "gray-queue", "store-remset", "marksweep-tables",
3039 "marksweep-block-info" };
3045 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
3047 fprintf (heap_dump_file, " reason=\"%s\"", reason);
3048 fprintf (heap_dump_file, ">\n");
3049 fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%ld\"/>\n", pinned_chunk_bytes_alloced);
3050 fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%ld\"/>\n", large_internal_bytes_alloced);
3051 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
3052 for (i = 0; i < INTERNAL_MEM_MAX; ++i)
3053 fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n", internal_mem_names [i], small_internal_mem_bytes [i]);
3054 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
3055 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
3056 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
3058 fprintf (heap_dump_file, "<pinned-objects>\n");
3059 for (list = pinned_objects; list; list = list->next)
3060 dump_object (list->obj, TRUE);
3061 fprintf (heap_dump_file, "</pinned-objects>\n");
3063 dump_section (nursery_section, "nursery");
3067 fprintf (heap_dump_file, "<los>\n");
3068 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
3069 dump_object ((MonoObject*)bigobj->data, FALSE);
3070 fprintf (heap_dump_file, "</los>\n");
3072 fprintf (heap_dump_file, "</collection>\n");
3078 static gboolean inited = FALSE;
3083 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
3084 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
3085 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
3086 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
3087 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
3088 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
3089 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3090 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
3092 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
3093 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
3094 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
3095 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
3096 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
3097 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
3098 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
3099 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
3100 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
3101 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
3102 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
3104 #ifdef HEAVY_STATISTICS
3105 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
3106 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
3107 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
3108 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
3109 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
3110 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
3111 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
3112 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
3114 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
3115 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
3116 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
3117 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
3118 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
3120 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
3121 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
3122 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
3123 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
3125 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
3126 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
3128 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
3129 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
3130 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
3132 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
3133 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
3134 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
3135 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
3136 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
3137 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
3138 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
3145 * Collect objects in the nursery. Returns whether to trigger a major
3149 collect_nursery (size_t requested_size)
3151 size_t max_garbage_amount;
3152 char *orig_nursery_next;
3153 TV_DECLARE (all_atv);
3154 TV_DECLARE (all_btv);
3158 current_collection_generation = GENERATION_NURSERY;
3161 binary_protocol_collection (GENERATION_NURSERY);
3162 check_scan_starts ();
3165 orig_nursery_next = nursery_next;
3166 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3167 /* FIXME: optimize later to use the higher address where an object can be present */
3168 nursery_next = MAX (nursery_next, nursery_real_end);
3170 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)));
3171 max_garbage_amount = nursery_next - nursery_start;
3172 g_assert (nursery_section->size >= max_garbage_amount);
3174 /* world must be stopped already */
3175 TV_GETTIME (all_atv);
3178 /* Pinning depends on this */
3179 clear_nursery_fragments (orig_nursery_next);
3182 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3185 check_for_xdomain_refs ();
3187 nursery_section->next_data = nursery_next;
3189 major_start_nursery_collection ();
3191 gray_object_queue_init ();
3194 mono_stats.minor_gc_count ++;
3195 /* pin from pinned handles */
3197 pin_from_roots (nursery_start, nursery_next);
3198 /* identify pinned objects */
3199 optimize_pin_queue (0);
3200 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
3201 nursery_section->pin_queue_start = 0;
3202 nursery_section->pin_queue_end = next_pin_slot;
3204 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3205 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3206 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3208 if (consistency_check_at_minor_collection)
3209 check_consistency ();
3212 * walk all the roots and copy the young objects to the old generation,
3213 * starting from to_space
3216 scan_from_remsets (nursery_start, nursery_next);
3217 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3219 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3220 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3222 drain_gray_stack ();
3225 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3226 /* registered roots, this includes static fields */
3227 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL);
3228 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER);
3230 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3232 scan_thread_data (nursery_start, nursery_next, TRUE);
3234 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3237 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY);
3239 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3241 /* walk the pin_queue, build up the fragment list of free memory, unmark
3242 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3245 build_nursery_fragments (0, next_pin_slot);
3247 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3248 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %zd bytes available\n", TV_ELAPSED (atv, btv), fragment_total));
3250 major_finish_nursery_collection ();
3252 TV_GETTIME (all_btv);
3253 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3256 dump_heap ("minor", num_minor_gcs - 1, NULL);
3258 /* prepare the pin queue for the next collection */
3259 last_num_pinned = next_pin_slot;
3261 if (fin_ready_list || critical_fin_list) {
3262 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3263 mono_gc_finalize_notify ();
3267 g_assert (gray_object_queue_is_empty ());
3269 check_scan_starts ();
3271 current_collection_generation = -1;
3273 return major_need_major_collection ();
3277 major_do_collection (const char *reason)
3279 LOSObject *bigobj, *prevbo;
3280 TV_DECLARE (all_atv);
3281 TV_DECLARE (all_btv);
3284 /* FIXME: only use these values for the precise scan
3285 * note that to_space pointers should be excluded anyway...
3287 char *heap_start = NULL;
3288 char *heap_end = (char*)-1;
3289 int old_num_major_sections = num_major_sections;
3290 int num_major_sections_saved, save_target, allowance_target;
3292 //count_ref_nonref_objs ();
3293 //consistency_check ();
3296 binary_protocol_collection (GENERATION_OLD);
3297 check_scan_starts ();
3298 gray_object_queue_init ();
3301 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3303 mono_stats.major_gc_count ++;
3305 /* world must be stopped already */
3306 TV_GETTIME (all_atv);
3309 /* Pinning depends on this */
3310 clear_nursery_fragments (nursery_next);
3313 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3316 check_for_xdomain_refs ();
3318 nursery_section->next_data = nursery_real_end;
3319 /* we should also coalesce scanning from sections close to each other
3320 * and deal with pointers outside of the sections later.
3322 /* The remsets are not useful for a major collection */
3327 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3328 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3329 optimize_pin_queue (0);
3332 * pin_queue now contains all candidate pointers, sorted and
3333 * uniqued. We must do two passes now to figure out which
3334 * objects are pinned.
3336 * The first is to find within the pin_queue the area for each
3337 * section. This requires that the pin_queue be sorted. We
3338 * also process the LOS objects and pinned chunks here.
3340 * The second, destructive, pass is to reduce the section
3341 * areas to pointers to the actually pinned objects.
3343 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3344 /* first pass for the sections */
3345 find_section_pin_queue_start_end (nursery_section);
3346 major_find_pin_queue_start_ends ();
3347 /* identify possible pointers to the insize of large objects */
3348 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3349 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3351 find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &start, &end);
3353 pin_object (bigobj->data);
3354 /* FIXME: only enqueue if object has references */
3355 GRAY_OBJECT_ENQUEUE (bigobj->data);
3357 pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3358 DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
3361 /* second pass for the sections */
3362 pin_objects_in_section (nursery_section);
3363 major_pin_objects ();
3366 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3367 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3368 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3370 major_init_to_space ();
3372 drain_gray_stack ();
3375 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3377 /* registered roots, this includes static fields */
3378 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL);
3379 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER);
3381 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3384 /* FIXME: This is the wrong place for this, because it does
3386 scan_thread_data (heap_start, heap_end, TRUE);
3388 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3391 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3393 /* scan the list of objects ready for finalization */
3394 scan_finalizer_entries (major_copy_or_mark_object, fin_ready_list);
3395 scan_finalizer_entries (major_copy_or_mark_object, critical_fin_list);
3397 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3398 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3401 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3403 /* all the objects in the heap */
3404 finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
3406 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3408 /* sweep the big objects list */
3410 for (bigobj = los_object_list; bigobj;) {
3411 if (object_is_pinned (bigobj->data)) {
3412 unpin_object (bigobj->data);
3415 /* not referenced anywhere, so we can free it */
3417 prevbo->next = bigobj->next;
3419 los_object_list = bigobj->next;
3421 bigobj = bigobj->next;
3422 free_large_object (to_free);
3426 bigobj = bigobj->next;
3432 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3434 /* walk the pin_queue, build up the fragment list of free memory, unmark
3435 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3438 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
3441 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3443 TV_GETTIME (all_btv);
3444 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3447 dump_heap ("major", num_major_gcs - 1, reason);
3449 /* prepare the pin queue for the next collection */
3451 if (fin_ready_list || critical_fin_list) {
3452 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3453 mono_gc_finalize_notify ();
3457 g_assert (gray_object_queue_is_empty ());
3459 num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 1);
3461 save_target = num_major_sections / 2;
3463 * We aim to allow the allocation of as many sections as is
3464 * necessary to reclaim save_target sections in the next
3465 * collection. We assume the collection pattern won't change.
3466 * In the last cycle, we had num_major_sections_saved for
3467 * minor_collection_sections_alloced. Assuming things won't
3468 * change, this must be the same ratio as save_target for
3469 * allowance_target, i.e.
3471 * num_major_sections_saved save_target
3472 * --------------------------------- == ----------------
3473 * minor_collection_sections_alloced allowance_target
3477 allowance_target = save_target * minor_collection_sections_alloced / num_major_sections_saved;
3479 minor_collection_section_allowance = MAX (MIN (allowance_target, num_major_sections), MIN_MINOR_COLLECTION_SECTION_ALLOWANCE);
3481 minor_collection_sections_alloced = 0;
3483 check_scan_starts ();
3485 //consistency_check ();
3489 major_collection (const char *reason)
3491 if (g_getenv ("MONO_GC_NO_MAJOR")) {
3492 collect_nursery (0);
3496 current_collection_generation = GENERATION_OLD;
3497 major_do_collection (reason);
3498 current_collection_generation = -1;
3502 * When deciding if it's better to collect or to expand, keep track
3503 * of how much garbage was reclaimed with the last collection: if it's too
3505 * This is called when we could not allocate a small object.
3507 static void __attribute__((noinline))
3508 minor_collect_or_expand_inner (size_t size)
3510 int do_minor_collection = 1;
3512 if (!nursery_section) {
3516 if (do_minor_collection) {
3518 if (collect_nursery (size))
3519 major_collection ("minor overflow");
3520 DEBUG (2, fprintf (gc_debug_file, "Heap size: %zd, LOS size: %zd\n", total_alloc, los_memory_usage));
3522 /* this also sets the proper pointers for the next allocation */
3523 if (!search_fragment_for_size (size)) {
3525 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3526 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3527 for (i = 0; i < last_num_pinned; ++i) {
3528 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])));
3533 //report_internal_mem_usage ();
3537 * ######################################################################
3538 * ######## Memory allocation from the OS
3539 * ######################################################################
3540 * This section of code deals with getting memory from the OS and
3541 * allocating memory for GC-internal data structures.
3542 * Internal memory can be handled with a freelist for small objects.
3546 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3547 * This must not require any lock.
3550 get_os_memory (size_t size, int activate)
3553 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3555 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3556 size += pagesize - 1;
3557 size &= ~(pagesize - 1);
3558 ptr = mono_valloc (0, size, prot_flags);
3563 * Free the memory returned by get_os_memory (), returning it to the OS.
3566 free_os_memory (void *addr, size_t size)
3568 mono_vfree (addr, size);
3575 report_pinned_chunk (PinnedChunk *chunk, int seq) {
3577 int i, free_pages, num_free, free_mem;
3579 for (i = 0; i < chunk->num_pages; ++i) {
3580 if (!chunk->page_sizes [i])
3583 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);
3584 free_mem = FREELIST_PAGESIZE * free_pages;
3585 for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
3586 if (!chunk->free_list [i])
3589 p = chunk->free_list [i];
3594 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
3595 free_mem += freelist_sizes [i] * num_free;
3597 printf ("\tfree memory in chunk: %d\n", free_mem);
3603 static G_GNUC_UNUSED void
3604 report_internal_mem_usage (void) {
3607 printf ("Internal memory usage:\n");
3609 for (chunk = internal_chunk_list; chunk; chunk = chunk->block.next) {
3610 report_pinned_chunk (chunk, i++);
3612 printf ("Pinned memory usage:\n");
3613 major_report_pinned_memory_usage ();
3617 * Find the slot number in the freelist for memory chunks that
3618 * can contain @size objects.
3621 slot_for_size (size_t size)
3624 /* do a binary search or lookup table later. */
3625 for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
3626 if (freelist_sizes [slot] >= size)
3629 g_assert_not_reached ();
3634 * Build a free list for @size memory chunks from the memory area between
3635 * start_page and end_page.
3638 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
3642 /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
3643 p = (void**)start_page;
3644 end = (void**)(end_page - size);
3645 g_assert (!chunk->free_list [slot]);
3646 chunk->free_list [slot] = p;
3647 while ((char*)p + size <= (char*)end) {
3649 *p = (void*)((char*)p + size);
3653 /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
3657 alloc_pinned_chunk (void)
3661 int size = PINNED_CHUNK_SIZE;
3663 chunk = get_os_memory_aligned (size, size, TRUE);
3664 chunk->block.role = MEMORY_ROLE_PINNED;
3666 UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
3667 total_alloc += size;
3668 pinned_chunk_bytes_alloced += size;
3670 /* setup the bookeeping fields */
3671 chunk->num_pages = size / FREELIST_PAGESIZE;
3672 offset = G_STRUCT_OFFSET (PinnedChunk, data);
3673 chunk->page_sizes = (void*)((char*)chunk + offset);
3674 offset += sizeof (int) * chunk->num_pages;
3675 offset += ALLOC_ALIGN - 1;
3676 offset &= ~(ALLOC_ALIGN - 1);
3677 chunk->free_list = (void*)((char*)chunk + offset);
3678 offset += sizeof (void*) * FREELIST_NUM_SLOTS;
3679 offset += ALLOC_ALIGN - 1;
3680 offset &= ~(ALLOC_ALIGN - 1);
3681 chunk->start_data = (void*)((char*)chunk + offset);
3683 /* allocate the first page to the freelist */
3684 chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
3685 build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
3686 DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
3690 /* assumes freelist for slot is empty, so try to alloc a new page */
3692 get_chunk_freelist (PinnedChunk *chunk, int slot)
3696 p = chunk->free_list [slot];
3698 chunk->free_list [slot] = *p;
3701 for (i = 0; i < chunk->num_pages; ++i) {
3703 if (chunk->page_sizes [i])
3705 size = freelist_sizes [slot];
3706 chunk->page_sizes [i] = size;
3707 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
3711 p = chunk->free_list [slot];
3713 chunk->free_list [slot] = *p;
3719 /* used for the GC-internal data structures */
3721 get_internal_mem (size_t size, int type)
3725 PinnedChunk *pchunk;
3727 if (size > freelist_sizes [FREELIST_NUM_SLOTS - 1]) {
3728 LargeInternalMemHeader *mh;
3730 size += sizeof (LargeInternalMemHeader);
3731 mh = get_os_memory (size, TRUE);
3732 mh->magic = LARGE_INTERNAL_MEM_HEADER_MAGIC;
3735 large_internal_bytes_alloced += size;
3740 slot = slot_for_size (size);
3741 g_assert (size <= freelist_sizes [slot]);
3743 small_internal_mem_bytes [type] += freelist_sizes [slot];
3745 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3746 void **p = pchunk->free_list [slot];
3748 pchunk->free_list [slot] = *p;
3749 memset (p, 0, size);
3753 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3754 res = get_chunk_freelist (pchunk, slot);
3756 memset (res, 0, size);
3760 pchunk = alloc_pinned_chunk ();
3761 /* FIXME: handle OOM */
3762 pchunk->block.next = internal_chunk_list;
3763 internal_chunk_list = pchunk;
3764 res = get_chunk_freelist (pchunk, slot);
3765 memset (res, 0, size);
3770 free_internal_mem (void *addr, int type)
3772 PinnedChunk *pchunk;
3773 LargeInternalMemHeader *mh;
3776 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3777 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
3778 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
3779 int offset = (char*)addr - (char*)pchunk;
3780 int page = offset / FREELIST_PAGESIZE;
3781 int slot = slot_for_size (pchunk->page_sizes [page]);
3783 *p = pchunk->free_list [slot];
3784 pchunk->free_list [slot] = p;
3786 small_internal_mem_bytes [type] -= freelist_sizes [slot];
3791 mh = (LargeInternalMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargeInternalMemHeader, data));
3792 g_assert (mh->magic == LARGE_INTERNAL_MEM_HEADER_MAGIC);
3793 large_internal_bytes_alloced -= mh->size;
3794 free_os_memory (mh, mh->size);
3798 * ######################################################################
3799 * ######## Object allocation
3800 * ######################################################################
3801 * This section of code deals with allocating memory for objects.
3802 * There are several ways:
3803 * *) allocate large objects
3804 * *) allocate normal objects
3805 * *) fast lock-free allocation
3806 * *) allocation of pinned objects
3810 free_large_object (LOSObject *obj)
3812 size_t size = obj->size;
3813 DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %zd\n", obj->data, obj->size));
3814 binary_protocol_empty (obj->data, obj->size);
3816 los_memory_usage -= size;
3817 size += sizeof (LOSObject);
3818 size += pagesize - 1;
3819 size &= ~(pagesize - 1);
3820 total_alloc -= size;
3822 free_os_memory (obj, size);
3826 * Objects with size >= 64KB are allocated in the large object space.
3827 * They are currently kept track of with a linked list.
3828 * They don't move, so there is no need to pin them during collection
3829 * and we avoid the memcpy overhead.
3831 static void* __attribute__((noinline))
3832 alloc_large_inner (MonoVTable *vtable, size_t size)
3838 g_assert (size > MAX_SMALL_OBJ_SIZE);
3840 if (los_memory_usage > next_los_collection) {
3841 static mword last_los_memory_usage = 0;
3843 mword los_memory_alloced;
3844 mword old_los_memory_usage;
3845 mword los_memory_saved;
3847 mword allowance_target;
3850 DEBUG (4, fprintf (gc_debug_file, "Should trigger major collection: req size %zd (los already: %zu, limit: %zu)\n", size, los_memory_usage, next_los_collection));
3853 g_assert (los_memory_usage >= last_los_memory_usage);
3854 los_memory_alloced = los_memory_usage - last_los_memory_usage;
3855 old_los_memory_usage = los_memory_usage;
3857 major_collection ("LOS overflow");
3859 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
3860 save_target = los_memory_usage / 2;
3862 * see the comment at the end of major_collection()
3863 * for the explanation for this calculation.
3865 allowance_target = (mword)((double)save_target * (double)los_memory_alloced / (double)los_memory_saved);
3866 allowance = MAX (MIN (allowance_target, los_memory_usage), MIN_LOS_ALLOWANCE);
3867 next_los_collection = los_memory_usage + allowance;
3869 last_los_memory_usage = los_memory_usage;
3874 alloc_size += sizeof (LOSObject);
3875 alloc_size += pagesize - 1;
3876 alloc_size &= ~(pagesize - 1);
3877 /* FIXME: handle OOM */
3878 obj = get_os_memory (alloc_size, TRUE);
3879 g_assert (!((mword)obj->data & (ALLOC_ALIGN - 1)));
3881 vtslot = (void**)obj->data;
3883 total_alloc += alloc_size;
3884 UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
3885 obj->next = los_object_list;
3886 los_object_list = obj;
3887 los_memory_usage += size;
3889 DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %zd\n", obj->data, vtable, vtable->klass->name, size));
3890 binary_protocol_alloc (obj->data, vtable, size);
3894 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3895 * an object of size @size
3896 * Return FALSE if not found (which means we need a collection)
3899 search_fragment_for_size (size_t size)
3901 Fragment *frag, *prev;
3902 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3904 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3905 /* Clear the remaining space, pinning depends on this */
3906 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3909 for (frag = nursery_fragments; frag; frag = frag->next) {
3910 if (size <= (frag->fragment_end - frag->fragment_start)) {
3911 /* remove from the list */
3913 prev->next = frag->next;
3915 nursery_fragments = frag->next;
3916 nursery_next = frag->fragment_start;
3917 nursery_frag_real_end = frag->fragment_end;
3919 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));
3920 frag->next = fragment_freelist;
3921 fragment_freelist = frag;
3930 * Provide a variant that takes just the vtable for small fixed-size objects.
3931 * The aligned size is already computed and stored in vt->gc_descr.
3932 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3933 * processing. We can keep track of where objects start, for example,
3934 * so when we scan the thread stacks for pinned objects, we can start
3935 * a search for the pinned object in SCAN_START_SIZE chunks.
3938 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3940 /* FIXME: handle OOM */
3946 HEAVY_STAT (++stat_objects_alloced);
3947 if (size <= MAX_SMALL_OBJ_SIZE)
3948 HEAVY_STAT (stat_bytes_alloced += size);
3950 HEAVY_STAT (stat_bytes_alloced_los += size);
3952 size += ALLOC_ALIGN - 1;
3953 size &= ~(ALLOC_ALIGN - 1);
3955 g_assert (vtable->gc_descr);
3957 if (G_UNLIKELY (collect_before_allocs)) {
3958 if (nursery_section) {
3960 collect_nursery (0);
3962 if (!degraded_mode && !search_fragment_for_size (size)) {
3964 g_assert_not_reached ();
3970 * We must already have the lock here instead of after the
3971 * fast path because we might be interrupted in the fast path
3972 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3973 * and we'll end up allocating an object in a fragment which
3974 * no longer belongs to us.
3976 * The managed allocator does not do this, but it's treated
3977 * specially by the world-stopping code.
3980 if (size > MAX_SMALL_OBJ_SIZE) {
3981 p = alloc_large_inner (vtable, size);
3983 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3985 p = (void**)TLAB_NEXT;
3986 /* FIXME: handle overflow */
3987 new_next = (char*)p + size;
3988 TLAB_NEXT = new_next;
3990 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3994 * FIXME: We might need a memory barrier here so the change to tlab_next is
3995 * visible before the vtable store.
3998 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3999 binary_protocol_alloc (p , vtable, size);
4000 g_assert (*p == NULL);
4003 g_assert (TLAB_NEXT == new_next);
4010 /* there are two cases: the object is too big or we run out of space in the TLAB */
4011 /* we also reach here when the thread does its first allocation after a minor
4012 * collection, since the tlab_ variables are initialized to NULL.
4013 * there can be another case (from ORP), if we cooperate with the runtime a bit:
4014 * objects that need finalizers can have the high bit set in their size
4015 * so the above check fails and we can readily add the object to the queue.
4016 * This avoids taking again the GC lock when registering, but this is moot when
4017 * doing thread-local allocation, so it may not be a good idea.
4019 g_assert (TLAB_NEXT == new_next);
4020 if (TLAB_NEXT >= TLAB_REAL_END) {
4022 * Run out of space in the TLAB. When this happens, some amount of space
4023 * remains in the TLAB, but not enough to satisfy the current allocation
4024 * request. Currently, we retire the TLAB in all cases, later we could
4025 * keep it if the remaining space is above a treshold, and satisfy the
4026 * allocation directly from the nursery.
4029 /* when running in degraded mode, we continue allocing that way
4030 * for a while, to decrease the number of useless nursery collections.
4032 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
4033 p = alloc_degraded (vtable, size);
4037 if (size > tlab_size) {
4038 /* Allocate directly from the nursery */
4039 if (nursery_next + size >= nursery_frag_real_end) {
4040 if (!search_fragment_for_size (size)) {
4041 minor_collect_or_expand_inner (size);
4042 if (degraded_mode) {
4043 p = alloc_degraded (vtable, size);
4049 p = (void*)nursery_next;
4050 nursery_next += size;
4051 if (nursery_next > nursery_frag_real_end) {
4056 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4057 memset (p, 0, size);
4060 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
4062 if (nursery_next + tlab_size >= nursery_frag_real_end) {
4063 res = search_fragment_for_size (tlab_size);
4065 minor_collect_or_expand_inner (tlab_size);
4066 if (degraded_mode) {
4067 p = alloc_degraded (vtable, size);
4073 /* Allocate a new TLAB from the current nursery fragment */
4074 TLAB_START = nursery_next;
4075 nursery_next += tlab_size;
4076 TLAB_NEXT = TLAB_START;
4077 TLAB_REAL_END = TLAB_START + tlab_size;
4078 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, tlab_size);
4080 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4081 memset (TLAB_START, 0, tlab_size);
4083 /* Allocate from the TLAB */
4084 p = (void*)TLAB_NEXT;
4086 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4088 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4091 /* Reached tlab_temp_end */
4093 /* record the scan start so we can find pinned objects more easily */
4094 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4095 /* we just bump tlab_temp_end as well */
4096 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4097 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4101 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4102 binary_protocol_alloc (p, vtable, size);
4109 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4115 size += ALLOC_ALIGN - 1;
4116 size &= ~(ALLOC_ALIGN - 1);
4118 g_assert (vtable->gc_descr);
4119 if (size <= MAX_SMALL_OBJ_SIZE) {
4120 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4122 p = (void**)TLAB_NEXT;
4123 /* FIXME: handle overflow */
4124 new_next = (char*)p + size;
4125 TLAB_NEXT = new_next;
4127 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4131 * FIXME: We might need a memory barrier here so the change to tlab_next is
4132 * visible before the vtable store.
4135 HEAVY_STAT (++stat_objects_alloced);
4136 HEAVY_STAT (stat_bytes_alloced += size);
4138 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4139 binary_protocol_alloc (p, vtable, size);
4140 g_assert (*p == NULL);
4143 g_assert (TLAB_NEXT == new_next);
4152 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4155 #ifndef DISABLE_CRITICAL_REGION
4157 ENTER_CRITICAL_REGION;
4158 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4160 EXIT_CRITICAL_REGION;
4163 EXIT_CRITICAL_REGION;
4166 res = mono_gc_alloc_obj_nolock (vtable, size);
4172 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4175 #ifndef DISABLE_CRITICAL_REGION
4177 ENTER_CRITICAL_REGION;
4178 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4180 arr->max_length = max_length;
4181 EXIT_CRITICAL_REGION;
4184 EXIT_CRITICAL_REGION;
4189 arr = mono_gc_alloc_obj_nolock (vtable, size);
4190 arr->max_length = max_length;
4198 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4201 MonoArrayBounds *bounds;
4205 arr = mono_gc_alloc_obj_nolock (vtable, size);
4206 arr->max_length = max_length;
4208 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4209 arr->bounds = bounds;
4217 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4220 #ifndef DISABLE_CRITICAL_REGION
4222 ENTER_CRITICAL_REGION;
4223 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4226 EXIT_CRITICAL_REGION;
4229 EXIT_CRITICAL_REGION;
4234 str = mono_gc_alloc_obj_nolock (vtable, size);
4243 * To be used for interned strings and possibly MonoThread, reflection handles.
4244 * We may want to explicitly free these objects.
4247 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4249 /* FIXME: handle OOM */
4251 size += ALLOC_ALIGN - 1;
4252 size &= ~(ALLOC_ALIGN - 1);
4254 if (size > MAX_SMALL_OBJ_SIZE) {
4255 /* large objects are always pinned anyway */
4256 p = alloc_large_inner (vtable, size);
4258 DEBUG (9, g_assert (vtable->klass->inited));
4259 p = major_alloc_small_pinned_obj (size, vtable->klass->has_references);
4261 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4262 binary_protocol_alloc (p, vtable, size);
4269 * ######################################################################
4270 * ######## Finalization support
4271 * ######################################################################
4275 * this is valid for the nursery: if the object has been forwarded it means it's
4276 * still refrenced from a root. If it is pinned it's still alive as well.
4277 * Return TRUE if @obj is ready to be finalized.
4279 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4282 is_critical_finalizer (FinalizeEntry *entry)
4287 if (!mono_defaults.critical_finalizer_object)
4290 obj = entry->object;
4291 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4293 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4297 queue_finalization_entry (FinalizeEntry *entry) {
4298 if (is_critical_finalizer (entry)) {
4299 entry->next = critical_fin_list;
4300 critical_fin_list = entry;
4302 entry->next = fin_ready_list;
4303 fin_ready_list = entry;
4307 /* LOCKING: requires that the GC lock is held */
4309 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4311 FinalizeEntry **finalizable_hash = hash_table->table;
4312 mword finalizable_hash_size = hash_table->size;
4315 FinalizeEntry **new_hash;
4316 FinalizeEntry *entry, *next;
4317 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4319 new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4320 for (i = 0; i < finalizable_hash_size; ++i) {
4321 for (entry = finalizable_hash [i]; entry; entry = next) {
4322 hash = mono_object_hash (entry->object) % new_size;
4324 entry->next = new_hash [hash];
4325 new_hash [hash] = entry;
4328 free_internal_mem (finalizable_hash, INTERNAL_MEM_FIN_TABLE);
4329 hash_table->table = new_hash;
4330 hash_table->size = new_size;
4333 /* LOCKING: requires that the GC lock is held */
4335 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4337 if (hash_table->num_registered >= hash_table->size * 2)
4338 rehash_fin_table (hash_table);
4341 /* LOCKING: requires that the GC lock is held */
4343 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4345 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4346 FinalizeEntry *entry, *prev;
4348 FinalizeEntry **finalizable_hash = hash_table->table;
4349 mword finalizable_hash_size = hash_table->size;
4353 for (i = 0; i < finalizable_hash_size; ++i) {
4355 for (entry = finalizable_hash [i]; entry;) {
4356 if ((char*)entry->object >= start && (char*)entry->object < end && !major_is_object_live (entry->object)) {
4357 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4358 char *copy = entry->object;
4359 copy_func ((void**)©);
4362 FinalizeEntry *next;
4363 /* remove and put in fin_ready_list */
4365 prev->next = entry->next;
4367 finalizable_hash [i] = entry->next;
4369 num_ready_finalizers++;
4370 hash_table->num_registered--;
4371 queue_finalization_entry (entry);
4372 /* Make it survive */
4373 from = entry->object;
4374 entry->object = copy;
4375 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));
4379 char *from = entry->object;
4380 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4381 FinalizeEntry *next = entry->next;
4382 unsigned int major_hash;
4383 /* remove from the list */
4385 prev->next = entry->next;
4387 finalizable_hash [i] = entry->next;
4388 hash_table->num_registered--;
4390 entry->object = copy;
4392 /* insert it into the major hash */
4393 rehash_fin_table_if_necessary (&major_finalizable_hash);
4394 major_hash = mono_object_hash ((MonoObject*) copy) %
4395 major_finalizable_hash.size;
4396 entry->next = major_finalizable_hash.table [major_hash];
4397 major_finalizable_hash.table [major_hash] = entry;
4398 major_finalizable_hash.num_registered++;
4400 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4405 /* update pointer */
4406 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4407 entry->object = copy;
4412 entry = entry->next;
4417 /* LOCKING: requires that the GC lock is held */
4419 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4421 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4422 DisappearingLink **disappearing_link_hash = hash->table;
4423 int disappearing_link_hash_size = hash->size;
4424 DisappearingLink *entry, *prev;
4426 if (!hash->num_links)
4428 for (i = 0; i < disappearing_link_hash_size; ++i) {
4430 for (entry = disappearing_link_hash [i]; entry;) {
4431 char *object = DISLINK_OBJECT (entry);
4432 if (object >= start && object < end && !major_is_object_live (object)) {
4433 gboolean track = DISLINK_TRACK (entry);
4434 if (!track && object_is_fin_ready (object)) {
4435 void **p = entry->link;
4436 DisappearingLink *old;
4438 /* remove from list */
4440 prev->next = entry->next;
4442 disappearing_link_hash [i] = entry->next;
4443 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4445 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4450 char *copy = object;
4451 copy_func ((void**)©);
4453 /* Update pointer if it's moved. If the object
4454 * has been moved out of the nursery, we need to
4455 * remove the link from the minor hash table to
4458 * FIXME: what if an object is moved earlier?
4461 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4462 void **link = entry->link;
4463 DisappearingLink *old;
4464 /* remove from list */
4466 prev->next = entry->next;
4468 disappearing_link_hash [i] = entry->next;
4470 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4474 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4475 track, GENERATION_OLD);
4477 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4481 /* We set the track resurrection bit to
4482 * FALSE if the object is to be finalized
4483 * so that the object can be collected in
4484 * the next cycle (i.e. after it was
4487 *entry->link = HIDE_POINTER (copy,
4488 object_is_fin_ready (object) ? FALSE : track);
4489 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4494 entry = entry->next;
4499 /* LOCKING: requires that the GC lock is held */
4501 null_links_for_domain (MonoDomain *domain, int generation)
4503 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4504 DisappearingLink **disappearing_link_hash = hash->table;
4505 int disappearing_link_hash_size = hash->size;
4506 DisappearingLink *entry, *prev;
4508 for (i = 0; i < disappearing_link_hash_size; ++i) {
4510 for (entry = disappearing_link_hash [i]; entry; ) {
4511 char *object = DISLINK_OBJECT (entry);
4512 /* FIXME: actually there should be no object
4513 left in the domain with a non-null vtable
4514 (provided we remove the Thread special
4516 if (object && (!((MonoObject*)object)->vtable || mono_object_domain (object) == domain)) {
4517 DisappearingLink *next = entry->next;
4522 disappearing_link_hash [i] = next;
4524 if (*(entry->link)) {
4525 *(entry->link) = NULL;
4526 g_warning ("Disappearing link %p not freed", entry->link);
4528 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4535 entry = entry->next;
4540 /* LOCKING: requires that the GC lock is held */
4542 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4543 FinalizeEntryHashTable *hash_table)
4545 FinalizeEntry **finalizable_hash = hash_table->table;
4546 mword finalizable_hash_size = hash_table->size;
4547 FinalizeEntry *entry, *prev;
4550 if (no_finalize || !out_size || !out_array)
4553 for (i = 0; i < finalizable_hash_size; ++i) {
4555 for (entry = finalizable_hash [i]; entry;) {
4556 if (mono_object_domain (entry->object) == domain) {
4557 FinalizeEntry *next;
4558 /* remove and put in out_array */
4560 prev->next = entry->next;
4562 finalizable_hash [i] = entry->next;
4564 hash_table->num_registered--;
4565 out_array [count ++] = entry->object;
4566 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));
4568 if (count == out_size)
4573 entry = entry->next;
4580 * mono_gc_finalizers_for_domain:
4581 * @domain: the unloading appdomain
4582 * @out_array: output array
4583 * @out_size: size of output array
4585 * Store inside @out_array up to @out_size objects that belong to the unloading
4586 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4587 * until it returns 0.
4588 * The items are removed from the finalizer data structure, so the caller is supposed
4590 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4593 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4598 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4599 if (result < out_size) {
4600 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4601 &major_finalizable_hash);
4609 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4611 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4612 FinalizeEntry **finalizable_hash;
4613 mword finalizable_hash_size;
4614 FinalizeEntry *entry, *prev;
4618 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4619 hash = mono_object_hash (obj);
4621 rehash_fin_table_if_necessary (hash_table);
4622 finalizable_hash = hash_table->table;
4623 finalizable_hash_size = hash_table->size;
4624 hash %= finalizable_hash_size;
4626 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4627 if (entry->object == obj) {
4629 /* remove from the list */
4631 prev->next = entry->next;
4633 finalizable_hash [hash] = entry->next;
4634 hash_table->num_registered--;
4635 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));
4636 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4644 /* request to deregister, but already out of the list */
4648 entry = get_internal_mem (sizeof (FinalizeEntry), INTERNAL_MEM_FINALIZE_ENTRY);
4649 entry->object = obj;
4650 entry->next = finalizable_hash [hash];
4651 finalizable_hash [hash] = entry;
4652 hash_table->num_registered++;
4653 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)));
4658 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4660 if (ptr_in_nursery (obj))
4661 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4663 register_for_finalization (obj, user_data, GENERATION_OLD);
4667 rehash_dislink (DisappearingLinkHashTable *hash_table)
4669 DisappearingLink **disappearing_link_hash = hash_table->table;
4670 int disappearing_link_hash_size = hash_table->size;
4673 DisappearingLink **new_hash;
4674 DisappearingLink *entry, *next;
4675 int new_size = g_spaced_primes_closest (hash_table->num_links);
4677 new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4678 for (i = 0; i < disappearing_link_hash_size; ++i) {
4679 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4680 hash = mono_aligned_addr_hash (entry->link) % new_size;
4682 entry->next = new_hash [hash];
4683 new_hash [hash] = entry;
4686 free_internal_mem (disappearing_link_hash, INTERNAL_MEM_DISLINK_TABLE);
4687 hash_table->table = new_hash;
4688 hash_table->size = new_size;
4691 /* LOCKING: assumes the GC lock is held */
4693 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4695 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4696 DisappearingLink *entry, *prev;
4698 DisappearingLink **disappearing_link_hash = hash_table->table;
4699 int disappearing_link_hash_size = hash_table->size;
4701 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4702 rehash_dislink (hash_table);
4703 disappearing_link_hash = hash_table->table;
4704 disappearing_link_hash_size = hash_table->size;
4706 /* FIXME: add check that link is not in the heap */
4707 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4708 entry = disappearing_link_hash [hash];
4710 for (; entry; entry = entry->next) {
4711 /* link already added */
4712 if (link == entry->link) {
4713 /* NULL obj means remove */
4716 prev->next = entry->next;
4718 disappearing_link_hash [hash] = entry->next;
4719 hash_table->num_links--;
4720 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4721 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4724 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4732 entry = get_internal_mem (sizeof (DisappearingLink), INTERNAL_MEM_DISLINK);
4733 *link = HIDE_POINTER (obj, track);
4735 entry->next = disappearing_link_hash [hash];
4736 disappearing_link_hash [hash] = entry;
4737 hash_table->num_links++;
4738 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)));
4741 /* LOCKING: assumes the GC lock is held */
4743 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4745 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4746 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4748 if (ptr_in_nursery (obj))
4749 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4751 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4756 mono_gc_invoke_finalizers (void)
4758 FinalizeEntry *entry = NULL;
4759 gboolean entry_is_critical = FALSE;
4762 /* FIXME: batch to reduce lock contention */
4763 while (fin_ready_list || critical_fin_list) {
4767 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4769 /* We have finalized entry in the last
4770 interation, now we need to remove it from
4773 *list = entry->next;
4775 FinalizeEntry *e = *list;
4776 while (e->next != entry)
4778 e->next = entry->next;
4780 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4784 /* Now look for the first non-null entry. */
4785 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4788 entry_is_critical = FALSE;
4790 entry_is_critical = TRUE;
4791 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4796 g_assert (entry->object);
4797 num_ready_finalizers--;
4798 obj = entry->object;
4799 entry->object = NULL;
4800 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4808 g_assert (entry->object == NULL);
4810 /* the object is on the stack so it is pinned */
4811 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4812 mono_gc_run_finalize (obj, NULL);
4819 mono_gc_pending_finalizers (void)
4821 return fin_ready_list || critical_fin_list;
4824 /* Negative value to remove */
4826 mono_gc_add_memory_pressure (gint64 value)
4828 /* FIXME: Use interlocked functions */
4830 memory_pressure += value;
4835 * ######################################################################
4836 * ######## registered roots support
4837 * ######################################################################
4841 rehash_roots (gboolean pinned)
4845 RootRecord **new_hash;
4846 RootRecord *entry, *next;
4849 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4850 new_hash = get_internal_mem (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4851 for (i = 0; i < roots_hash_size [pinned]; ++i) {
4852 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4853 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4855 entry->next = new_hash [hash];
4856 new_hash [hash] = entry;
4859 free_internal_mem (roots_hash [pinned], INTERNAL_MEM_ROOTS_TABLE);
4860 roots_hash [pinned] = new_hash;
4861 roots_hash_size [pinned] = new_size;
4865 find_root (int root_type, char *start, guint32 addr_hash)
4867 RootRecord *new_root;
4869 guint32 hash = addr_hash % roots_hash_size [root_type];
4870 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4871 /* we allow changing the size and the descriptor (for thread statics etc) */
4872 if (new_root->start_root == start) {
4881 * We do not coalesce roots.
4884 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4886 RootRecord *new_root;
4887 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
4890 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4891 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
4894 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4895 new_root = find_root (i, start, addr_hash);
4896 /* we allow changing the size and the descriptor (for thread statics etc) */
4898 size_t old_size = new_root->end_root - new_root->start_root;
4899 new_root->end_root = new_root->start_root + size;
4900 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
4901 ((new_root->root_desc == 0) && (descr == NULL)));
4902 new_root->root_desc = (mword)descr;
4904 roots_size -= old_size;
4909 new_root = get_internal_mem (sizeof (RootRecord), INTERNAL_MEM_ROOT_RECORD);
4911 new_root->start_root = start;
4912 new_root->end_root = new_root->start_root + size;
4913 new_root->root_desc = (mword)descr;
4915 hash = addr_hash % roots_hash_size [root_type];
4916 num_roots_entries [root_type]++;
4917 new_root->next = roots_hash [root_type] [hash];
4918 roots_hash [root_type][hash] = new_root;
4919 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));
4929 mono_gc_register_root (char *start, size_t size, void *descr)
4931 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4935 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4937 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4941 mono_gc_deregister_root (char* addr)
4943 RootRecord *tmp, *prev;
4944 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
4948 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4949 hash = addr_hash % roots_hash_size [root_type];
4950 tmp = roots_hash [root_type][hash];
4953 if (tmp->start_root == (char*)addr) {
4955 prev->next = tmp->next;
4957 roots_hash [root_type][hash] = tmp->next;
4958 roots_size -= (tmp->end_root - tmp->start_root);
4959 num_roots_entries [root_type]--;
4960 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
4961 free_internal_mem (tmp, INTERNAL_MEM_ROOT_RECORD);
4972 * ######################################################################
4973 * ######## Thread handling (stop/start code)
4974 * ######################################################################
4977 /* FIXME: handle large/small config */
4978 #define THREAD_HASH_SIZE 11
4979 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
4981 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
4983 #if USE_SIGNAL_BASED_START_STOP_WORLD
4985 static MonoSemType suspend_ack_semaphore;
4986 static MonoSemType *suspend_ack_semaphore_ptr;
4987 static unsigned int global_stop_count = 0;
4989 static int suspend_signal_num = SIGXFSZ;
4991 static int suspend_signal_num = SIGPWR;
4993 static int restart_signal_num = SIGXCPU;
4994 static sigset_t suspend_signal_mask;
4995 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4997 /* LOCKING: assumes the GC lock is held */
4998 static SgenThreadInfo*
4999 thread_info_lookup (ARCH_THREAD_TYPE id)
5001 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5002 SgenThreadInfo *info;
5004 info = thread_table [hash];
5005 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5012 update_current_thread_stack (void *start)
5014 void *ptr = cur_thread_regs;
5015 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5017 info->stack_start = align_pointer (&ptr);
5018 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5019 ARCH_STORE_REGS (ptr);
5020 info->stopped_regs = ptr;
5021 if (gc_callbacks.thread_suspend_func)
5022 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5026 signal_desc (int signum)
5028 if (signum == suspend_signal_num)
5030 if (signum == restart_signal_num)
5036 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5037 * have cross-domain checks in the write barrier.
5039 //#define XDOMAIN_CHECKS_IN_WBARRIER
5041 #ifndef BINARY_PROTOCOL
5042 #ifndef HEAVY_STATISTICS
5043 #define MANAGED_ALLOCATION
5044 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5045 #define MANAGED_WBARRIER
5051 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5054 wait_for_suspend_ack (int count)
5058 for (i = 0; i < count; ++i) {
5059 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5060 if (errno != EINTR) {
5061 g_error ("sem_wait ()");
5067 /* LOCKING: assumes the GC lock is held */
5069 thread_handshake (int signum)
5071 int count, i, result;
5072 SgenThreadInfo *info;
5073 pthread_t me = pthread_self ();
5076 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5077 for (info = thread_table [i]; info; info = info->next) {
5078 DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
5079 if (ARCH_THREAD_EQUALS (info->id, me)) {
5080 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
5083 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
5085 result = pthread_kill (info->id, signum);
5087 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
5090 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
5096 wait_for_suspend_ack (count);
5102 restart_threads_until_none_in_managed_allocator (void)
5104 SgenThreadInfo *info;
5105 int i, result, num_threads_died = 0;
5106 int sleep_duration = -1;
5109 int restart_count = 0, restarted_count = 0;
5110 /* restart all threads that stopped in the
5112 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5113 for (info = thread_table [i]; info; info = info->next) {
5116 if (!info->stack_start || info->in_critical_region ||
5117 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5118 binary_protocol_thread_restart ((gpointer)info->id);
5119 result = pthread_kill (info->id, restart_signal_num);
5126 /* we set the stopped_ip to
5127 NULL for threads which
5128 we're not restarting so
5129 that we can easily identify
5131 info->stopped_ip = NULL;
5132 info->stopped_domain = NULL;
5136 /* if no threads were restarted, we're done */
5137 if (restart_count == 0)
5140 /* wait for the threads to signal their restart */
5141 wait_for_suspend_ack (restart_count);
5143 if (sleep_duration < 0) {
5147 g_usleep (sleep_duration);
5148 sleep_duration += 10;
5151 /* stop them again */
5152 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5153 for (info = thread_table [i]; info; info = info->next) {
5154 if (info->skip || info->stopped_ip == NULL)
5156 result = pthread_kill (info->id, suspend_signal_num);
5164 /* some threads might have died */
5165 num_threads_died += restart_count - restarted_count;
5166 /* wait for the threads to signal their suspension
5168 wait_for_suspend_ack (restart_count);
5171 return num_threads_died;
5174 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5176 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5178 SgenThreadInfo *info;
5181 int old_errno = errno;
5182 gpointer regs [ARCH_NUM_REGS];
5183 gpointer stack_start;
5185 id = pthread_self ();
5186 info = thread_info_lookup (id);
5187 info->stopped_domain = mono_domain_get ();
5188 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5189 stop_count = global_stop_count;
5190 /* duplicate signal */
5191 if (0 && info->stop_count == stop_count) {
5195 #ifdef HAVE_KW_THREAD
5196 /* update the remset info in the thread data structure */
5197 info->remset = remembered_set;
5199 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5200 /* If stack_start is not within the limits, then don't set it
5201 in info and we will be restarted. */
5202 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5203 info->stack_start = stack_start;
5205 ARCH_COPY_SIGCTX_REGS (regs, context);
5206 info->stopped_regs = regs;
5208 g_assert (!info->stack_start);
5211 /* Notify the JIT */
5212 if (gc_callbacks.thread_suspend_func)
5213 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5215 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5216 /* notify the waiting thread */
5217 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5218 info->stop_count = stop_count;
5220 /* wait until we receive the restart signal */
5223 sigsuspend (&suspend_signal_mask);
5224 } while (info->signal != restart_signal_num);
5226 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5227 /* notify the waiting thread */
5228 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5234 restart_handler (int sig)
5236 SgenThreadInfo *info;
5237 int old_errno = errno;
5239 info = thread_info_lookup (pthread_self ());
5240 info->signal = restart_signal_num;
5241 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5247 acquire_gc_locks (void)
5253 release_gc_locks (void)
5255 UNLOCK_INTERRUPTION;
5258 static TV_DECLARE (stop_world_time);
5259 static unsigned long max_pause_usec = 0;
5261 /* LOCKING: assumes the GC lock is held */
5267 acquire_gc_locks ();
5269 update_current_thread_stack (&count);
5271 global_stop_count++;
5272 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 ()));
5273 TV_GETTIME (stop_world_time);
5274 count = thread_handshake (suspend_signal_num);
5275 count -= restart_threads_until_none_in_managed_allocator ();
5276 g_assert (count >= 0);
5277 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5281 /* LOCKING: assumes the GC lock is held */
5283 restart_world (void)
5286 SgenThreadInfo *info;
5287 TV_DECLARE (end_sw);
5290 /* notify the profiler of the leftovers */
5291 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5292 if (moved_objects_idx) {
5293 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5294 moved_objects_idx = 0;
5297 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5298 for (info = thread_table [i]; info; info = info->next) {
5299 info->stack_start = NULL;
5300 info->stopped_regs = NULL;
5304 release_gc_locks ();
5306 count = thread_handshake (restart_signal_num);
5307 TV_GETTIME (end_sw);
5308 usec = TV_ELAPSED (stop_world_time, end_sw);
5309 max_pause_usec = MAX (usec, max_pause_usec);
5310 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5314 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5317 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5319 gc_callbacks = *callbacks;
5322 /* Variables holding start/end nursery so it won't have to be passed at every call */
5323 static void *scan_area_arg_start, *scan_area_arg_end;
5326 mono_gc_conservatively_scan_area (void *start, void *end)
5328 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5332 mono_gc_scan_object (void *obj)
5334 if (current_collection_generation == GENERATION_NURSERY)
5337 major_copy_or_mark_object (&obj);
5342 * Mark from thread stacks and registers.
5345 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5348 SgenThreadInfo *info;
5350 scan_area_arg_start = start_nursery;
5351 scan_area_arg_end = end_nursery;
5353 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5354 for (info = thread_table [i]; info; info = info->next) {
5356 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));
5359 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));
5360 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
5361 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5363 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5366 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5367 start_nursery, end_nursery, PIN_TYPE_STACK);
5373 find_pinning_ref_from_thread (char *obj, size_t size)
5376 SgenThreadInfo *info;
5377 char *endobj = obj + size;
5379 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5380 for (info = thread_table [i]; info; info = info->next) {
5381 char **start = (char**)info->stack_start;
5384 while (start < (char**)info->stack_end) {
5385 if (*start >= obj && *start < endobj) {
5386 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));
5391 /* FIXME: check info->stopped_regs */
5397 ptr_on_stack (void *ptr)
5399 gpointer stack_start = &stack_start;
5400 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5402 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5408 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
5415 HEAVY_STAT (++stat_global_remsets_processed);
5417 /* FIXME: exclude stack locations */
5418 switch ((*p) & REMSET_TYPE_MASK) {
5419 case REMSET_LOCATION:
5421 //__builtin_prefetch (ptr);
5422 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5423 gpointer old = *ptr;
5425 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5427 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5428 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5430 * If the object is pinned, each reference to it from nonpinned objects
5431 * becomes part of the global remset, which can grow very large.
5433 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5434 add_to_global_remset (ptr, FALSE);
5437 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5441 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5442 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5445 while (count-- > 0) {
5447 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5448 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5449 add_to_global_remset (ptr, FALSE);
5454 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5455 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5457 scan_object ((char*)ptr);
5459 case REMSET_OTHER: {
5460 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5464 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5469 ptr = (void**) scan_vtype ((char*)ptr, desc, start_nursery, end_nursery);
5471 case REMSET_ROOT_LOCATION:
5472 /* Same as REMSET_LOCATION, but the address is not required to be in the heap */
5474 DEBUG (9, fprintf (gc_debug_file, "Overwrote root location remset at %p with %p\n", ptr, *ptr));
5475 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5477 * If the object is pinned, each reference to it from nonpinned objects
5478 * becomes part of the global remset, which can grow very large.
5480 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5481 add_to_global_remset (ptr, TRUE);
5485 g_assert_not_reached ();
5490 g_assert_not_reached ();
5495 #ifdef HEAVY_STATISTICS
5497 collect_store_remsets (RememberedSet *remset, mword *bumper)
5499 mword *p = remset->data;
5504 while (p < remset->store_next) {
5505 switch ((*p) & REMSET_TYPE_MASK) {
5506 case REMSET_LOCATION:
5509 ++stat_saved_remsets_1;
5511 if (*p == last1 || *p == last2) {
5512 ++stat_saved_remsets_2;
5530 case REMSET_ROOT_LOCATION:
5534 g_assert_not_reached ();
5538 g_assert_not_reached ();
5548 RememberedSet *remset;
5550 SgenThreadInfo *info;
5552 mword *addresses, *bumper, *p, *r;
5554 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5555 for (info = thread_table [i]; info; info = info->next) {
5556 for (remset = info->remset; remset; remset = remset->next)
5557 size += remset->store_next - remset->data;
5560 for (remset = freed_thread_remsets; remset; remset = remset->next)
5561 size += remset->store_next - remset->data;
5562 for (remset = global_remset; remset; remset = remset->next)
5563 size += remset->store_next - remset->data;
5565 bumper = addresses = get_internal_mem (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5567 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5568 for (info = thread_table [i]; info; info = info->next) {
5569 for (remset = info->remset; remset; remset = remset->next)
5570 bumper = collect_store_remsets (remset, bumper);
5573 for (remset = global_remset; remset; remset = remset->next)
5574 bumper = collect_store_remsets (remset, bumper);
5575 for (remset = freed_thread_remsets; remset; remset = remset->next)
5576 bumper = collect_store_remsets (remset, bumper);
5578 g_assert (bumper <= addresses + size);
5580 stat_store_remsets += bumper - addresses;
5582 sort_addresses ((void**)addresses, bumper - addresses);
5585 while (r < bumper) {
5591 stat_store_remsets_unique += p - addresses;
5593 free_internal_mem (addresses, INTERNAL_MEM_STATISTICS);
5598 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5600 *info->store_remset_buffer_index_addr = 0;
5601 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5605 scan_from_remsets (void *start_nursery, void *end_nursery)
5608 SgenThreadInfo *info;
5609 RememberedSet *remset;
5610 GenericStoreRememberedSet *store_remset;
5611 mword *p, *next_p, *store_pos;
5613 #ifdef HEAVY_STATISTICS
5617 /* the global one */
5618 for (remset = global_remset; remset; remset = remset->next) {
5619 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));
5620 store_pos = remset->data;
5621 for (p = remset->data; p < remset->store_next; p = next_p) {
5624 next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
5627 * Clear global remsets of locations which no longer point to the
5628 * nursery. Otherwise, they could grow indefinitely between major
5631 ptr = (p [0] & ~REMSET_TYPE_MASK);
5632 if ((p [0] & REMSET_TYPE_MASK) == REMSET_LOCATION) {
5633 if (ptr_in_nursery (*(void**)ptr)) {
5634 *store_pos ++ = p [0];
5635 HEAVY_STAT (++stat_global_remsets_readded);
5638 g_assert ((p [0] & REMSET_TYPE_MASK) == REMSET_OTHER);
5639 g_assert (p [1] == REMSET_ROOT_LOCATION);
5640 if (ptr_in_nursery (*(void**)ptr)) {
5641 *store_pos ++ = p [0];
5642 *store_pos ++ = p [1];
5643 HEAVY_STAT (++stat_global_remsets_readded);
5648 /* Truncate the remset */
5649 remset->store_next = store_pos;
5652 /* the generic store ones */
5653 store_remset = generic_store_remsets;
5654 while (store_remset) {
5655 GenericStoreRememberedSet *next = store_remset->next;
5657 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5658 gpointer addr = store_remset->data [i];
5660 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE);
5663 free_internal_mem (store_remset, INTERNAL_MEM_STORE_REMSET);
5665 store_remset = next;
5667 generic_store_remsets = NULL;
5669 /* the per-thread ones */
5670 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5671 for (info = thread_table [i]; info; info = info->next) {
5672 RememberedSet *next;
5674 for (remset = info->remset; remset; remset = next) {
5675 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));
5676 for (p = remset->data; p < remset->store_next;) {
5677 p = handle_remset (p, start_nursery, end_nursery, FALSE);
5679 remset->store_next = remset->data;
5680 next = remset->next;
5681 remset->next = NULL;
5682 if (remset != info->remset) {
5683 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5684 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5687 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5688 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE);
5689 clear_thread_store_remset_buffer (info);
5693 /* the freed thread ones */
5694 while (freed_thread_remsets) {
5695 RememberedSet *next;
5696 remset = freed_thread_remsets;
5697 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));
5698 for (p = remset->data; p < remset->store_next;) {
5699 p = handle_remset (p, start_nursery, end_nursery, FALSE);
5701 next = remset->next;
5702 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5703 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5704 freed_thread_remsets = next;
5709 * Clear the info in the remembered sets: we're doing a major collection, so
5710 * the per-thread ones are not needed and the global ones will be reconstructed
5714 clear_remsets (void)
5717 SgenThreadInfo *info;
5718 RememberedSet *remset, *next;
5720 /* the global list */
5721 for (remset = global_remset; remset; remset = next) {
5722 remset->store_next = remset->data;
5723 next = remset->next;
5724 remset->next = NULL;
5725 if (remset != global_remset) {
5726 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5727 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5730 /* the generic store ones */
5731 while (generic_store_remsets) {
5732 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5733 free_internal_mem (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5734 generic_store_remsets = gs_next;
5736 /* the per-thread ones */
5737 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5738 for (info = thread_table [i]; info; info = info->next) {
5739 for (remset = info->remset; remset; remset = next) {
5740 remset->store_next = remset->data;
5741 next = remset->next;
5742 remset->next = NULL;
5743 if (remset != info->remset) {
5744 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5745 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5748 clear_thread_store_remset_buffer (info);
5752 /* the freed thread ones */
5753 while (freed_thread_remsets) {
5754 next = freed_thread_remsets->next;
5755 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5756 free_internal_mem (freed_thread_remsets, INTERNAL_MEM_REMSET);
5757 freed_thread_remsets = next;
5762 * Clear the thread local TLAB variables for all threads.
5767 SgenThreadInfo *info;
5770 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5771 for (info = thread_table [i]; info; info = info->next) {
5772 /* A new TLAB will be allocated when the thread does its first allocation */
5773 *info->tlab_start_addr = NULL;
5774 *info->tlab_next_addr = NULL;
5775 *info->tlab_temp_end_addr = NULL;
5776 *info->tlab_real_end_addr = NULL;
5781 /* LOCKING: assumes the GC lock is held */
5782 static SgenThreadInfo*
5783 gc_register_current_thread (void *addr)
5786 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5787 #ifndef HAVE_KW_THREAD
5788 SgenThreadInfo *__thread_info__ = info;
5794 memset (info, 0, sizeof (SgenThreadInfo));
5795 #ifndef HAVE_KW_THREAD
5796 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5798 g_assert (!pthread_getspecific (thread_info_key));
5799 pthread_setspecific (thread_info_key, info);
5804 info->id = ARCH_GET_THREAD ();
5805 info->stop_count = -1;
5808 info->stack_start = NULL;
5809 info->tlab_start_addr = &TLAB_START;
5810 info->tlab_next_addr = &TLAB_NEXT;
5811 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5812 info->tlab_real_end_addr = &TLAB_REAL_END;
5813 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5814 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5815 info->stopped_ip = NULL;
5816 info->stopped_domain = NULL;
5817 info->stopped_regs = NULL;
5819 binary_protocol_thread_register ((gpointer)info->id);
5821 #ifdef HAVE_KW_THREAD
5822 tlab_next_addr = &tlab_next;
5823 store_remset_buffer_index_addr = &store_remset_buffer_index;
5826 /* try to get it with attributes first */
5827 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5831 pthread_attr_t attr;
5832 pthread_getattr_np (pthread_self (), &attr);
5833 pthread_attr_getstack (&attr, &sstart, &size);
5834 info->stack_start_limit = sstart;
5835 info->stack_end = (char*)sstart + size;
5836 pthread_attr_destroy (&attr);
5838 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5839 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5840 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5843 /* FIXME: we assume the stack grows down */
5844 gsize stack_bottom = (gsize)addr;
5845 stack_bottom += 4095;
5846 stack_bottom &= ~4095;
5847 info->stack_end = (char*)stack_bottom;
5851 #ifdef HAVE_KW_THREAD
5852 stack_end = info->stack_end;
5855 /* hash into the table */
5856 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
5857 info->next = thread_table [hash];
5858 thread_table [hash] = info;
5860 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
5861 pthread_setspecific (remembered_set_key, info->remset);
5862 #ifdef HAVE_KW_THREAD
5863 remembered_set = info->remset;
5866 STORE_REMSET_BUFFER = get_internal_mem (sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE, INTERNAL_MEM_STORE_REMSET);
5867 STORE_REMSET_BUFFER_INDEX = 0;
5869 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
5871 if (gc_callbacks.thread_attach_func)
5872 info->runtime_data = gc_callbacks.thread_attach_func ();
5878 add_generic_store_remset_from_buffer (gpointer *buffer)
5880 GenericStoreRememberedSet *remset = get_internal_mem (sizeof (GenericStoreRememberedSet), INTERNAL_MEM_STORE_REMSET);
5881 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5882 remset->next = generic_store_remsets;
5883 generic_store_remsets = remset;
5887 unregister_current_thread (void)
5890 SgenThreadInfo *prev = NULL;
5892 RememberedSet *rset;
5893 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
5895 binary_protocol_thread_unregister ((gpointer)id);
5897 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5898 p = thread_table [hash];
5900 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
5901 while (!ARCH_THREAD_EQUALS (p->id, id)) {
5906 thread_table [hash] = p->next;
5908 prev->next = p->next;
5911 if (freed_thread_remsets) {
5912 for (rset = p->remset; rset->next; rset = rset->next)
5914 rset->next = freed_thread_remsets;
5915 freed_thread_remsets = p->remset;
5917 freed_thread_remsets = p->remset;
5920 if (*p->store_remset_buffer_index_addr)
5921 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5922 free_internal_mem (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5927 unregister_thread (void *k)
5929 g_assert (!mono_domain_get ());
5931 unregister_current_thread ();
5936 mono_gc_register_thread (void *baseptr)
5938 SgenThreadInfo *info;
5942 info = thread_info_lookup (ARCH_GET_THREAD ());
5944 info = gc_register_current_thread (baseptr);
5946 return info != NULL;
5949 #if USE_PTHREAD_INTERCEPT
5951 #undef pthread_create
5953 #undef pthread_detach
5956 void *(*start_routine) (void *);
5959 MonoSemType registered;
5960 } SgenThreadStartInfo;
5963 gc_start_thread (void *arg)
5965 SgenThreadStartInfo *start_info = arg;
5966 SgenThreadInfo* info;
5967 void *t_arg = start_info->arg;
5968 void *(*start_func) (void*) = start_info->start_routine;
5973 info = gc_register_current_thread (&result);
5975 post_result = MONO_SEM_POST (&(start_info->registered));
5976 g_assert (!post_result);
5977 result = start_func (t_arg);
5978 g_assert (!mono_domain_get ());
5980 * this is done by the pthread key dtor
5982 unregister_current_thread ();
5990 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5992 SgenThreadStartInfo *start_info;
5995 start_info = malloc (sizeof (SgenThreadStartInfo));
5998 result = MONO_SEM_INIT (&(start_info->registered), 0);
6000 start_info->arg = arg;
6001 start_info->start_routine = start_routine;
6003 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6005 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6006 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6009 MONO_SEM_DESTROY (&(start_info->registered));
6015 mono_gc_pthread_join (pthread_t thread, void **retval)
6017 return pthread_join (thread, retval);
6021 mono_gc_pthread_detach (pthread_t thread)
6023 return pthread_detach (thread);
6026 #endif /* USE_PTHREAD_INTERCEPT */
6029 * ######################################################################
6030 * ######## Write barriers
6031 * ######################################################################
6034 static RememberedSet*
6035 alloc_remset (int size, gpointer id) {
6036 RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6037 res->store_next = res->data;
6038 res->end_set = res->data + size;
6040 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6045 * Note: the write barriers first do the needed GC work and then do the actual store:
6046 * this way the value is visible to the conservative GC scan after the write barrier
6047 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6048 * the conservative scan, otherwise by the remembered set scan.
6051 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6055 HEAVY_STAT (++stat_wbarrier_set_field);
6056 if (ptr_in_nursery (field_ptr)) {
6057 *(void**)field_ptr = value;
6060 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6062 rs = REMEMBERED_SET;
6063 if (rs->store_next < rs->end_set) {
6064 *(rs->store_next++) = (mword)field_ptr;
6065 *(void**)field_ptr = value;
6069 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6070 rs->next = REMEMBERED_SET;
6071 REMEMBERED_SET = rs;
6072 #ifdef HAVE_KW_THREAD
6073 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6075 *(rs->store_next++) = (mword)field_ptr;
6076 *(void**)field_ptr = value;
6081 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6085 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6086 if (ptr_in_nursery (slot_ptr)) {
6087 *(void**)slot_ptr = value;
6090 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6092 rs = REMEMBERED_SET;
6093 if (rs->store_next < rs->end_set) {
6094 *(rs->store_next++) = (mword)slot_ptr;
6095 *(void**)slot_ptr = value;
6099 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6100 rs->next = REMEMBERED_SET;
6101 REMEMBERED_SET = rs;
6102 #ifdef HAVE_KW_THREAD
6103 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6105 *(rs->store_next++) = (mword)slot_ptr;
6106 *(void**)slot_ptr = value;
6111 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6115 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6117 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6118 if (ptr_in_nursery (dest_ptr)) {
6122 rs = REMEMBERED_SET;
6123 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6124 if (rs->store_next + 1 < rs->end_set) {
6125 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6126 *(rs->store_next++) = count;
6130 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6131 rs->next = REMEMBERED_SET;
6132 REMEMBERED_SET = rs;
6133 #ifdef HAVE_KW_THREAD
6134 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6136 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6137 *(rs->store_next++) = count;
6142 find_object_for_ptr_in_area (char *ptr, char *start, char *end)
6144 while (start < end) {
6147 if (!*(void**)start) {
6148 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
6154 #define SCAN_OBJECT_NOSCAN
6155 #include "sgen-scan-object.h"
6157 if (ptr >= old_start && ptr < start)
6164 static char *found_obj;
6167 find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
6169 if (ptr >= obj && ptr < obj + size) {
6170 g_assert (!found_obj);
6175 /* for use in the debugger */
6176 char* find_object_for_ptr (char *ptr);
6178 find_object_for_ptr (char *ptr)
6182 if (ptr >= nursery_section->data && ptr < nursery_section->end_data)
6183 return find_object_for_ptr_in_area (ptr, nursery_section->data, nursery_section->end_data);
6185 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
6186 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
6187 return bigobj->data;
6191 * Very inefficient, but this is debugging code, supposed to
6192 * be called from gdb, so we don't care.
6195 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
6200 evacuate_remset_buffer (void)
6205 buffer = STORE_REMSET_BUFFER;
6207 add_generic_store_remset_from_buffer (buffer);
6208 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6210 STORE_REMSET_BUFFER_INDEX = 0;
6214 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6220 HEAVY_STAT (++stat_wbarrier_generic_store);
6222 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6223 /* FIXME: ptr_in_heap must be called with the GC lock held */
6224 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6225 char *start = find_object_for_ptr (ptr);
6226 MonoObject *value = *(MonoObject**)ptr;
6230 MonoObject *obj = (MonoObject*)start;
6231 if (obj->vtable->domain != value->vtable->domain)
6232 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6240 if (*(gpointer*)ptr)
6241 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6243 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6244 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6249 buffer = STORE_REMSET_BUFFER;
6250 index = STORE_REMSET_BUFFER_INDEX;
6251 /* This simple optimization eliminates a sizable portion of
6252 entries. Comparing it to the last but one entry as well
6253 doesn't eliminate significantly more entries. */
6254 if (buffer [index] == ptr) {
6259 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6260 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6263 if (index >= STORE_REMSET_BUFFER_SIZE) {
6264 evacuate_remset_buffer ();
6265 index = STORE_REMSET_BUFFER_INDEX;
6266 g_assert (index == 0);
6269 buffer [index] = ptr;
6270 STORE_REMSET_BUFFER_INDEX = index;
6276 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6278 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6279 *(void**)ptr = value;
6280 if (ptr_in_nursery (value))
6281 mono_gc_wbarrier_generic_nostore (ptr);
6285 mono_gc_wbarrier_set_root (gpointer ptr, MonoObject *value)
6289 HEAVY_STAT (++stat_wbarrier_set_root);
6290 if (ptr_in_nursery (ptr))
6292 DEBUG (8, fprintf (gc_debug_file, "Adding root remset at %p (%s)\n", ptr, value ? safe_name (value) : "null"));
6294 rs = REMEMBERED_SET;
6295 if (rs->store_next + 2 < rs->end_set) {
6296 *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
6297 *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
6298 *(void**)ptr = value;
6301 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6302 rs->next = REMEMBERED_SET;
6303 REMEMBERED_SET = rs;
6304 #ifdef HAVE_KW_THREAD
6305 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6307 *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
6308 *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
6310 *(void**)ptr = value;
6314 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6318 HEAVY_STAT (++stat_wbarrier_value_copy);
6319 g_assert (klass->valuetype);
6321 memmove (dest, src, count * mono_class_value_size (klass, NULL));
6322 rs = REMEMBERED_SET;
6323 if (ptr_in_nursery (dest) || ptr_on_stack (dest)) {
6327 g_assert (klass->gc_descr_inited);
6328 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));
6330 if (rs->store_next + 3 < rs->end_set) {
6331 *(rs->store_next++) = (mword)dest | REMSET_OTHER;
6332 *(rs->store_next++) = (mword)REMSET_VTYPE;
6333 *(rs->store_next++) = (mword)klass->gc_descr;
6334 *(rs->store_next++) = (mword)count;
6338 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6339 rs->next = REMEMBERED_SET;
6340 REMEMBERED_SET = rs;
6341 #ifdef HAVE_KW_THREAD
6342 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6344 *(rs->store_next++) = (mword)dest | REMSET_OTHER;
6345 *(rs->store_next++) = (mword)REMSET_VTYPE;
6346 *(rs->store_next++) = (mword)klass->gc_descr;
6347 *(rs->store_next++) = (mword)count;
6352 * mono_gc_wbarrier_object_copy:
6354 * Write barrier to call when obj is the result of a clone or copy of an object.
6357 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6363 HEAVY_STAT (++stat_wbarrier_object_copy);
6364 rs = REMEMBERED_SET;
6365 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6366 size = mono_object_class (obj)->instance_size;
6368 /* do not copy the sync state */
6369 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6370 size - sizeof (MonoObject));
6371 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6375 if (rs->store_next < rs->end_set) {
6376 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6380 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6381 rs->next = REMEMBERED_SET;
6382 REMEMBERED_SET = rs;
6383 #ifdef HAVE_KW_THREAD
6384 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6386 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6391 * ######################################################################
6392 * ######## Collector debugging
6393 * ######################################################################
6396 const char*descriptor_types [] = {
6408 describe_ptr (char *ptr)
6414 if (ptr_in_nursery (ptr)) {
6415 printf ("Pointer inside nursery.\n");
6417 if (major_ptr_is_in_non_pinned_space (ptr)) {
6418 printf ("Pointer inside oldspace.\n");
6419 } else if (obj_is_from_pinned_alloc (ptr)) {
6420 printf ("Pointer is inside a pinned chunk.\n");
6422 printf ("Pointer unknown.\n");
6427 if (object_is_pinned (ptr))
6428 printf ("Object is pinned.\n");
6430 if (object_is_forwarded (ptr))
6431 printf ("Object is forwared.\n");
6433 // FIXME: Handle pointers to the inside of objects
6434 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6436 printf ("VTable: %p\n", vtable);
6437 if (vtable == NULL) {
6438 printf ("VTable is invalid (empty).\n");
6441 if (ptr_in_nursery (vtable)) {
6442 printf ("VTable is invalid (points inside nursery).\n");
6445 printf ("Class: %s\n", vtable->klass->name);
6447 desc = ((GCVTable*)vtable)->desc;
6448 printf ("Descriptor: %lx\n", (long)desc);
6451 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6455 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6461 switch ((*p) & REMSET_TYPE_MASK) {
6462 case REMSET_LOCATION:
6463 if (*p == (mword)addr)
6467 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6469 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6473 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6474 count = safe_object_get_size ((MonoObject*)ptr);
6475 count += (ALLOC_ALIGN - 1);
6476 count &= (ALLOC_ALIGN - 1);
6477 count /= sizeof (mword);
6478 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6481 case REMSET_OTHER: {
6484 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6488 switch (desc & 0x7) {
6489 case DESC_TYPE_RUN_LENGTH:
6490 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6492 case DESC_TYPE_SMALL_BITMAP:
6493 OBJ_BITMAP_SIZE (skip_size, desc, start);
6497 g_assert_not_reached ();
6500 /* The descriptor includes the size of MonoObject */
6501 skip_size -= sizeof (MonoObject);
6503 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6507 case REMSET_ROOT_LOCATION:
6510 g_assert_not_reached ();
6515 g_assert_not_reached ();
6521 * Return whenever ADDR occurs in the remembered sets
6524 find_in_remsets (char *addr)
6527 SgenThreadInfo *info;
6528 RememberedSet *remset;
6529 GenericStoreRememberedSet *store_remset;
6531 gboolean found = FALSE;
6533 /* the global one */
6534 for (remset = global_remset; remset; remset = remset->next) {
6535 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));
6536 for (p = remset->data; p < remset->store_next;) {
6537 p = find_in_remset_loc (p, addr, &found);
6543 /* the generic store ones */
6544 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6545 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6546 if (store_remset->data [i] == addr)
6551 /* the per-thread ones */
6552 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6553 for (info = thread_table [i]; info; info = info->next) {
6555 for (remset = info->remset; remset; remset = remset->next) {
6556 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));
6557 for (p = remset->data; p < remset->store_next;) {
6558 p = find_in_remset_loc (p, addr, &found);
6563 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6564 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6570 /* the freed thread ones */
6571 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6572 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));
6573 for (p = remset->data; p < remset->store_next;) {
6574 p = find_in_remset_loc (p, addr, &found);
6583 static gboolean missing_remsets;
6586 * We let a missing remset slide if the target object is pinned,
6587 * because the store might have happened but the remset not yet added,
6588 * but in that case the target must be pinned. We might theoretically
6589 * miss some missing remsets this way, but it's very unlikely.
6592 #define HANDLE_PTR(ptr,obj) do { \
6593 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6594 if (!find_in_remsets ((char*)(ptr))) { \
6595 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); \
6596 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6597 if (!object_is_pinned (*(ptr))) \
6598 missing_remsets = TRUE; \
6604 * Check that each object reference which points into the nursery can
6605 * be found in the remembered sets.
6608 check_consistency_callback (char *start, size_t size, void *dummy)
6610 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6611 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6613 #define SCAN_OBJECT_ACTION
6614 #include "sgen-scan-object.h"
6618 * Perform consistency check of the heap.
6620 * Assumes the world is stopped.
6623 check_consistency (void)
6625 // Need to add more checks
6627 missing_remsets = FALSE;
6629 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6631 // Check that oldspace->newspace pointers are registered with the collector
6632 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6634 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6636 #ifdef BINARY_PROTOCOL
6637 if (!binary_protocol_file)
6639 g_assert (!missing_remsets);
6642 /* Check that the reference is valid */
6644 #define HANDLE_PTR(ptr,obj) do { \
6646 g_assert (safe_name (*(ptr)) != NULL); \
6653 * Perform consistency check on an object. Currently we only check that the
6654 * reference fields are valid.
6657 check_object (char *start)
6662 #include "sgen-scan-object.h"
6668 * ######################################################################
6669 * ######## Other mono public interface functions.
6670 * ######################################################################
6674 mono_gc_collect (int generation)
6678 if (generation == 0) {
6679 collect_nursery (0);
6681 major_collection ("user request");
6688 mono_gc_max_generation (void)
6694 mono_gc_collection_count (int generation)
6696 if (generation == 0)
6697 return num_minor_gcs;
6698 return num_major_gcs;
6702 mono_gc_get_used_size (void)
6706 tot = los_memory_usage;
6707 tot += nursery_section->next_data - nursery_section->data;
6708 tot += major_get_used_size ();
6709 /* FIXME: account for pinned objects */
6715 mono_gc_get_heap_size (void)
6721 mono_gc_disable (void)
6729 mono_gc_enable (void)
6737 mono_gc_get_los_limit (void)
6739 return MAX_SMALL_OBJ_SIZE;
6743 mono_object_is_alive (MonoObject* o)
6749 mono_gc_get_generation (MonoObject *obj)
6751 if (ptr_in_nursery (obj))
6757 mono_gc_enable_events (void)
6762 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6765 mono_gc_register_disappearing_link (obj, link_addr, track);
6770 mono_gc_weak_link_remove (void **link_addr)
6773 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
6778 mono_gc_weak_link_get (void **link_addr)
6782 return (MonoObject*) REVEAL_POINTER (*link_addr);
6786 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6788 if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6789 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6791 mword complex = alloc_complex_descriptor (bitmap, numbits);
6792 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6797 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6801 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6802 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6803 user_descriptors [user_descriptors_next ++] = marker;
6809 mono_gc_alloc_fixed (size_t size, void *descr)
6811 /* FIXME: do a single allocation */
6812 void *res = calloc (1, size);
6815 if (!mono_gc_register_root (res, size, descr)) {
6823 mono_gc_free_fixed (void* addr)
6825 mono_gc_deregister_root (addr);
6830 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6834 result = func (data);
6835 UNLOCK_INTERRUPTION;
6840 mono_gc_is_gc_thread (void)
6844 result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
6851 /* Tries to extract a number from the passed string, taking in to account m, k
6854 parse_environment_string_extract_number (gchar *str, glong *out)
6857 int len = strlen (str), shift = 0;
6859 gboolean is_suffix = FALSE;
6862 switch (str [len - 1]) {
6873 suffix = str [len - 1];
6878 val = strtol (str, &endptr, 10);
6880 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
6881 || (errno != 0 && val == 0) || (endptr == str))
6885 if (*(endptr + 1)) /* Invalid string. */
6897 mono_gc_base_init (void)
6901 struct sigaction sinfo;
6903 LOCK_INIT (gc_mutex);
6905 if (gc_initialized) {
6909 pagesize = mono_pagesize ();
6910 gc_debug_file = stderr;
6914 if ((env = getenv ("MONO_GC_PARAMS"))) {
6915 if (g_str_has_prefix (env, "nursery-size")) {
6918 while (env [index] && env [index++] != '=')
6920 if (env [index] && parse_environment_string_extract_number (env
6922 default_nursery_size = val;
6923 #ifdef ALIGN_NURSERY
6924 if ((val & (val - 1))) {
6925 fprintf (stderr, "The nursery size must be a power of two.\n");
6929 default_nursery_bits = 0;
6930 while (1 << (++ default_nursery_bits) != default_nursery_size)
6934 fprintf (stderr, "nursery-size must be an integer.\n");
6938 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");
6945 nursery_size = DEFAULT_NURSERY_SIZE;
6949 if ((env = getenv ("MONO_GC_DEBUG"))) {
6950 opts = g_strsplit (env, ",", -1);
6951 for (ptr = opts; ptr && *ptr; ptr ++) {
6953 if (opt [0] >= '0' && opt [0] <= '9') {
6954 gc_debug_level = atoi (opt);
6959 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
6960 gc_debug_file = fopen (rf, "wb");
6962 gc_debug_file = stderr;
6965 } else if (!strcmp (opt, "collect-before-allocs")) {
6966 collect_before_allocs = TRUE;
6967 } else if (!strcmp (opt, "check-at-minor-collections")) {
6968 consistency_check_at_minor_collection = TRUE;
6969 } else if (!strcmp (opt, "xdomain-checks")) {
6970 xdomain_checks = TRUE;
6971 } else if (!strcmp (opt, "clear-at-gc")) {
6972 nursery_clear_policy = CLEAR_AT_GC;
6973 } else if (!strcmp (opt, "conservative-stack-mark")) {
6974 conservative_stack_mark = TRUE;
6975 } else if (!strcmp (opt, "check-scan-starts")) {
6976 do_scan_starts_check = TRUE;
6977 } else if (g_str_has_prefix (opt, "heap-dump=")) {
6978 char *filename = strchr (opt, '=') + 1;
6979 nursery_clear_policy = CLEAR_AT_GC;
6980 heap_dump_file = fopen (filename, "w");
6982 fprintf (heap_dump_file, "<sgen-dump>\n");
6983 #ifdef BINARY_PROTOCOL
6984 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
6985 char *filename = strchr (opt, '=') + 1;
6986 binary_protocol_file = fopen (filename, "w");
6989 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
6990 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
6991 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
6998 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
6999 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7001 sigfillset (&sinfo.sa_mask);
7002 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7003 sinfo.sa_sigaction = suspend_handler;
7004 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7005 g_error ("failed sigaction");
7008 sinfo.sa_handler = restart_handler;
7009 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7010 g_error ("failed sigaction");
7013 sigfillset (&suspend_signal_mask);
7014 sigdelset (&suspend_signal_mask, restart_signal_num);
7016 global_remset = alloc_remset (1024, NULL);
7017 global_remset->next = NULL;
7019 pthread_key_create (&remembered_set_key, unregister_thread);
7021 #ifndef HAVE_KW_THREAD
7022 pthread_key_create (&thread_info_key, NULL);
7025 gc_initialized = TRUE;
7027 mono_gc_register_thread (&sinfo);
7031 mono_gc_get_suspend_signal (void)
7033 return suspend_signal_num;
7043 #ifdef HAVE_KW_THREAD
7044 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7045 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7046 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7047 mono_mb_emit_i4 ((mb), (offset)); \
7050 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7051 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7052 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7053 mono_mb_emit_i4 ((mb), thread_info_key); \
7054 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7055 mono_mb_emit_byte ((mb), CEE_ADD); \
7056 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7060 #ifdef MANAGED_ALLOCATION
7061 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7062 * for each class. This is currently not easy to do, as it is hard to generate basic
7063 * blocks + branches, but it is easy with the linear IL codebase.
7065 * For this to work we'd need to solve the TLAB race, first. Now we
7066 * require the allocator to be in a few known methods to make sure
7067 * that they are executed atomically via the restart mechanism.
7070 create_allocator (int atype)
7072 int p_var, size_var;
7073 guint32 slowpath_branch, max_size_branch;
7074 MonoMethodBuilder *mb;
7076 MonoMethodSignature *csig;
7077 static gboolean registered = FALSE;
7078 int tlab_next_addr_var, new_next_var;
7080 const char *name = NULL;
7081 AllocatorWrapperInfo *info;
7083 #ifdef HAVE_KW_THREAD
7084 int tlab_next_addr_offset = -1;
7085 int tlab_temp_end_offset = -1;
7087 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7088 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7090 g_assert (tlab_next_addr_offset != -1);
7091 g_assert (tlab_temp_end_offset != -1);
7095 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7096 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7100 if (atype == ATYPE_SMALL) {
7102 name = "AllocSmall";
7103 } else if (atype == ATYPE_NORMAL) {
7106 } else if (atype == ATYPE_VECTOR) {
7108 name = "AllocVector";
7110 g_assert_not_reached ();
7113 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7114 csig->ret = &mono_defaults.object_class->byval_arg;
7115 for (i = 0; i < num_params; ++i)
7116 csig->params [i] = &mono_defaults.int_class->byval_arg;
7118 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7119 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7120 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7121 /* size = vtable->klass->instance_size; */
7122 mono_mb_emit_ldarg (mb, 0);
7123 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7124 mono_mb_emit_byte (mb, CEE_ADD);
7125 mono_mb_emit_byte (mb, CEE_LDIND_I);
7126 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7127 mono_mb_emit_byte (mb, CEE_ADD);
7128 /* FIXME: assert instance_size stays a 4 byte integer */
7129 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7130 mono_mb_emit_stloc (mb, size_var);
7131 } else if (atype == ATYPE_VECTOR) {
7132 MonoExceptionClause *clause;
7134 MonoClass *oom_exc_class;
7137 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7138 mono_mb_emit_ldarg (mb, 1);
7139 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7140 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7141 mono_mb_emit_exception (mb, "OverflowException", NULL);
7142 mono_mb_patch_short_branch (mb, pos);
7144 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7145 clause->try_offset = mono_mb_get_label (mb);
7147 /* vtable->klass->sizes.element_size */
7148 mono_mb_emit_ldarg (mb, 0);
7149 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7150 mono_mb_emit_byte (mb, CEE_ADD);
7151 mono_mb_emit_byte (mb, CEE_LDIND_I);
7152 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7153 mono_mb_emit_byte (mb, CEE_ADD);
7154 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7157 mono_mb_emit_ldarg (mb, 1);
7158 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7159 /* + sizeof (MonoArray) */
7160 mono_mb_emit_icon (mb, sizeof (MonoArray));
7161 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7162 mono_mb_emit_stloc (mb, size_var);
7164 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7167 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7168 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7169 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7170 "System", "OverflowException");
7171 g_assert (clause->data.catch_class);
7172 clause->handler_offset = mono_mb_get_label (mb);
7174 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7175 "System", "OutOfMemoryException");
7176 g_assert (oom_exc_class);
7177 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7180 mono_mb_emit_byte (mb, CEE_POP);
7181 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7182 mono_mb_emit_byte (mb, CEE_THROW);
7184 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7185 mono_mb_set_clauses (mb, 1, clause);
7186 mono_mb_patch_branch (mb, pos_leave);
7189 g_assert_not_reached ();
7192 /* size += ALLOC_ALIGN - 1; */
7193 mono_mb_emit_ldloc (mb, size_var);
7194 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7195 mono_mb_emit_byte (mb, CEE_ADD);
7196 /* size &= ~(ALLOC_ALIGN - 1); */
7197 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7198 mono_mb_emit_byte (mb, CEE_AND);
7199 mono_mb_emit_stloc (mb, size_var);
7201 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7202 if (atype != ATYPE_SMALL) {
7203 mono_mb_emit_ldloc (mb, size_var);
7204 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7205 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7209 * We need to modify tlab_next, but the JIT only supports reading, so we read
7210 * another tls var holding its address instead.
7213 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7214 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7215 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7216 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7218 /* p = (void**)tlab_next; */
7219 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7220 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7221 mono_mb_emit_byte (mb, CEE_LDIND_I);
7222 mono_mb_emit_stloc (mb, p_var);
7224 /* new_next = (char*)p + size; */
7225 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7226 mono_mb_emit_ldloc (mb, p_var);
7227 mono_mb_emit_ldloc (mb, size_var);
7228 mono_mb_emit_byte (mb, CEE_CONV_I);
7229 mono_mb_emit_byte (mb, CEE_ADD);
7230 mono_mb_emit_stloc (mb, new_next_var);
7232 /* tlab_next = new_next */
7233 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7234 mono_mb_emit_ldloc (mb, new_next_var);
7235 mono_mb_emit_byte (mb, CEE_STIND_I);
7237 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7238 mono_mb_emit_ldloc (mb, new_next_var);
7239 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7240 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7243 if (atype != ATYPE_SMALL)
7244 mono_mb_patch_short_branch (mb, max_size_branch);
7246 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7247 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7249 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7250 mono_mb_emit_ldarg (mb, 0);
7251 mono_mb_emit_ldloc (mb, size_var);
7252 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7253 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7254 } else if (atype == ATYPE_VECTOR) {
7255 mono_mb_emit_ldarg (mb, 1);
7256 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7258 g_assert_not_reached ();
7260 mono_mb_emit_byte (mb, CEE_RET);
7263 mono_mb_patch_short_branch (mb, slowpath_branch);
7265 /* FIXME: Memory barrier */
7268 mono_mb_emit_ldloc (mb, p_var);
7269 mono_mb_emit_ldarg (mb, 0);
7270 mono_mb_emit_byte (mb, CEE_STIND_I);
7272 if (atype == ATYPE_VECTOR) {
7273 /* arr->max_length = max_length; */
7274 mono_mb_emit_ldloc (mb, p_var);
7275 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7276 mono_mb_emit_ldarg (mb, 1);
7277 mono_mb_emit_byte (mb, CEE_STIND_I);
7281 mono_mb_emit_ldloc (mb, p_var);
7282 mono_mb_emit_byte (mb, CEE_RET);
7284 res = mono_mb_create_method (mb, csig, 8);
7286 mono_method_get_header (res)->init_locals = FALSE;
7288 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7289 info->alloc_type = atype;
7290 mono_marshal_set_wrapper_info (res, info);
7296 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7297 static MonoMethod *write_barrier_method;
7300 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7308 ji = mono_jit_info_table_find (domain, ip);
7311 method = ji->method;
7313 if (method == write_barrier_method)
7315 for (i = 0; i < ATYPE_NUM; ++i)
7316 if (method == alloc_method_cache [i])
7322 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7323 * The signature of the called method is:
7324 * object allocate (MonoVTable *vtable)
7327 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7329 #ifdef MANAGED_ALLOCATION
7330 MonoClass *klass = vtable->klass;
7332 #ifdef HAVE_KW_THREAD
7333 int tlab_next_offset = -1;
7334 int tlab_temp_end_offset = -1;
7335 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7336 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7338 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7342 if (!mono_runtime_has_tls_get ())
7344 if (klass->instance_size > tlab_size)
7346 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7350 if (klass->byval_arg.type == MONO_TYPE_STRING)
7352 if (collect_before_allocs)
7355 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7356 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7358 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7365 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7367 #ifdef MANAGED_ALLOCATION
7368 MonoClass *klass = vtable->klass;
7370 #ifdef HAVE_KW_THREAD
7371 int tlab_next_offset = -1;
7372 int tlab_temp_end_offset = -1;
7373 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7374 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7376 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7382 if (!mono_runtime_has_tls_get ())
7384 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7386 if (collect_before_allocs)
7388 g_assert (!klass->has_finalize && !klass->marshalbyref);
7390 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7397 mono_gc_get_managed_allocator_by_type (int atype)
7399 #ifdef MANAGED_ALLOCATION
7402 if (!mono_runtime_has_tls_get ())
7405 mono_loader_lock ();
7406 res = alloc_method_cache [atype];
7408 res = alloc_method_cache [atype] = create_allocator (atype);
7409 mono_loader_unlock ();
7417 mono_gc_get_managed_allocator_types (void)
7424 mono_gc_get_write_barrier (void)
7427 MonoMethodBuilder *mb;
7428 MonoMethodSignature *sig;
7429 #ifdef MANAGED_WBARRIER
7430 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7431 #ifndef ALIGN_NURSERY
7432 int label_continue_1, label_continue_2, label_no_wb_5;
7433 int dereferenced_var;
7435 int buffer_var, buffer_index_var, dummy_var;
7437 #ifdef HAVE_KW_THREAD
7438 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7439 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7441 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7442 g_assert (stack_end_offset != -1);
7443 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7444 g_assert (store_remset_buffer_offset != -1);
7445 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7446 g_assert (store_remset_buffer_index_offset != -1);
7447 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7448 g_assert (store_remset_buffer_index_addr_offset != -1);
7452 // FIXME: Maybe create a separate version for ctors (the branch would be
7453 // correctly predicted more times)
7454 if (write_barrier_method)
7455 return write_barrier_method;
7457 /* Create the IL version of mono_gc_barrier_generic_store () */
7458 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7459 sig->ret = &mono_defaults.void_class->byval_arg;
7460 sig->params [0] = &mono_defaults.int_class->byval_arg;
7462 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7464 #ifdef MANAGED_WBARRIER
7465 if (mono_runtime_has_tls_get ()) {
7466 #ifdef ALIGN_NURSERY
7467 // if (ptr_in_nursery (ptr)) return;
7469 * Masking out the bits might be faster, but we would have to use 64 bit
7470 * immediates, which might be slower.
7472 mono_mb_emit_ldarg (mb, 0);
7473 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7474 mono_mb_emit_byte (mb, CEE_SHR_UN);
7475 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7476 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7478 // if (!ptr_in_nursery (*ptr)) return;
7479 mono_mb_emit_ldarg (mb, 0);
7480 mono_mb_emit_byte (mb, CEE_LDIND_I);
7481 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7482 mono_mb_emit_byte (mb, CEE_SHR_UN);
7483 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7484 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7487 // if (ptr < (nursery_start)) goto continue;
7488 mono_mb_emit_ldarg (mb, 0);
7489 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7490 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7492 // if (ptr >= nursery_real_end)) goto continue;
7493 mono_mb_emit_ldarg (mb, 0);
7494 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7495 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7498 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7501 mono_mb_patch_branch (mb, label_continue_1);
7502 mono_mb_patch_branch (mb, label_continue_2);
7504 // Dereference and store in local var
7505 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7506 mono_mb_emit_ldarg (mb, 0);
7507 mono_mb_emit_byte (mb, CEE_LDIND_I);
7508 mono_mb_emit_stloc (mb, dereferenced_var);
7510 // if (*ptr < nursery_start) return;
7511 mono_mb_emit_ldloc (mb, dereferenced_var);
7512 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7513 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7515 // if (*ptr >= nursery_end) return;
7516 mono_mb_emit_ldloc (mb, dereferenced_var);
7517 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7518 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7521 // if (ptr >= stack_end) goto need_wb;
7522 mono_mb_emit_ldarg (mb, 0);
7523 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7524 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7526 // if (ptr >= stack_start) return;
7527 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7528 mono_mb_emit_ldarg (mb, 0);
7529 mono_mb_emit_ldloc_addr (mb, dummy_var);
7530 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7533 mono_mb_patch_branch (mb, label_need_wb);
7535 // buffer = STORE_REMSET_BUFFER;
7536 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7537 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7538 mono_mb_emit_stloc (mb, buffer_var);
7540 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7541 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7542 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7543 mono_mb_emit_stloc (mb, buffer_index_var);
7545 // if (buffer [buffer_index] == ptr) return;
7546 mono_mb_emit_ldloc (mb, buffer_var);
7547 mono_mb_emit_ldloc (mb, buffer_index_var);
7548 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7549 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7550 mono_mb_emit_byte (mb, CEE_SHL);
7551 mono_mb_emit_byte (mb, CEE_ADD);
7552 mono_mb_emit_byte (mb, CEE_LDIND_I);
7553 mono_mb_emit_ldarg (mb, 0);
7554 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7557 mono_mb_emit_ldloc (mb, buffer_index_var);
7558 mono_mb_emit_icon (mb, 1);
7559 mono_mb_emit_byte (mb, CEE_ADD);
7560 mono_mb_emit_stloc (mb, buffer_index_var);
7562 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7563 mono_mb_emit_ldloc (mb, buffer_index_var);
7564 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7565 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7567 // buffer [buffer_index] = ptr;
7568 mono_mb_emit_ldloc (mb, buffer_var);
7569 mono_mb_emit_ldloc (mb, buffer_index_var);
7570 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7571 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7572 mono_mb_emit_byte (mb, CEE_SHL);
7573 mono_mb_emit_byte (mb, CEE_ADD);
7574 mono_mb_emit_ldarg (mb, 0);
7575 mono_mb_emit_byte (mb, CEE_STIND_I);
7577 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7578 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7579 mono_mb_emit_ldloc (mb, buffer_index_var);
7580 mono_mb_emit_byte (mb, CEE_STIND_I);
7583 mono_mb_patch_branch (mb, label_no_wb_1);
7584 mono_mb_patch_branch (mb, label_no_wb_2);
7585 mono_mb_patch_branch (mb, label_no_wb_3);
7586 mono_mb_patch_branch (mb, label_no_wb_4);
7587 #ifndef ALIGN_NURSERY
7588 mono_mb_patch_branch (mb, label_no_wb_5);
7590 mono_mb_emit_byte (mb, CEE_RET);
7593 mono_mb_patch_branch (mb, label_slow_path);
7597 mono_mb_emit_ldarg (mb, 0);
7598 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7599 mono_mb_emit_byte (mb, CEE_RET);
7601 res = mono_mb_create_method (mb, sig, 16);
7604 mono_loader_lock ();
7605 if (write_barrier_method) {
7606 /* Already created */
7607 mono_free_method (res);
7609 /* double-checked locking */
7610 mono_memory_barrier ();
7611 write_barrier_method = res;
7613 mono_loader_unlock ();
7615 return write_barrier_method;
7618 #endif /* HAVE_SGEN_GC */