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,
334 INTERNAL_MEM_EPHEMERON_LINK,
338 static long small_internal_mem_bytes [INTERNAL_MEM_MAX];
342 mono_gc_flush_info (void)
344 fflush (gc_debug_file);
348 #define MAX_DEBUG_LEVEL 2
349 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
351 /* Define this to allow the user to change some of the constants by specifying
352 * their values in the MONO_GC_PARAMS environmental variable. See
353 * mono_gc_base_init for details. */
354 #define USER_CONFIG 1
356 #define TV_DECLARE(name) gint64 name
357 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
358 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
359 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
361 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
363 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
371 typedef struct _Block Block;
377 /* each request from the OS ends up in a GCMemSection */
378 typedef struct _GCMemSection GCMemSection;
379 struct _GCMemSection {
383 /* pointer where more data could be allocated if it fits */
387 * scan starts is an array of pointers to objects equally spaced in the allocation area
388 * They let use quickly find pinned objects from pinning pointers.
391 /* in major collections indexes in the pin_queue for objects that pin this section */
394 unsigned short num_scan_start;
395 gboolean is_to_space;
398 #define SIZEOF_GC_MEM_SECTION ((sizeof (GCMemSection) + 7) & ~7)
400 /* large object space struct: 64+ KB */
401 /* we could make this limit much smaller to avoid memcpy copy
402 * and potentially have more room in the GC descriptor: need to measure
403 * This also means that such small OS objects will need to be
404 * allocated in a different way (using pinned chunks).
405 * We may want to put large but smaller than 64k objects in the fixed space
406 * when we move the object from one generation to another (to limit the
407 * pig in the snake effect).
408 * Note: it may be worth to have an optimized copy function, since we can
409 * assume that objects are aligned and have a multiple of 8 size.
410 * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
411 * true if MONO_ZERO_LEN_ARRAY is nonzero.
413 typedef struct _LOSObject LOSObject;
416 mword size; /* this is the object size */
418 int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN and data starting at same alignment */
419 char data [MONO_ZERO_LEN_ARRAY];
422 /* Pinned objects are allocated in the LOS space if bigger than half a page
423 * or from freelists otherwise. We assume that pinned objects are relatively few
424 * and they have a slow dying speed (like interned strings, thread objects).
425 * As such they will be collected only at major collections.
426 * free lists are not global: when we need memory we allocate a PinnedChunk.
427 * Each pinned chunk is made of several pages, the first of wich is used
428 * internally for bookeeping (here think of a page as 4KB). The bookeeping
429 * includes the freelists vectors and info about the object size of each page
430 * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
431 * a size is assigned to it, the page is divided in the proper chunks and each
432 * chunk is added to the freelist. To not waste space, the remaining space in the
433 * first page is used as objects of size 16 or 32 (need to measure which are more
435 * We use this same structure to allocate memory used internally by the GC, so
436 * we never use malloc/free if we need to alloc during collection: the world is stopped
437 * and malloc/free will deadlock.
438 * When we want to iterate over pinned objects, we just scan a page at a time
439 * linearly according to the size of objects in the page: the next pointer used to link
440 * the items in the freelist uses the same word as the vtable. Since we keep freelists
441 * for each pinned chunk, if the word points outside the pinned chunk it means
443 * We could avoid this expensive scanning in creative ways. We could have a policy
444 * of putting in the pinned space only objects we know about that have no struct fields
445 * with references and we can easily use a even expensive write barrier for them,
446 * since pointer writes on such objects should be rare.
447 * The best compromise is to just alloc interned strings and System.MonoType in them.
448 * It would be nice to allocate MonoThread in it, too: must check that we properly
449 * use write barriers so we don't have to do any expensive scanning of the whole pinned
450 * chunk list during minor collections. We can avoid it now because we alloc in it only
451 * reference-free objects.
453 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
454 #define MAX_FREELIST_SIZE 2048
455 #define PINNED_PAGE_SIZE (4096)
456 #define PINNED_CHUNK_MIN_SIZE (4096*8)
457 typedef struct _PinnedChunk PinnedChunk;
458 struct _PinnedChunk {
461 int *page_sizes; /* a 0 means the page is still unused */
464 void *data [1]; /* page sizes and free lists are stored here */
467 /* The method used to clear the nursery */
468 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
469 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
474 CLEAR_AT_TLAB_CREATION
475 } NurseryClearPolicy;
477 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
480 * If this is set, the nursery is aligned to an address aligned to its size, ie.
481 * a 1MB nursery will be aligned to an address divisible by 1MB. This allows us to
482 * speed up ptr_in_nursery () checks which are very frequent. This requires the
483 * nursery size to be a compile time constant.
485 #define ALIGN_NURSERY 1
488 * The young generation is divided into fragments. This is because
489 * we can hand one fragments to a thread for lock-less fast alloc and
490 * because the young generation ends up fragmented anyway by pinned objects.
491 * Once a collection is done, a list of fragments is created. When doing
492 * thread local alloc we use smallish nurseries so we allow new threads to
493 * allocate memory from gen0 without triggering a collection. Threads that
494 * are found to allocate lots of memory are given bigger fragments. This
495 * should make the finalizer thread use little nursery memory after a while.
496 * We should start assigning threads very small fragments: if there are many
497 * threads the nursery will be full of reserved space that the threads may not
498 * use at all, slowing down allocation speed.
499 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
500 * Allocation Buffers (TLABs).
502 typedef struct _Fragment Fragment;
506 char *fragment_start;
507 char *fragment_limit; /* the current soft limit for allocation */
511 /* the runtime can register areas of memory as roots: we keep two lists of roots,
512 * a pinned root set for conservatively scanned roots and a normal one for
513 * precisely scanned roots (currently implemented as a single list).
515 typedef struct _RootRecord RootRecord;
523 /* for use with write barriers */
524 typedef struct _RememberedSet RememberedSet;
525 struct _RememberedSet {
529 mword data [MONO_ZERO_LEN_ARRAY];
533 * We're never actually using the first element. It's always set to
534 * NULL to simplify the elimination of consecutive duplicate
537 #define STORE_REMSET_BUFFER_SIZE 1024
539 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
540 struct _GenericStoreRememberedSet {
541 GenericStoreRememberedSet *next;
542 /* We need one entry less because the first entry of store
543 remset buffers is always a dummy and we don't copy it. */
544 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
547 /* we have 4 possible values in the low 2 bits */
549 REMSET_LOCATION, /* just a pointer to the exact location */
550 REMSET_RANGE, /* range of pointer fields */
551 REMSET_OBJECT, /* mark all the object for scanning */
552 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
553 REMSET_TYPE_MASK = 0x3
556 #ifdef HAVE_KW_THREAD
557 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
559 static pthread_key_t remembered_set_key;
560 static RememberedSet *global_remset;
561 static RememberedSet *freed_thread_remsets;
562 static GenericStoreRememberedSet *generic_store_remsets = NULL;
564 /* FIXME: later choose a size that takes into account the RememberedSet struct
565 * and doesn't waste any alloc paddin space.
567 #define DEFAULT_REMSET_SIZE 1024
568 static RememberedSet* alloc_remset (int size, gpointer id);
570 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
571 * no cast from a pointer to an integer
578 /* these bits are set in the object vtable: we could merge them since an object can be
579 * either pinned or forwarded but not both.
580 * We store them in the vtable slot because the bits are used in the sync block for
581 * other purposes: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
582 * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
583 * would be an invalid combination for the monitor and hash code).
584 * The values are already shifted.
585 * The forwarding address is stored in the sync block.
587 #define FORWARDED_BIT 1
589 #define VTABLE_BITS_MASK 0x3
591 /* returns NULL if not forwarded, or the forwarded address */
592 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
593 /* set the forwarded address fw_addr for object obj */
594 #define forward_object(obj,fw_addr) do { \
595 ((mword*)(obj))[0] |= FORWARDED_BIT; \
596 ((mword*)(obj))[1] = (mword)(fw_addr); \
599 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
600 #define pin_object(obj) do { \
601 ((mword*)(obj))[0] |= PINNED_BIT; \
603 #define unpin_object(obj) do { \
604 ((mword*)(obj))[0] &= ~PINNED_BIT; \
608 #define ptr_in_nursery(ptr) (((mword)(ptr) & ~((1 << DEFAULT_NURSERY_BITS) - 1)) == (mword)nursery_start)
610 #define ptr_in_nursery(ptr) ((char*)(ptr) >= nursery_start && (char*)(ptr) < nursery_real_end)
614 * Since we set bits in the vtable, use the macro to load it from the pointer to
615 * an object that is potentially pinned.
617 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
620 safe_name (void* obj)
622 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
623 return vt->klass->name;
627 safe_object_get_size (MonoObject* o)
629 MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
630 if (klass == mono_defaults.string_class) {
631 return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
632 } else if (klass->rank) {
633 MonoArray *array = (MonoArray*)o;
634 size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length (array);
635 if (G_UNLIKELY (array->bounds)) {
636 size += sizeof (mono_array_size_t) - 1;
637 size &= ~(sizeof (mono_array_size_t) - 1);
638 size += sizeof (MonoArrayBounds) * klass->rank;
642 /* from a created object: the class must be inited already */
643 return klass->instance_size;
648 * ######################################################################
649 * ######## Global data.
650 * ######################################################################
652 static LOCK_DECLARE (gc_mutex);
653 static int gc_disabled = 0;
654 static int num_minor_gcs = 0;
655 static int num_major_gcs = 0;
659 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
660 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
661 static int default_nursery_size = (1 << 20);
663 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
664 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
665 static int default_nursery_bits = 20;
670 #define DEFAULT_NURSERY_SIZE (1024*512*2)
672 #define DEFAULT_NURSERY_BITS 20
677 #define MIN_LOS_ALLOWANCE (DEFAULT_NURSERY_SIZE * 2)
678 /* to quickly find the head of an object pinned by a conservative address
679 * we keep track of the objects allocated for each SCAN_START_SIZE memory
680 * chunk in the nursery or other memory sections. Larger values have less
681 * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
683 #define SCAN_START_SIZE (4096*2)
684 /* the minimum size of a fragment that we consider useful for allocation */
685 #define FRAGMENT_MIN_SIZE (512)
686 /* This is a fixed value used for pinned chunks, not the system pagesize */
687 #define FREELIST_PAGESIZE 4096
689 static mword pagesize = 4096;
690 static mword nursery_size;
691 static int degraded_mode = 0;
693 static LOSObject *los_object_list = NULL;
694 static mword los_memory_usage = 0;
695 static mword los_num_objects = 0;
696 static mword next_los_collection = 2*1024*1024; /* 2 MB, need to tune */
697 static mword total_alloc = 0;
698 /* use this to tune when to do a major/minor collection */
699 static mword memory_pressure = 0;
701 static GCMemSection *nursery_section = NULL;
702 static mword lowest_heap_address = ~(mword)0;
703 static mword highest_heap_address = 0;
705 static LOCK_DECLARE (interruption_mutex);
707 typedef struct _FinalizeEntry FinalizeEntry;
708 struct _FinalizeEntry {
713 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
714 struct _FinalizeEntryHashTable {
715 FinalizeEntry **table;
720 typedef struct _DisappearingLink DisappearingLink;
721 struct _DisappearingLink {
722 DisappearingLink *next;
726 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
727 struct _DisappearingLinkHashTable {
728 DisappearingLink **table;
733 typedef struct _EphemeronLinkNode EphemeronLinkNode;
735 struct _EphemeronLinkNode {
736 EphemeronLinkNode *next;
745 #define LARGE_INTERNAL_MEM_HEADER_MAGIC 0x7d289f3a
747 typedef struct _LargeInternalMemHeader LargeInternalMemHeader;
748 struct _LargeInternalMemHeader {
760 int current_collection_generation = -1;
763 * The link pointer is hidden by negating each bit. We use the lowest
764 * bit of the link (before negation) to store whether it needs
765 * resurrection tracking.
767 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
768 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
770 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
771 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
774 * The finalizable hash has the object as the key, the
775 * disappearing_link hash, has the link address as key.
777 static FinalizeEntryHashTable minor_finalizable_hash;
778 static FinalizeEntryHashTable major_finalizable_hash;
779 /* objects that are ready to be finalized */
780 static FinalizeEntry *fin_ready_list = NULL;
781 static FinalizeEntry *critical_fin_list = NULL;
783 static DisappearingLinkHashTable minor_disappearing_link_hash;
784 static DisappearingLinkHashTable major_disappearing_link_hash;
786 static EphemeronLinkNode *ephemeron_list;
788 static int num_ready_finalizers = 0;
789 static int no_finalize = 0;
791 /* keep each size a multiple of ALLOC_ALIGN */
792 /* on 64 bit systems 8 is likely completely unused. */
793 static const int freelist_sizes [] = {
794 8, 16, 24, 32, 40, 48, 64, 80,
795 96, 128, 160, 192, 224, 256, 320, 384,
796 448, 512, 584, 680, 816, 1024, 1360, 2048};
797 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
799 /* This is also the MAJOR_SECTION_SIZE for the copying major
801 #define PINNED_CHUNK_SIZE (128 * 1024)
803 /* internal_chunk_list is used for allocating structures needed by the GC */
804 static PinnedChunk *internal_chunk_list = NULL;
806 static int slot_for_size (size_t size);
809 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
810 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
811 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
815 /* registered roots: the key to the hash is the root start address */
817 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
819 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
820 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
821 static mword roots_size = 0; /* amount of memory in the root set */
822 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
825 * The current allocation cursors
826 * We allocate objects in the nursery.
827 * The nursery is the area between nursery_start and nursery_real_end.
828 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
829 * from nursery fragments.
830 * tlab_next is the pointer to the space inside the TLAB where the next object will
832 * tlab_temp_end is the pointer to the end of the temporary space reserved for
833 * the allocation: it allows us to set the scan starts at reasonable intervals.
834 * tlab_real_end points to the end of the TLAB.
835 * nursery_frag_real_end points to the end of the currently used nursery fragment.
836 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
837 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
838 * At the next allocation, the area of the nursery where objects can be present is
839 * between MIN(nursery_first_pinned_start, first_fragment_start) and
840 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
842 static char *nursery_start = NULL;
844 /* eventually share with MonoThread? */
845 typedef struct _SgenThreadInfo SgenThreadInfo;
847 struct _SgenThreadInfo {
848 SgenThreadInfo *next;
850 unsigned int stop_count; /* to catch duplicate signals */
853 volatile int in_critical_region;
856 void *stack_start_limit;
857 char **tlab_next_addr;
858 char **tlab_start_addr;
859 char **tlab_temp_end_addr;
860 char **tlab_real_end_addr;
861 gpointer **store_remset_buffer_addr;
862 long *store_remset_buffer_index_addr;
863 RememberedSet *remset;
864 gpointer runtime_data;
865 gpointer stopped_ip; /* only valid if the thread is stopped */
866 MonoDomain *stopped_domain; /* ditto */
867 gpointer *stopped_regs; /* ditto */
868 #ifndef HAVE_KW_THREAD
873 gpointer *store_remset_buffer;
874 long store_remset_buffer_index;
878 #ifdef HAVE_KW_THREAD
879 #define TLAB_ACCESS_INIT
880 #define TLAB_START tlab_start
881 #define TLAB_NEXT tlab_next
882 #define TLAB_TEMP_END tlab_temp_end
883 #define TLAB_REAL_END tlab_real_end
884 #define REMEMBERED_SET remembered_set
885 #define STORE_REMSET_BUFFER store_remset_buffer
886 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
887 #define IN_CRITICAL_REGION thread_info->in_critical_region
889 static pthread_key_t thread_info_key;
890 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
891 #define TLAB_START (__thread_info__->tlab_start)
892 #define TLAB_NEXT (__thread_info__->tlab_next)
893 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
894 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
895 #define REMEMBERED_SET (__thread_info__->remset)
896 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
897 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
898 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
901 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
902 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
903 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
906 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
907 * variables for next+temp_end ?
909 #ifdef HAVE_KW_THREAD
910 static __thread SgenThreadInfo *thread_info;
911 static __thread char *tlab_start;
912 static __thread char *tlab_next;
913 static __thread char *tlab_temp_end;
914 static __thread char *tlab_real_end;
915 static __thread gpointer *store_remset_buffer;
916 static __thread long store_remset_buffer_index;
917 /* Used by the managed allocator/wbarrier */
918 static __thread char **tlab_next_addr;
919 static __thread char *stack_end;
920 static __thread long *store_remset_buffer_index_addr;
922 static char *nursery_next = NULL;
923 static char *nursery_frag_real_end = NULL;
924 static char *nursery_real_end = NULL;
925 static char *nursery_last_pinned_end = NULL;
927 /* The size of a TLAB */
928 /* The bigger the value, the less often we have to go to the slow path to allocate a new
929 * one, but the more space is wasted by threads not allocating much memory.
931 * FIXME: Make this self-tuning for each thread.
933 static guint32 tlab_size = (1024 * 4);
935 /* fragments that are free and ready to be used for allocation */
936 static Fragment *nursery_fragments = NULL;
937 /* freeelist of fragment structures */
938 static Fragment *fragment_freelist = NULL;
940 /* objects bigger then this go into the large object space */
941 #define MAX_SMALL_OBJ_SIZE 2040
943 /* Functions supplied by the runtime to be called by the GC */
944 static MonoGCCallbacks gc_callbacks;
946 #define ALLOC_ALIGN 8
947 #define ALLOC_ALIGN_BITS 3
949 #define MOVED_OBJECTS_NUM 64
950 static void *moved_objects [MOVED_OBJECTS_NUM];
951 static int moved_objects_idx = 0;
954 * ######################################################################
955 * ######## Macros and function declarations.
956 * ######################################################################
959 #define UPDATE_HEAP_BOUNDARIES(low,high) do { \
960 if ((mword)(low) < lowest_heap_address) \
961 lowest_heap_address = (mword)(low); \
962 if ((mword)(high) > highest_heap_address) \
963 highest_heap_address = (mword)(high); \
965 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
968 align_pointer (void *ptr)
970 mword p = (mword)ptr;
971 p += sizeof (gpointer) - 1;
972 p &= ~ (sizeof (gpointer) - 1);
976 typedef void (*CopyOrMarkObjectFunc) (void**);
977 typedef char* (*ScanObjectFunc) (char*);
979 /* forward declarations */
980 static void* get_internal_mem (size_t size, int type);
981 static void free_internal_mem (void *addr, int type);
982 static void* get_os_memory (size_t size, int activate);
983 static void* get_os_memory_aligned (mword size, mword alignment, gboolean activate);
984 static void free_os_memory (void *addr, size_t size);
985 static G_GNUC_UNUSED void report_internal_mem_usage (void);
987 static int stop_world (void);
988 static int restart_world (void);
989 static void add_to_global_remset (gpointer ptr);
990 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
991 static void scan_from_remsets (void *start_nursery, void *end_nursery);
992 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type);
993 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list);
994 static void find_pinning_ref_from_thread (char *obj, size_t size);
995 static void update_current_thread_stack (void *start);
996 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
997 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
998 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation);
999 static void null_links_for_domain (MonoDomain *domain, int generation);
1000 static gboolean search_fragment_for_size (size_t size);
1001 static void build_nursery_fragments (int start_pin, int end_pin);
1002 static void clear_nursery_fragments (char *next);
1003 static void pin_from_roots (void *start_nursery, void *end_nursery);
1004 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery);
1005 static void pin_objects_in_section (GCMemSection *section);
1006 static void optimize_pin_queue (int start_slot);
1007 static void clear_remsets (void);
1008 static void clear_tlabs (void);
1009 typedef void (*IterateObjectCallbackFunc) (char*, size_t, void*);
1010 static void scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data);
1011 static char* scan_object (char *start);
1012 static char* major_scan_object (char *start);
1013 static void* copy_object_no_checks (void *obj);
1014 static void copy_object (void **obj_slot);
1015 static void* get_chunk_freelist (PinnedChunk *chunk, int slot);
1016 static PinnedChunk* alloc_pinned_chunk (void);
1017 static void free_large_object (LOSObject *obj);
1018 static void sort_addresses (void **array, int size);
1019 static void drain_gray_stack (void);
1020 static void finish_gray_stack (char *start_addr, char *end_addr, int generation);
1022 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
1024 void describe_ptr (char *ptr);
1025 static void check_consistency (void);
1026 static void check_section_scan_starts (GCMemSection *section);
1027 static void check_scan_starts (void);
1028 static void check_for_xdomain_refs (void);
1029 static void dump_occupied (char *start, char *end, char *section_start);
1030 static void dump_section (GCMemSection *section, const char *type);
1031 static void dump_heap (const char *type, int num, const char *reason);
1032 static void commit_stats (int generation);
1033 static void report_pinned_chunk (PinnedChunk *chunk, int seq);
1035 void mono_gc_scan_for_specific_ref (MonoObject *key);
1037 static void init_stats (void);
1039 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end);
1040 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end);
1041 static void null_ephemerons_for_domain (MonoDomain *domain);
1043 //#define BINARY_PROTOCOL
1044 #include "sgen-protocol.c"
1045 #include "sgen-pinning.c"
1046 #include "sgen-pinning-stats.c"
1047 #include "sgen-gray.c"
1050 * ######################################################################
1051 * ######## GC descriptors
1052 * ######################################################################
1053 * Used to quickly get the info the GC needs about an object: size and
1054 * where the references are held.
1056 /* objects are aligned to 8 bytes boundaries
1057 * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
1058 * The low 3 bits define the type of the descriptor. The other bits
1059 * depend on the type.
1060 * As a general rule the 13 remaining low bits define the size, either
1061 * of the whole object or of the elements in the arrays. While for objects
1062 * the size is already in bytes, for arrays we need to shift, because
1063 * array elements might be smaller than 8 bytes. In case of arrays, we
1064 * use two bits to describe what the additional high bits represents,
1065 * so the default behaviour can handle element sizes less than 2048 bytes.
1066 * The high 16 bits, if 0 it means the object is pointer-free.
1067 * This design should make it easy and fast to skip over ptr-free data.
1068 * The first 4 types should cover >95% of the objects.
1069 * Note that since the size of objects is limited to 64K, larger objects
1070 * will be allocated in the large object heap.
1071 * If we want 4-bytes alignment, we need to put vector and small bitmap
1075 DESC_TYPE_RUN_LENGTH, /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
1076 DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
1077 DESC_TYPE_STRING, /* nothing */
1078 DESC_TYPE_COMPLEX, /* index for bitmap into complex_descriptors */
1079 DESC_TYPE_VECTOR, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1080 DESC_TYPE_ARRAY, /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1081 DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
1082 DESC_TYPE_COMPLEX_ARR, /* index for bitmap into complex_descriptors */
1083 /* subtypes for arrays and vectors */
1084 DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value */
1085 DESC_TYPE_V_REFS, /* all the array elements are refs */
1086 DESC_TYPE_V_RUN_LEN, /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
1087 DESC_TYPE_V_BITMAP /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
1090 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
1091 #define LOW_TYPE_BITS 3
1092 #define SMALL_BITMAP_SHIFT 16
1093 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
1094 #define VECTOR_INFO_SHIFT 14
1095 #define VECTOR_ELSIZE_SHIFT 3
1096 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
1097 #define MAX_ELEMENT_SIZE 0x3ff
1098 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
1099 #define VECTOR_SUBTYPE_REFS (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
1100 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
1101 #define VECTOR_SUBTYPE_BITMAP (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
1104 /* Root bitmap descriptors are simpler: the lower three bits describe the type
1105 * and we either have 30/62 bitmap bits or nibble-based run-length,
1106 * or a complex descriptor, or a user defined marker function.
1109 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
1114 ROOT_DESC_TYPE_MASK = 0x7,
1115 ROOT_DESC_TYPE_SHIFT = 3,
1118 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
1120 #define MAX_USER_DESCRIPTORS 16
1122 static gsize* complex_descriptors = NULL;
1123 static int complex_descriptors_size = 0;
1124 static int complex_descriptors_next = 0;
1125 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
1126 static int user_descriptors_next = 0;
1129 alloc_complex_descriptor (gsize *bitmap, int numbits)
1133 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
1134 nwords = numbits / GC_BITS_PER_WORD + 1;
1137 res = complex_descriptors_next;
1138 /* linear search, so we don't have duplicates with domain load/unload
1139 * this should not be performance critical or we'd have bigger issues
1140 * (the number and size of complex descriptors should be small).
1142 for (i = 0; i < complex_descriptors_next; ) {
1143 if (complex_descriptors [i] == nwords) {
1144 int j, found = TRUE;
1145 for (j = 0; j < nwords - 1; ++j) {
1146 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
1156 i += complex_descriptors [i];
1158 if (complex_descriptors_next + nwords > complex_descriptors_size) {
1159 int new_size = complex_descriptors_size * 2 + nwords;
1160 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
1161 complex_descriptors_size = new_size;
1163 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
1164 complex_descriptors_next += nwords;
1165 complex_descriptors [res] = nwords;
1166 for (i = 0; i < nwords - 1; ++i) {
1167 complex_descriptors [res + 1 + i] = bitmap [i];
1168 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
1175 * Descriptor builders.
1178 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
1180 return (void*) DESC_TYPE_STRING;
1184 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
1186 int first_set = -1, num_set = 0, last_set = -1, i;
1188 size_t stored_size = obj_size;
1189 stored_size += ALLOC_ALIGN - 1;
1190 stored_size &= ~(ALLOC_ALIGN - 1);
1191 for (i = 0; i < numbits; ++i) {
1192 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1199 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
1200 /* check run-length encoding first: one byte offset, one byte number of pointers
1201 * on 64 bit archs, we can have 3 runs, just one on 32.
1202 * It may be better to use nibbles.
1204 if (first_set < 0) {
1205 desc = DESC_TYPE_RUN_LENGTH | stored_size;
1206 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1207 return (void*) desc;
1208 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1209 desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
1210 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));
1211 return (void*) desc;
1213 /* we know the 2-word header is ptr-free */
1214 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1215 desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1216 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1217 return (void*) desc;
1220 /* we know the 2-word header is ptr-free */
1221 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1222 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1223 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1224 return (void*) desc;
1226 /* it's a complex object ... */
1227 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1228 return (void*) desc;
1231 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1233 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1235 int first_set = -1, num_set = 0, last_set = -1, i;
1236 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1237 for (i = 0; i < numbits; ++i) {
1238 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1245 if (elem_size <= MAX_ELEMENT_SIZE) {
1246 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1248 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1250 /* Note: we also handle structs with just ref fields */
1251 if (num_set * sizeof (gpointer) == elem_size) {
1252 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1254 /* FIXME: try run-len first */
1255 /* Note: we can't skip the object header here, because it's not present */
1256 if (last_set <= SMALL_BITMAP_SIZE) {
1257 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1260 /* it's am array of complex structs ... */
1261 desc = DESC_TYPE_COMPLEX_ARR;
1262 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1263 return (void*) desc;
1266 /* Return the bitmap encoded by a descriptor */
1268 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1270 mword d = (mword)descr;
1274 case DESC_TYPE_RUN_LENGTH: {
1275 int first_set = (d >> 16) & 0xff;
1276 int num_set = (d >> 24) & 0xff;
1279 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1281 for (i = first_set; i < first_set + num_set; ++i)
1282 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1284 *numbits = first_set + num_set;
1288 case DESC_TYPE_SMALL_BITMAP:
1289 bitmap = g_new0 (gsize, 1);
1291 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1293 *numbits = GC_BITS_PER_WORD;
1297 g_assert_not_reached ();
1301 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
1302 #define STRING_SIZE(size,str) do { \
1303 (size) = sizeof (MonoString) + 2 * mono_string_length ((MonoString*)(str)) + 2; \
1304 (size) += (ALLOC_ALIGN - 1); \
1305 (size) &= ~(ALLOC_ALIGN - 1); \
1308 #define OBJ_RUN_LEN_SIZE(size,desc,obj) do { \
1309 (size) = (desc) & 0xfff8; \
1312 #define OBJ_BITMAP_SIZE(size,desc,obj) do { \
1313 (size) = (desc) & 0xfff8; \
1316 //#define PREFETCH(addr) __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(addr)))
1317 #define PREFETCH(addr)
1319 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
1320 #define OBJ_RUN_LEN_FOREACH_PTR(desc,obj) do { \
1321 if ((desc) & 0xffff0000) { \
1322 /* there are pointers */ \
1323 void **_objptr_end; \
1324 void **_objptr = (void**)(obj); \
1325 _objptr += ((desc) >> 16) & 0xff; \
1326 _objptr_end = _objptr + (((desc) >> 24) & 0xff); \
1327 while (_objptr < _objptr_end) { \
1328 HANDLE_PTR (_objptr, (obj)); \
1334 /* a bitmap desc means that there are pointer references or we'd have
1335 * choosen run-length, instead: add an assert to check.
1337 #define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
1338 /* there are pointers */ \
1339 void **_objptr = (void**)(obj); \
1340 gsize _bmap = (desc) >> 16; \
1341 _objptr += OBJECT_HEADER_WORDS; \
1343 if ((_bmap & 1)) { \
1344 HANDLE_PTR (_objptr, (obj)); \
1351 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj) do { \
1352 /* there are pointers */ \
1353 void **_objptr = (void**)(obj); \
1354 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS; \
1355 _objptr += OBJECT_HEADER_WORDS; \
1357 if ((_bmap & 1)) { \
1358 HANDLE_PTR (_objptr, (obj)); \
1365 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do { \
1366 /* there are pointers */ \
1367 void **_objptr = (void**)(obj); \
1368 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1369 int bwords = (*bitmap_data) - 1; \
1370 void **start_run = _objptr; \
1373 MonoObject *myobj = (MonoObject*)obj; \
1374 g_print ("found %d at %p (0x%zx): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1376 while (bwords-- > 0) { \
1377 gsize _bmap = *bitmap_data++; \
1378 _objptr = start_run; \
1379 /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/ \
1381 if ((_bmap & 1)) { \
1382 HANDLE_PTR (_objptr, (obj)); \
1387 start_run += GC_BITS_PER_WORD; \
1391 /* this one is untested */
1392 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj) do { \
1393 /* there are pointers */ \
1394 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS); \
1395 int mbwords = (*mbitmap_data++) - 1; \
1396 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass); \
1397 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1398 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1400 MonoObject *myobj = (MonoObject*)start; \
1401 g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name); \
1403 while (e_start < e_end) { \
1404 void **_objptr = (void**)e_start; \
1405 gsize *bitmap_data = mbitmap_data; \
1406 unsigned int bwords = mbwords; \
1407 while (bwords-- > 0) { \
1408 gsize _bmap = *bitmap_data++; \
1409 void **start_run = _objptr; \
1410 /*g_print ("bitmap: 0x%x\n", _bmap);*/ \
1412 if ((_bmap & 1)) { \
1413 HANDLE_PTR (_objptr, (obj)); \
1418 _objptr = start_run + GC_BITS_PER_WORD; \
1420 e_start += el_size; \
1424 #define OBJ_VECTOR_FOREACH_PTR(vt,obj) do { \
1425 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */ \
1426 if ((vt)->desc & 0xffffc000) { \
1427 int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE; \
1428 /* there are pointers */ \
1429 int etype = (vt)->desc & 0xc000; \
1430 if (etype == (DESC_TYPE_V_REFS << 14)) { \
1431 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector)); \
1432 void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
1433 /* Note: this code can handle also arrays of struct with only references in them */ \
1434 while (p < end_refs) { \
1435 HANDLE_PTR (p, (obj)); \
1438 } else if (etype == DESC_TYPE_V_RUN_LEN << 14) { \
1439 int offset = ((vt)->desc >> 16) & 0xff; \
1440 int num_refs = ((vt)->desc >> 24) & 0xff; \
1441 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1442 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1443 while (e_start < e_end) { \
1444 void **p = (void**)e_start; \
1447 for (i = 0; i < num_refs; ++i) { \
1448 HANDLE_PTR (p + i, (obj)); \
1450 e_start += el_size; \
1452 } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
1453 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
1454 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj)); \
1455 while (e_start < e_end) { \
1456 void **p = (void**)e_start; \
1457 gsize _bmap = (vt)->desc >> 16; \
1458 /* Note: there is no object header here to skip */ \
1460 if ((_bmap & 1)) { \
1461 HANDLE_PTR (p, (obj)); \
1466 e_start += el_size; \
1472 //#include "sgen-major-copying.c"
1473 #include "sgen-marksweep.c"
1476 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1478 MonoObject *o = (MonoObject*)(obj);
1479 MonoObject *ref = (MonoObject*)*(ptr);
1480 int offset = (char*)(ptr) - (char*)o;
1482 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1484 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1486 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1487 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1489 /* Thread.cached_culture_info */
1490 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1491 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1492 !strcmp(o->vtable->klass->name_space, "System") &&
1493 !strcmp(o->vtable->klass->name, "Object[]"))
1496 * 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
1497 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1498 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1499 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1500 * 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
1501 * 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
1502 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1503 * 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
1504 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1506 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1507 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1508 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1509 !strcmp (o->vtable->klass->name, "MemoryStream"))
1511 /* append_job() in threadpool.c */
1512 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1513 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1514 !strcmp (o->vtable->klass->name_space, "System") &&
1515 !strcmp (o->vtable->klass->name, "Object[]") &&
1516 mono_thread_pool_is_queue_array ((MonoArray*) o))
1522 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1524 MonoObject *o = (MonoObject*)(obj);
1525 MonoObject *ref = (MonoObject*)*(ptr);
1526 int offset = (char*)(ptr) - (char*)o;
1528 MonoClassField *field;
1531 if (!ref || ref->vtable->domain == domain)
1533 if (is_xdomain_ref_allowed (ptr, obj, domain))
1537 for (class = o->vtable->klass; class; class = class->parent) {
1540 for (i = 0; i < class->field.count; ++i) {
1541 if (class->fields[i].offset == offset) {
1542 field = &class->fields[i];
1550 if (ref->vtable->klass == mono_defaults.string_class)
1551 str = mono_string_to_utf8 ((MonoString*)ref);
1554 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1555 o, o->vtable->klass->name_space, o->vtable->klass->name,
1556 offset, field ? field->name : "",
1557 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1558 mono_gc_scan_for_specific_ref (o);
1564 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1567 scan_object_for_xdomain_refs (char *start)
1569 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1571 #include "sgen-scan-object.h"
1577 scan_area_for_xdomain_refs (char *start, char *end)
1579 while (start < end) {
1580 if (!*(void**)start) {
1581 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1585 start = scan_object_for_xdomain_refs (start);
1590 #define HANDLE_PTR(ptr,obj) do { \
1591 if ((MonoObject*)*(ptr) == key) { \
1592 g_print ("found ref to %p in object %p (%s) at offset %zd\n", \
1593 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1598 scan_object_for_specific_ref (char *start, MonoObject *key)
1600 #include "sgen-scan-object.h"
1606 scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data)
1608 while (start < end) {
1610 if (!*(void**)start) {
1611 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1615 size = safe_object_get_size ((MonoObject*) start);
1616 size += ALLOC_ALIGN - 1;
1617 size &= ~(ALLOC_ALIGN - 1);
1619 callback (start, size, data);
1626 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1628 scan_object_for_specific_ref (obj, key);
1632 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1636 g_print ("found ref to %p in root record %p\n", key, root);
1639 static MonoObject *check_key = NULL;
1640 static RootRecord *check_root = NULL;
1643 check_root_obj_specific_ref_from_marker (void **obj)
1645 check_root_obj_specific_ref (check_root, check_key, *obj);
1649 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1654 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1655 for (root = roots_hash [root_type][i]; root; root = root->next) {
1656 void **start_root = (void**)root->start_root;
1657 mword desc = root->root_desc;
1661 switch (desc & ROOT_DESC_TYPE_MASK) {
1662 case ROOT_DESC_BITMAP:
1663 desc >>= ROOT_DESC_TYPE_SHIFT;
1666 check_root_obj_specific_ref (root, key, *start_root);
1671 case ROOT_DESC_COMPLEX: {
1672 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1673 int bwords = (*bitmap_data) - 1;
1674 void **start_run = start_root;
1676 while (bwords-- > 0) {
1677 gsize bmap = *bitmap_data++;
1678 void **objptr = start_run;
1681 check_root_obj_specific_ref (root, key, *objptr);
1685 start_run += GC_BITS_PER_WORD;
1689 case ROOT_DESC_USER: {
1690 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1691 marker (start_root, check_root_obj_specific_ref_from_marker);
1694 case ROOT_DESC_RUN_LEN:
1695 g_assert_not_reached ();
1697 g_assert_not_reached ();
1706 mono_gc_scan_for_specific_ref (MonoObject *key)
1712 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1713 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1715 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1717 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1718 scan_object_for_specific_ref (bigobj->data, key);
1720 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1721 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1723 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1724 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1725 void **ptr = (void**)root->start_root;
1727 while (ptr < (void**)root->end_root) {
1728 check_root_obj_specific_ref (root, *ptr, key);
1735 /* Clear all remaining nursery fragments */
1737 clear_nursery_fragments (char *next)
1740 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1741 g_assert (next <= nursery_frag_real_end);
1742 memset (next, 0, nursery_frag_real_end - next);
1743 for (frag = nursery_fragments; frag; frag = frag->next) {
1744 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1750 need_remove_object_for_domain (char *start, MonoDomain *domain)
1752 if (mono_object_domain (start) == domain) {
1753 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1754 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1761 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1763 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1764 if (vt->klass == mono_defaults.internal_thread_class)
1765 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1766 /* The object could be a proxy for an object in the domain
1768 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1769 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1771 /* The server could already have been zeroed out, so
1772 we need to check for that, too. */
1773 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1774 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1776 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1781 static MonoDomain *check_domain = NULL;
1784 check_obj_not_in_domain (void **o)
1786 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1790 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1794 check_domain = domain;
1795 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1796 for (root = roots_hash [root_type][i]; root; root = root->next) {
1797 void **start_root = (void**)root->start_root;
1798 mword desc = root->root_desc;
1800 /* The MonoDomain struct is allowed to hold
1801 references to objects in its own domain. */
1802 if (start_root == (void**)domain)
1805 switch (desc & ROOT_DESC_TYPE_MASK) {
1806 case ROOT_DESC_BITMAP:
1807 desc >>= ROOT_DESC_TYPE_SHIFT;
1809 if ((desc & 1) && *start_root)
1810 check_obj_not_in_domain (*start_root);
1815 case ROOT_DESC_COMPLEX: {
1816 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1817 int bwords = (*bitmap_data) - 1;
1818 void **start_run = start_root;
1820 while (bwords-- > 0) {
1821 gsize bmap = *bitmap_data++;
1822 void **objptr = start_run;
1824 if ((bmap & 1) && *objptr)
1825 check_obj_not_in_domain (*objptr);
1829 start_run += GC_BITS_PER_WORD;
1833 case ROOT_DESC_USER: {
1834 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1835 marker (start_root, check_obj_not_in_domain);
1838 case ROOT_DESC_RUN_LEN:
1839 g_assert_not_reached ();
1841 g_assert_not_reached ();
1845 check_domain = NULL;
1849 scan_pinned_object_for_xdomain_refs_callback (char *obj, size_t size, gpointer dummy)
1851 scan_object_for_xdomain_refs (obj);
1855 check_for_xdomain_refs (void)
1859 scan_area_for_xdomain_refs (nursery_section->data, nursery_section->end_data);
1861 major_iterate_objects (TRUE, TRUE, scan_pinned_object_for_xdomain_refs_callback, NULL);
1863 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1864 scan_object_for_xdomain_refs (bigobj->data);
1868 clear_domain_process_object (char *obj, MonoDomain *domain)
1872 process_object_for_domain_clearing (obj, domain);
1873 remove = need_remove_object_for_domain (obj, domain);
1875 if (remove && ((MonoObject*)obj)->synchronisation) {
1876 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1878 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1885 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1887 if (clear_domain_process_object (obj, domain))
1888 memset (obj, 0, size);
1892 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1894 clear_domain_process_object (obj, domain);
1898 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1900 if (need_remove_object_for_domain (obj, domain))
1901 major_free_non_pinned_object (obj, size);
1905 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1907 if (need_remove_object_for_domain (obj, domain))
1908 free_pinned_object (obj, size);
1912 * When appdomains are unloaded we can easily remove objects that have finalizers,
1913 * but all the others could still be present in random places on the heap.
1914 * We need a sweep to get rid of them even though it's going to be costly
1916 * The reason we need to remove them is because we access the vtable and class
1917 * structures to know the object size and the reference bitmap: once the domain is
1918 * unloaded the point to random memory.
1921 mono_gc_clear_domain (MonoDomain * domain)
1923 LOSObject *bigobj, *prev;
1928 clear_nursery_fragments (nursery_next);
1930 if (xdomain_checks && domain != mono_get_root_domain ()) {
1931 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1932 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1933 check_for_xdomain_refs ();
1936 scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1937 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain);
1939 /* We need two passes over major and large objects because
1940 freeing such objects might give their memory back to the OS
1941 (in the case of large objects) or obliterate its vtable
1942 (pinned objects with major-copying or pinned and non-pinned
1943 objects with major-mark&sweep), but we might need to
1944 dereference a pointer from an object to another object if
1945 the first object is a proxy. */
1946 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1947 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1948 clear_domain_process_object (bigobj->data, domain);
1951 for (bigobj = los_object_list; bigobj;) {
1952 if (need_remove_object_for_domain (bigobj->data, domain)) {
1953 LOSObject *to_free = bigobj;
1955 prev->next = bigobj->next;
1957 los_object_list = bigobj->next;
1958 bigobj = bigobj->next;
1959 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1961 free_large_object (to_free);
1965 bigobj = bigobj->next;
1967 major_iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1968 major_iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1970 null_ephemerons_for_domain (domain);
1972 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1973 null_links_for_domain (domain, i);
1979 * add_to_global_remset:
1981 * The global remset contains locations which point into newspace after
1982 * a minor collection. This can happen if the objects they point to are pinned.
1985 add_to_global_remset (gpointer ptr)
1989 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1990 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1992 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1994 HEAVY_STAT (++stat_global_remsets_added);
1997 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1998 * To avoid uncontrolled growth of the global remset, only add each pointer once.
2000 if (global_remset->store_next + 3 < global_remset->end_set) {
2001 *(global_remset->store_next++) = (mword)ptr;
2004 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
2005 rs->next = global_remset;
2007 *(global_remset->store_next++) = (mword)ptr;
2010 int global_rs_size = 0;
2012 for (rs = global_remset; rs; rs = rs->next) {
2013 global_rs_size += rs->store_next - rs->data;
2015 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
2020 * FIXME: allocate before calling this function and pass the
2021 * destination address.
2024 copy_object_no_checks (void *obj)
2026 static const void *copy_labels [] = { &&LAB_0, &&LAB_1, &&LAB_2, &&LAB_3, &&LAB_4, &&LAB_5, &&LAB_6, &&LAB_7, &&LAB_8 };
2030 MonoVTable *vt = ((MonoObject*)obj)->vtable;
2031 gboolean has_references = vt->klass->has_references;
2033 objsize = safe_object_get_size ((MonoObject*)obj);
2034 objsize += ALLOC_ALIGN - 1;
2035 objsize &= ~(ALLOC_ALIGN - 1);
2037 DEBUG (9, g_assert (vt->klass->inited));
2038 MAJOR_GET_COPY_OBJECT_SPACE (destination, objsize, has_references);
2040 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %zd)\n", destination, ((MonoObject*)obj)->vtable->klass->name, objsize));
2041 binary_protocol_copy (obj, destination, ((MonoObject*)obj)->vtable, objsize);
2043 if (objsize <= sizeof (gpointer) * 8) {
2044 mword *dest = (mword*)destination;
2045 goto *copy_labels [objsize / sizeof (gpointer)];
2047 (dest) [7] = ((mword*)obj) [7];
2049 (dest) [6] = ((mword*)obj) [6];
2051 (dest) [5] = ((mword*)obj) [5];
2053 (dest) [4] = ((mword*)obj) [4];
2055 (dest) [3] = ((mword*)obj) [3];
2057 (dest) [2] = ((mword*)obj) [2];
2059 (dest) [1] = ((mword*)obj) [1];
2061 (dest) [0] = ((mword*)obj) [0];
2069 char* edi = destination;
2070 __asm__ __volatile__(
2072 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
2073 : "0" (objsize/4), "1" (edi),"2" (esi)
2078 memcpy (destination, obj, objsize);
2081 /* adjust array->bounds */
2082 DEBUG (9, g_assert (vt->gc_descr));
2083 if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
2084 MonoArray *array = (MonoArray*)destination;
2085 array->bounds = (MonoArrayBounds*)((char*)destination + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
2086 DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %zd, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
2088 /* set the forwarding pointer */
2089 forward_object (obj, destination);
2090 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
2091 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2092 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2093 moved_objects_idx = 0;
2095 moved_objects [moved_objects_idx++] = obj;
2096 moved_objects [moved_objects_idx++] = destination;
2099 if (has_references) {
2100 DEBUG (9, fprintf (gc_debug_file, "Enqueuing gray object %p (%s)\n", obj, safe_name (obj)));
2101 GRAY_OBJECT_ENQUEUE (obj);
2107 * This is how the copying happens from the nursery to the old generation.
2108 * We assume that at this time all the pinned objects have been identified and
2110 * We run scan_object() for each pinned object so that each referenced
2111 * objects if possible are copied. The new gray objects created can have
2112 * scan_object() run on them right away, too.
2113 * Then we run copy_object() for the precisely tracked roots. At this point
2114 * all the roots are either gray or black. We run scan_object() on the gray
2115 * objects until no more gray objects are created.
2116 * At the end of the process we walk again the pinned list and we unmark
2117 * the pinned flag. As we go we also create the list of free space for use
2118 * in the next allocation runs.
2120 * We need to remember objects from the old generation that point to the new one
2121 * (or just addresses?).
2123 * copy_object could be made into a macro once debugged (use inline for now).
2126 static void __attribute__((noinline))
2127 copy_object (void **obj_slot)
2130 char *obj = *obj_slot;
2132 DEBUG (9, g_assert (current_collection_generation == GENERATION_NURSERY));
2134 HEAVY_STAT (++stat_copy_object_called_nursery);
2136 if (!ptr_in_nursery (obj)) {
2137 HEAVY_STAT (++stat_nursery_copy_object_failed_from_space);
2141 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
2144 * Before we can copy the object we must make sure that we are
2145 * allowed to, i.e. that the object not pinned or not already
2149 if ((forwarded = object_is_forwarded (obj))) {
2150 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2151 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
2152 HEAVY_STAT (++stat_nursery_copy_object_failed_forwarded);
2153 *obj_slot = forwarded;
2156 if (object_is_pinned (obj)) {
2157 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2158 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
2159 HEAVY_STAT (++stat_nursery_copy_object_failed_pinned);
2163 HEAVY_STAT (++stat_objects_copied_nursery);
2165 *obj_slot = copy_object_no_checks (obj);
2169 #define HANDLE_PTR(ptr,obj) do { \
2170 void *__old = *(ptr); \
2173 copy_object ((ptr)); \
2175 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2176 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2177 add_to_global_remset ((ptr)); \
2182 * Scan the object pointed to by @start for references to
2183 * other objects between @from_start and @from_end and copy
2184 * them to the gray_objects area.
2185 * Returns a pointer to the end of the object.
2188 scan_object (char *start)
2190 #include "sgen-scan-object.h"
2192 HEAVY_STAT (++stat_scan_object_called_nursery);
2200 * Scan the valuetype pointed to by START, described by DESC for references to
2201 * other objects between @from_start and @from_end and copy them to the gray_objects area.
2202 * Returns a pointer to the end of the object.
2205 scan_vtype (char *start, mword desc, char* from_start, char* from_end)
2209 /* The descriptors include info about the MonoObject header as well */
2210 start -= sizeof (MonoObject);
2212 switch (desc & 0x7) {
2213 case DESC_TYPE_RUN_LENGTH:
2214 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
2215 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
2216 g_assert (skip_size);
2217 return start + skip_size;
2218 case DESC_TYPE_SMALL_BITMAP:
2219 OBJ_BITMAP_FOREACH_PTR (desc,start);
2220 OBJ_BITMAP_SIZE (skip_size, desc, start);
2221 return start + skip_size;
2222 case DESC_TYPE_LARGE_BITMAP:
2223 case DESC_TYPE_COMPLEX:
2225 g_assert_not_reached ();
2228 // The other descriptors can't happen with vtypes
2229 g_assert_not_reached ();
2236 #define HANDLE_PTR(ptr,obj) do { \
2237 void *__old = *(ptr); \
2240 major_copy_or_mark_object ((ptr)); \
2242 DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old)); \
2243 if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2244 add_to_global_remset ((ptr)); \
2249 major_scan_object (char *start)
2251 #include "sgen-scan-object.h"
2253 HEAVY_STAT (++stat_scan_object_called_major);
2261 * Scan objects in the gray stack until the stack is empty. This should be called
2262 * frequently after each object is copied, to achieve better locality and cache
2266 drain_gray_stack (void)
2270 if (current_collection_generation == GENERATION_NURSERY) {
2272 GRAY_OBJECT_DEQUEUE (obj);
2275 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2280 GRAY_OBJECT_DEQUEUE (obj);
2283 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2284 major_scan_object (obj);
2290 * Addresses from start to end are already sorted. This function finds
2291 * the object header for each address and pins the object. The
2292 * addresses must be inside the passed section. The (start of the)
2293 * address array is overwritten with the addresses of the actually
2294 * pinned objects. Return the number of pinned objects.
2297 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
2302 void *last_obj = NULL;
2303 size_t last_obj_size = 0;
2306 void **definitely_pinned = start;
2307 while (start < end) {
2309 /* the range check should be reduntant */
2310 if (addr != last && addr >= start_nursery && addr < end_nursery) {
2311 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
2312 /* multiple pointers to the same object */
2313 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
2317 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
2318 g_assert (idx < section->num_scan_start);
2319 search_start = (void*)section->scan_starts [idx];
2320 if (!search_start || search_start > addr) {
2323 search_start = section->scan_starts [idx];
2324 if (search_start && search_start <= addr)
2327 if (!search_start || search_start > addr)
2328 search_start = start_nursery;
2330 if (search_start < last_obj)
2331 search_start = (char*)last_obj + last_obj_size;
2332 /* now addr should be in an object a short distance from search_start
2333 * Note that search_start must point to zeroed mem or point to an object.
2336 if (!*(void**)search_start) {
2337 mword p = (mword)search_start;
2338 p += sizeof (gpointer);
2339 p += ALLOC_ALIGN - 1;
2340 p &= ~(ALLOC_ALIGN - 1);
2341 search_start = (void*)p;
2344 last_obj = search_start;
2345 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
2346 last_obj_size += ALLOC_ALIGN - 1;
2347 last_obj_size &= ~(ALLOC_ALIGN - 1);
2348 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
2349 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
2350 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));
2351 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
2352 pin_object (search_start);
2353 GRAY_OBJECT_ENQUEUE (search_start);
2355 pin_stats_register_object (search_start, last_obj_size);
2356 definitely_pinned [count] = search_start;
2360 /* skip to the next object */
2361 search_start = (void*)((char*)search_start + last_obj_size);
2362 } while (search_start <= addr);
2363 /* we either pinned the correct object or we ignored the addr because
2364 * it points to unused zeroed memory.
2370 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
2375 pin_objects_in_section (GCMemSection *section)
2377 int start = section->pin_queue_start;
2378 int end = section->pin_queue_end;
2381 reduced_to = pin_objects_from_addresses (section, pin_queue + start, pin_queue + end,
2382 section->data, section->next_data);
2383 section->pin_queue_start = start;
2384 section->pin_queue_end = start + reduced_to;
2391 gap = (gap * 10) / 13;
2392 if (gap == 9 || gap == 10)
2401 compare_addr (const void *a, const void *b)
2403 return *(const void **)a - *(const void **)b;
2407 /* sort the addresses in array in increasing order */
2409 sort_addresses (void **array, int size)
2412 * qsort is slower as predicted.
2413 * qsort (array, size, sizeof (gpointer), compare_addr);
2420 gap = new_gap (gap);
2423 for (i = 0; i < end; i++) {
2425 if (array [i] > array [j]) {
2426 void* val = array [i];
2427 array [i] = array [j];
2432 if (gap == 1 && !swapped)
2437 static G_GNUC_UNUSED void
2438 print_nursery_gaps (void* start_nursery, void *end_nursery)
2441 gpointer first = start_nursery;
2443 for (i = 0; i < next_pin_slot; ++i) {
2444 next = pin_queue [i];
2445 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2449 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2452 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
2454 optimize_pin_queue (int start_slot)
2456 void **start, **cur, **end;
2457 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
2458 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
2459 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
2460 if ((next_pin_slot - start_slot) > 1)
2461 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
2462 start = cur = pin_queue + start_slot;
2463 end = pin_queue + next_pin_slot;
2466 while (*start == *cur && cur < end)
2470 next_pin_slot = start - pin_queue;
2471 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2472 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2477 * Scan the memory between start and end and queue values which could be pointers
2478 * to the area between start_nursery and end_nursery for later consideration.
2479 * Typically used for thread stacks.
2482 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2485 while (start < end) {
2486 if (*start >= start_nursery && *start < end_nursery) {
2488 * *start can point to the middle of an object
2489 * note: should we handle pointing at the end of an object?
2490 * pinning in C# code disallows pointing at the end of an object
2491 * but there is some small chance that an optimizing C compiler
2492 * may keep the only reference to an object by pointing
2493 * at the end of it. We ignore this small chance for now.
2494 * Pointers to the end of an object are indistinguishable
2495 * from pointers to the start of the next object in memory
2496 * so if we allow that we'd need to pin two objects...
2497 * We queue the pointer in an array, the
2498 * array will then be sorted and uniqued. This way
2499 * we can coalesce several pinning pointers and it should
2500 * be faster since we'd do a memory scan with increasing
2501 * addresses. Note: we can align the address to the allocation
2502 * alignment, so the unique process is more effective.
2504 mword addr = (mword)*start;
2505 addr &= ~(ALLOC_ALIGN - 1);
2506 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2507 pin_stage_ptr ((void*)addr);
2509 pin_stats_register_address ((char*)addr, pin_type);
2510 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
2515 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2519 * Debugging function: find in the conservative roots where @obj is being pinned.
2521 static G_GNUC_UNUSED void
2522 find_pinning_reference (char *obj, size_t size)
2526 char *endobj = obj + size;
2527 for (i = 0; i < roots_hash_size [0]; ++i) {
2528 for (root = roots_hash [0][i]; root; root = root->next) {
2529 /* if desc is non-null it has precise info */
2530 if (!root->root_desc) {
2531 char ** start = (char**)root->start_root;
2532 while (start < (char**)root->end_root) {
2533 if (*start >= obj && *start < endobj) {
2534 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));
2541 find_pinning_ref_from_thread (obj, size);
2545 * The first thing we do in a collection is to identify pinned objects.
2546 * This function considers all the areas of memory that need to be
2547 * conservatively scanned.
2550 pin_from_roots (void *start_nursery, void *end_nursery)
2554 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]));
2555 /* objects pinned from the API are inside these roots */
2556 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2557 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2558 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2559 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2562 /* now deal with the thread stacks
2563 * in the future we should be able to conservatively scan only:
2564 * *) the cpu registers
2565 * *) the unmanaged stack frames
2566 * *) the _last_ managed stack frame
2567 * *) pointers slots in managed frames
2569 scan_thread_data (start_nursery, end_nursery, FALSE);
2571 evacuate_pin_staging_area ();
2575 * The memory area from start_root to end_root contains pointers to objects.
2576 * Their position is precisely described by @desc (this means that the pointer
2577 * can be either NULL or the pointer to the start of an object).
2578 * This functions copies them to to_space updates them.
2581 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
2583 switch (desc & ROOT_DESC_TYPE_MASK) {
2584 case ROOT_DESC_BITMAP:
2585 desc >>= ROOT_DESC_TYPE_SHIFT;
2587 if ((desc & 1) && *start_root) {
2588 copy_func (start_root);
2589 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2590 drain_gray_stack ();
2596 case ROOT_DESC_COMPLEX: {
2597 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2598 int bwords = (*bitmap_data) - 1;
2599 void **start_run = start_root;
2601 while (bwords-- > 0) {
2602 gsize bmap = *bitmap_data++;
2603 void **objptr = start_run;
2605 if ((bmap & 1) && *objptr) {
2607 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2608 drain_gray_stack ();
2613 start_run += GC_BITS_PER_WORD;
2617 case ROOT_DESC_USER: {
2618 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2619 marker (start_root, copy_func);
2622 case ROOT_DESC_RUN_LEN:
2623 g_assert_not_reached ();
2625 g_assert_not_reached ();
2630 alloc_fragment (void)
2632 Fragment *frag = fragment_freelist;
2634 fragment_freelist = frag->next;
2638 frag = get_internal_mem (sizeof (Fragment), INTERNAL_MEM_FRAGMENT);
2643 /* size must be a power of 2 */
2645 get_os_memory_aligned (mword size, mword alignment, gboolean activate)
2647 /* Allocate twice the memory to be able to put the block on an aligned address */
2648 char *mem = get_os_memory (size + alignment, activate);
2653 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2654 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2657 free_os_memory (mem, aligned - mem);
2658 if (aligned + size < mem + size + alignment)
2659 free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2665 * Allocate and setup the data structures needed to be able to allocate objects
2666 * in the nursery. The nursery is stored in nursery_section.
2669 alloc_nursery (void)
2671 GCMemSection *section;
2677 if (nursery_section)
2679 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %zd\n", nursery_size));
2680 /* later we will alloc a larger area for the nursery but only activate
2681 * what we need. The rest will be used as expansion if we have too many pinned
2682 * objects in the existing nursery.
2684 /* FIXME: handle OOM */
2685 section = get_internal_mem (SIZEOF_GC_MEM_SECTION, INTERNAL_MEM_SECTION);
2687 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2688 alloc_size = nursery_size;
2689 #ifdef ALIGN_NURSERY
2690 data = get_os_memory_aligned (alloc_size, alloc_size, TRUE);
2692 data = get_os_memory (alloc_size, TRUE);
2694 nursery_start = data;
2695 nursery_real_end = nursery_start + nursery_size;
2696 UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
2697 nursery_next = nursery_start;
2698 total_alloc += alloc_size;
2699 DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %zd, total: %zd\n", data, data + alloc_size, nursery_size, total_alloc));
2700 section->data = section->next_data = data;
2701 section->size = alloc_size;
2702 section->end_data = nursery_real_end;
2703 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2704 section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2705 section->num_scan_start = scan_starts;
2706 section->block.role = MEMORY_ROLE_GEN0;
2707 section->block.next = NULL;
2709 nursery_section = section;
2711 /* Setup the single first large fragment */
2712 frag = alloc_fragment ();
2713 frag->fragment_start = nursery_start;
2714 frag->fragment_limit = nursery_start;
2715 frag->fragment_end = nursery_real_end;
2716 nursery_frag_real_end = nursery_real_end;
2717 /* FIXME: frag here is lost */
2721 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list) {
2724 for (fin = list; fin; fin = fin->next) {
2727 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2728 copy_func (&fin->object);
2732 static mword fragment_total = 0;
2734 * We found a fragment of free memory in the nursery: memzero it and if
2735 * it is big enough, add it to the list of fragments that can be used for
2739 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2742 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2743 binary_protocol_empty (frag_start, frag_size);
2744 /* memsetting just the first chunk start is bound to provide better cache locality */
2745 if (nursery_clear_policy == CLEAR_AT_GC)
2746 memset (frag_start, 0, frag_size);
2747 /* Not worth dealing with smaller fragments: need to tune */
2748 if (frag_size >= FRAGMENT_MIN_SIZE) {
2749 fragment = alloc_fragment ();
2750 fragment->fragment_start = frag_start;
2751 fragment->fragment_limit = frag_start;
2752 fragment->fragment_end = frag_end;
2753 fragment->next = nursery_fragments;
2754 nursery_fragments = fragment;
2755 fragment_total += frag_size;
2757 /* Clear unused fragments, pinning depends on this */
2758 memset (frag_start, 0, frag_size);
2763 generation_name (int generation)
2765 switch (generation) {
2766 case GENERATION_NURSERY: return "nursery";
2767 case GENERATION_OLD: return "old";
2768 default: g_assert_not_reached ();
2772 static DisappearingLinkHashTable*
2773 get_dislink_hash_table (int generation)
2775 switch (generation) {
2776 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2777 case GENERATION_OLD: return &major_disappearing_link_hash;
2778 default: g_assert_not_reached ();
2782 static FinalizeEntryHashTable*
2783 get_finalize_entry_hash_table (int generation)
2785 switch (generation) {
2786 case GENERATION_NURSERY: return &minor_finalizable_hash;
2787 case GENERATION_OLD: return &major_finalizable_hash;
2788 default: g_assert_not_reached ();
2793 finish_gray_stack (char *start_addr, char *end_addr, int generation)
2798 int ephemeron_rounds = 0;
2799 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? copy_object : major_copy_or_mark_object;
2802 * We copied all the reachable objects. Now it's the time to copy
2803 * the objects that were not referenced by the roots, but by the copied objects.
2804 * we built a stack of objects pointed to by gray_start: they are
2805 * additional roots and we may add more items as we go.
2806 * We loop until gray_start == gray_objects which means no more objects have
2807 * been added. Note this is iterative: no recursion is involved.
2808 * We need to walk the LO list as well in search of marked big objects
2809 * (use a flag since this is needed only on major collections). We need to loop
2810 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2811 * To achieve better cache locality and cache usage, we drain the gray stack
2812 * frequently, after each object is copied, and just finish the work here.
2814 drain_gray_stack ();
2816 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2817 /* walk the finalization queue and move also the objects that need to be
2818 * finalized: use the finalized objects as new roots so the objects they depend
2819 * on are also not reclaimed. As with the roots above, only objects in the nursery
2820 * are marked/copied.
2821 * We need a loop here, since objects ready for finalizers may reference other objects
2822 * that are fin-ready. Speedup with a flag?
2826 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2827 * before processing finalizable objects to avoid finalizing reachable values.
2829 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2830 * while they are been finalized.
2832 int done_with_ephemerons = 0;
2834 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr);
2835 drain_gray_stack ();
2837 } while (!done_with_ephemerons);
2839 fin_ready = num_ready_finalizers;
2840 finalize_in_range (copy_func, start_addr, end_addr, generation);
2841 if (generation == GENERATION_OLD)
2842 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY);
2844 /* drain the new stack that might have been created */
2845 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2846 drain_gray_stack ();
2847 } while (fin_ready != num_ready_finalizers);
2850 * Clear ephemeron pairs with unreachable keys.
2851 * We pass the copy func so we can figure out if an array was promoted or not.
2853 clear_unreachable_ephemerons (copy_func, start_addr, end_addr);
2856 DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron roundss\n", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds));
2859 * handle disappearing links
2860 * Note we do this after checking the finalization queue because if an object
2861 * survives (at least long enough to be finalized) we don't clear the link.
2862 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2863 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2866 g_assert (gray_object_queue_is_empty ());
2868 null_link_in_range (copy_func, start_addr, end_addr, generation);
2869 if (generation == GENERATION_OLD)
2870 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY);
2871 if (gray_object_queue_is_empty ())
2873 drain_gray_stack ();
2876 g_assert (gray_object_queue_is_empty ());
2880 check_section_scan_starts (GCMemSection *section)
2883 for (i = 0; i < section->num_scan_start; ++i) {
2884 if (section->scan_starts [i]) {
2885 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2886 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2892 check_scan_starts (void)
2894 if (!do_scan_starts_check)
2896 check_section_scan_starts (nursery_section);
2897 major_check_scan_starts ();
2900 static int last_num_pinned = 0;
2903 build_nursery_fragments (int start_pin, int end_pin)
2905 char *frag_start, *frag_end;
2909 while (nursery_fragments) {
2910 Fragment *next = nursery_fragments->next;
2911 nursery_fragments->next = fragment_freelist;
2912 fragment_freelist = nursery_fragments;
2913 nursery_fragments = next;
2915 frag_start = nursery_start;
2917 /* clear scan starts */
2918 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2919 for (i = start_pin; i < end_pin; ++i) {
2920 frag_end = pin_queue [i];
2921 /* remove the pin bit from pinned objects */
2922 unpin_object (frag_end);
2923 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2924 frag_size = frag_end - frag_start;
2926 add_nursery_frag (frag_size, frag_start, frag_end);
2927 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
2928 frag_size += ALLOC_ALIGN - 1;
2929 frag_size &= ~(ALLOC_ALIGN - 1);
2930 frag_start = (char*)pin_queue [i] + frag_size;
2932 nursery_last_pinned_end = frag_start;
2933 frag_end = nursery_real_end;
2934 frag_size = frag_end - frag_start;
2936 add_nursery_frag (frag_size, frag_start, frag_end);
2937 if (!nursery_fragments) {
2938 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
2939 for (i = start_pin; i < end_pin; ++i) {
2940 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])));
2945 nursery_next = nursery_frag_real_end = NULL;
2947 /* Clear TLABs for all threads */
2952 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type)
2956 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2957 for (root = roots_hash [root_type][i]; root; root = root->next) {
2958 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2959 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
2965 dump_occupied (char *start, char *end, char *section_start)
2967 fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
2971 dump_section (GCMemSection *section, const char *type)
2973 char *start = section->data;
2974 char *end = section->data + section->size;
2975 char *occ_start = NULL;
2977 char *old_start = NULL; /* just for debugging */
2979 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", type, section->size);
2981 while (start < end) {
2985 if (!*(void**)start) {
2987 dump_occupied (occ_start, start, section->data);
2990 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2993 g_assert (start < section->next_data);
2998 vt = (GCVTable*)LOAD_VTABLE (start);
3001 size = safe_object_get_size ((MonoObject*) start);
3002 size += ALLOC_ALIGN - 1;
3003 size &= ~(ALLOC_ALIGN - 1);
3006 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
3007 start - section->data,
3008 vt->klass->name_space, vt->klass->name,
3016 dump_occupied (occ_start, start, section->data);
3018 fprintf (heap_dump_file, "</section>\n");
3022 dump_object (MonoObject *obj, gboolean dump_location)
3024 static char class_name [1024];
3026 MonoClass *class = mono_object_class (obj);
3030 * Python's XML parser is too stupid to parse angle brackets
3031 * in strings, so we just ignore them;
3034 while (class->name [i] && j < sizeof (class_name) - 1) {
3035 if (!strchr ("<>\"", class->name [i]))
3036 class_name [j++] = class->name [i];
3039 g_assert (j < sizeof (class_name));
3042 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
3043 class->name_space, class_name,
3044 safe_object_get_size (obj));
3045 if (dump_location) {
3046 const char *location;
3047 if (ptr_in_nursery (obj))
3048 location = "nursery";
3049 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
3053 fprintf (heap_dump_file, " location=\"%s\"", location);
3055 fprintf (heap_dump_file, "/>\n");
3059 dump_heap (const char *type, int num, const char *reason)
3061 static char const *internal_mem_names [] = { "pin-queue", "fragment", "section", "scan-starts",
3062 "fin-table", "finalize-entry", "dislink-table",
3063 "dislink", "roots-table", "root-record", "statistics",
3064 "remset", "gray-queue", "store-remset", "marksweep-tables",
3065 "marksweep-block-info", "ephemeron-link" };
3071 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
3073 fprintf (heap_dump_file, " reason=\"%s\"", reason);
3074 fprintf (heap_dump_file, ">\n");
3075 fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%ld\"/>\n", pinned_chunk_bytes_alloced);
3076 fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%ld\"/>\n", large_internal_bytes_alloced);
3077 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
3078 for (i = 0; i < INTERNAL_MEM_MAX; ++i)
3079 fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n", internal_mem_names [i], small_internal_mem_bytes [i]);
3080 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
3081 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
3082 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
3084 fprintf (heap_dump_file, "<pinned-objects>\n");
3085 for (list = pinned_objects; list; list = list->next)
3086 dump_object (list->obj, TRUE);
3087 fprintf (heap_dump_file, "</pinned-objects>\n");
3089 dump_section (nursery_section, "nursery");
3093 fprintf (heap_dump_file, "<los>\n");
3094 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
3095 dump_object ((MonoObject*)bigobj->data, FALSE);
3096 fprintf (heap_dump_file, "</los>\n");
3098 fprintf (heap_dump_file, "</collection>\n");
3104 static gboolean inited = FALSE;
3109 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
3110 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
3111 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
3112 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
3113 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
3114 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
3115 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3116 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
3118 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
3119 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
3120 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
3121 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
3122 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
3123 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
3124 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
3125 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
3126 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
3127 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
3128 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
3130 #ifdef HEAVY_STATISTICS
3131 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
3132 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
3133 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
3134 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
3135 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
3136 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
3137 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
3138 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
3140 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
3141 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
3142 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
3143 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
3144 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
3146 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
3147 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
3148 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
3149 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
3151 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
3152 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
3154 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
3155 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
3156 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
3158 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
3159 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
3160 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
3161 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
3162 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
3163 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
3164 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
3171 * Collect objects in the nursery. Returns whether to trigger a major
3175 collect_nursery (size_t requested_size)
3177 size_t max_garbage_amount;
3178 char *orig_nursery_next;
3179 TV_DECLARE (all_atv);
3180 TV_DECLARE (all_btv);
3184 current_collection_generation = GENERATION_NURSERY;
3187 binary_protocol_collection (GENERATION_NURSERY);
3188 check_scan_starts ();
3191 orig_nursery_next = nursery_next;
3192 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3193 /* FIXME: optimize later to use the higher address where an object can be present */
3194 nursery_next = MAX (nursery_next, nursery_real_end);
3196 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)));
3197 max_garbage_amount = nursery_next - nursery_start;
3198 g_assert (nursery_section->size >= max_garbage_amount);
3200 /* world must be stopped already */
3201 TV_GETTIME (all_atv);
3204 /* Pinning depends on this */
3205 clear_nursery_fragments (orig_nursery_next);
3208 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3211 check_for_xdomain_refs ();
3213 nursery_section->next_data = nursery_next;
3215 major_start_nursery_collection ();
3217 gray_object_queue_init ();
3220 mono_stats.minor_gc_count ++;
3221 /* pin from pinned handles */
3223 pin_from_roots (nursery_start, nursery_next);
3224 /* identify pinned objects */
3225 optimize_pin_queue (0);
3226 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
3227 nursery_section->pin_queue_start = 0;
3228 nursery_section->pin_queue_end = next_pin_slot;
3230 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3231 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3232 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3234 if (consistency_check_at_minor_collection)
3235 check_consistency ();
3238 * walk all the roots and copy the young objects to the old generation,
3239 * starting from to_space
3242 scan_from_remsets (nursery_start, nursery_next);
3243 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3245 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3246 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3248 drain_gray_stack ();
3251 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3252 /* registered roots, this includes static fields */
3253 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL);
3254 scan_from_registered_roots (copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER);
3256 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3258 scan_thread_data (nursery_start, nursery_next, TRUE);
3260 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3263 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY);
3265 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3267 /* walk the pin_queue, build up the fragment list of free memory, unmark
3268 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3271 build_nursery_fragments (0, next_pin_slot);
3273 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3274 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %zd bytes available\n", TV_ELAPSED (atv, btv), fragment_total));
3276 major_finish_nursery_collection ();
3278 TV_GETTIME (all_btv);
3279 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3282 dump_heap ("minor", num_minor_gcs - 1, NULL);
3284 /* prepare the pin queue for the next collection */
3285 last_num_pinned = next_pin_slot;
3287 if (fin_ready_list || critical_fin_list) {
3288 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3289 mono_gc_finalize_notify ();
3293 g_assert (gray_object_queue_is_empty ());
3295 check_scan_starts ();
3297 current_collection_generation = -1;
3299 return major_need_major_collection ();
3303 major_do_collection (const char *reason)
3305 LOSObject *bigobj, *prevbo;
3306 TV_DECLARE (all_atv);
3307 TV_DECLARE (all_btv);
3310 /* FIXME: only use these values for the precise scan
3311 * note that to_space pointers should be excluded anyway...
3313 char *heap_start = NULL;
3314 char *heap_end = (char*)-1;
3315 int old_num_major_sections = num_major_sections;
3316 int num_major_sections_saved, save_target, allowance_target;
3318 //count_ref_nonref_objs ();
3319 //consistency_check ();
3322 binary_protocol_collection (GENERATION_OLD);
3323 check_scan_starts ();
3324 gray_object_queue_init ();
3327 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3329 mono_stats.major_gc_count ++;
3331 /* world must be stopped already */
3332 TV_GETTIME (all_atv);
3335 /* Pinning depends on this */
3336 clear_nursery_fragments (nursery_next);
3339 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3342 check_for_xdomain_refs ();
3344 nursery_section->next_data = nursery_real_end;
3345 /* we should also coalesce scanning from sections close to each other
3346 * and deal with pointers outside of the sections later.
3348 /* The remsets are not useful for a major collection */
3353 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3354 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3355 optimize_pin_queue (0);
3358 * pin_queue now contains all candidate pointers, sorted and
3359 * uniqued. We must do two passes now to figure out which
3360 * objects are pinned.
3362 * The first is to find within the pin_queue the area for each
3363 * section. This requires that the pin_queue be sorted. We
3364 * also process the LOS objects and pinned chunks here.
3366 * The second, destructive, pass is to reduce the section
3367 * areas to pointers to the actually pinned objects.
3369 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3370 /* first pass for the sections */
3371 find_section_pin_queue_start_end (nursery_section);
3372 major_find_pin_queue_start_ends ();
3373 /* identify possible pointers to the insize of large objects */
3374 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3375 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3377 find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &start, &end);
3379 pin_object (bigobj->data);
3380 /* FIXME: only enqueue if object has references */
3381 GRAY_OBJECT_ENQUEUE (bigobj->data);
3383 pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3384 DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
3387 /* second pass for the sections */
3388 pin_objects_in_section (nursery_section);
3389 major_pin_objects ();
3392 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3393 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3394 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3396 major_init_to_space ();
3398 drain_gray_stack ();
3401 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3403 /* registered roots, this includes static fields */
3404 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL);
3405 scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER);
3407 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3410 /* FIXME: This is the wrong place for this, because it does
3412 scan_thread_data (heap_start, heap_end, TRUE);
3414 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3417 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3419 /* scan the list of objects ready for finalization */
3420 scan_finalizer_entries (major_copy_or_mark_object, fin_ready_list);
3421 scan_finalizer_entries (major_copy_or_mark_object, critical_fin_list);
3423 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3424 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3427 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3429 /* all the objects in the heap */
3430 finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
3432 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3434 /* sweep the big objects list */
3436 for (bigobj = los_object_list; bigobj;) {
3437 if (object_is_pinned (bigobj->data)) {
3438 unpin_object (bigobj->data);
3441 /* not referenced anywhere, so we can free it */
3443 prevbo->next = bigobj->next;
3445 los_object_list = bigobj->next;
3447 bigobj = bigobj->next;
3448 free_large_object (to_free);
3452 bigobj = bigobj->next;
3458 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3460 /* walk the pin_queue, build up the fragment list of free memory, unmark
3461 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3464 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
3467 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3469 TV_GETTIME (all_btv);
3470 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3473 dump_heap ("major", num_major_gcs - 1, reason);
3475 /* prepare the pin queue for the next collection */
3477 if (fin_ready_list || critical_fin_list) {
3478 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3479 mono_gc_finalize_notify ();
3483 g_assert (gray_object_queue_is_empty ());
3485 num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 1);
3487 save_target = num_major_sections / 2;
3489 * We aim to allow the allocation of as many sections as is
3490 * necessary to reclaim save_target sections in the next
3491 * collection. We assume the collection pattern won't change.
3492 * In the last cycle, we had num_major_sections_saved for
3493 * minor_collection_sections_alloced. Assuming things won't
3494 * change, this must be the same ratio as save_target for
3495 * allowance_target, i.e.
3497 * num_major_sections_saved save_target
3498 * --------------------------------- == ----------------
3499 * minor_collection_sections_alloced allowance_target
3503 allowance_target = save_target * minor_collection_sections_alloced / num_major_sections_saved;
3505 minor_collection_section_allowance = MAX (MIN (allowance_target, num_major_sections), MIN_MINOR_COLLECTION_SECTION_ALLOWANCE);
3507 minor_collection_sections_alloced = 0;
3509 check_scan_starts ();
3511 //consistency_check ();
3515 major_collection (const char *reason)
3517 if (g_getenv ("MONO_GC_NO_MAJOR")) {
3518 collect_nursery (0);
3522 current_collection_generation = GENERATION_OLD;
3523 major_do_collection (reason);
3524 current_collection_generation = -1;
3528 * When deciding if it's better to collect or to expand, keep track
3529 * of how much garbage was reclaimed with the last collection: if it's too
3531 * This is called when we could not allocate a small object.
3533 static void __attribute__((noinline))
3534 minor_collect_or_expand_inner (size_t size)
3536 int do_minor_collection = 1;
3538 if (!nursery_section) {
3542 if (do_minor_collection) {
3544 if (collect_nursery (size))
3545 major_collection ("minor overflow");
3546 DEBUG (2, fprintf (gc_debug_file, "Heap size: %zd, LOS size: %zd\n", total_alloc, los_memory_usage));
3548 /* this also sets the proper pointers for the next allocation */
3549 if (!search_fragment_for_size (size)) {
3551 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3552 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3553 for (i = 0; i < last_num_pinned; ++i) {
3554 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])));
3559 //report_internal_mem_usage ();
3563 * ######################################################################
3564 * ######## Memory allocation from the OS
3565 * ######################################################################
3566 * This section of code deals with getting memory from the OS and
3567 * allocating memory for GC-internal data structures.
3568 * Internal memory can be handled with a freelist for small objects.
3572 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3573 * This must not require any lock.
3576 get_os_memory (size_t size, int activate)
3579 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3581 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3582 size += pagesize - 1;
3583 size &= ~(pagesize - 1);
3584 ptr = mono_valloc (0, size, prot_flags);
3589 * Free the memory returned by get_os_memory (), returning it to the OS.
3592 free_os_memory (void *addr, size_t size)
3594 mono_vfree (addr, size);
3601 report_pinned_chunk (PinnedChunk *chunk, int seq) {
3603 int i, free_pages, num_free, free_mem;
3605 for (i = 0; i < chunk->num_pages; ++i) {
3606 if (!chunk->page_sizes [i])
3609 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);
3610 free_mem = FREELIST_PAGESIZE * free_pages;
3611 for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
3612 if (!chunk->free_list [i])
3615 p = chunk->free_list [i];
3620 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
3621 free_mem += freelist_sizes [i] * num_free;
3623 printf ("\tfree memory in chunk: %d\n", free_mem);
3629 static G_GNUC_UNUSED void
3630 report_internal_mem_usage (void) {
3633 printf ("Internal memory usage:\n");
3635 for (chunk = internal_chunk_list; chunk; chunk = chunk->block.next) {
3636 report_pinned_chunk (chunk, i++);
3638 printf ("Pinned memory usage:\n");
3639 major_report_pinned_memory_usage ();
3643 * Find the slot number in the freelist for memory chunks that
3644 * can contain @size objects.
3647 slot_for_size (size_t size)
3650 /* do a binary search or lookup table later. */
3651 for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
3652 if (freelist_sizes [slot] >= size)
3655 g_assert_not_reached ();
3660 * Build a free list for @size memory chunks from the memory area between
3661 * start_page and end_page.
3664 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
3668 /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
3669 p = (void**)start_page;
3670 end = (void**)(end_page - size);
3671 g_assert (!chunk->free_list [slot]);
3672 chunk->free_list [slot] = p;
3673 while ((char*)p + size <= (char*)end) {
3675 *p = (void*)((char*)p + size);
3679 /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
3683 alloc_pinned_chunk (void)
3687 int size = PINNED_CHUNK_SIZE;
3689 chunk = get_os_memory_aligned (size, size, TRUE);
3690 chunk->block.role = MEMORY_ROLE_PINNED;
3692 UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
3693 total_alloc += size;
3694 pinned_chunk_bytes_alloced += size;
3696 /* setup the bookeeping fields */
3697 chunk->num_pages = size / FREELIST_PAGESIZE;
3698 offset = G_STRUCT_OFFSET (PinnedChunk, data);
3699 chunk->page_sizes = (void*)((char*)chunk + offset);
3700 offset += sizeof (int) * chunk->num_pages;
3701 offset += ALLOC_ALIGN - 1;
3702 offset &= ~(ALLOC_ALIGN - 1);
3703 chunk->free_list = (void*)((char*)chunk + offset);
3704 offset += sizeof (void*) * FREELIST_NUM_SLOTS;
3705 offset += ALLOC_ALIGN - 1;
3706 offset &= ~(ALLOC_ALIGN - 1);
3707 chunk->start_data = (void*)((char*)chunk + offset);
3709 /* allocate the first page to the freelist */
3710 chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
3711 build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
3712 DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
3716 /* assumes freelist for slot is empty, so try to alloc a new page */
3718 get_chunk_freelist (PinnedChunk *chunk, int slot)
3722 p = chunk->free_list [slot];
3724 chunk->free_list [slot] = *p;
3727 for (i = 0; i < chunk->num_pages; ++i) {
3729 if (chunk->page_sizes [i])
3731 size = freelist_sizes [slot];
3732 chunk->page_sizes [i] = size;
3733 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
3737 p = chunk->free_list [slot];
3739 chunk->free_list [slot] = *p;
3745 /* used for the GC-internal data structures */
3747 get_internal_mem (size_t size, int type)
3751 PinnedChunk *pchunk;
3753 if (size > freelist_sizes [FREELIST_NUM_SLOTS - 1]) {
3754 LargeInternalMemHeader *mh;
3756 size += sizeof (LargeInternalMemHeader);
3757 mh = get_os_memory (size, TRUE);
3758 mh->magic = LARGE_INTERNAL_MEM_HEADER_MAGIC;
3761 large_internal_bytes_alloced += size;
3766 slot = slot_for_size (size);
3767 g_assert (size <= freelist_sizes [slot]);
3769 small_internal_mem_bytes [type] += freelist_sizes [slot];
3771 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3772 void **p = pchunk->free_list [slot];
3774 pchunk->free_list [slot] = *p;
3775 memset (p, 0, size);
3779 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3780 res = get_chunk_freelist (pchunk, slot);
3782 memset (res, 0, size);
3786 pchunk = alloc_pinned_chunk ();
3787 /* FIXME: handle OOM */
3788 pchunk->block.next = internal_chunk_list;
3789 internal_chunk_list = pchunk;
3790 res = get_chunk_freelist (pchunk, slot);
3791 memset (res, 0, size);
3796 free_internal_mem (void *addr, int type)
3798 PinnedChunk *pchunk;
3799 LargeInternalMemHeader *mh;
3802 for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
3803 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
3804 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
3805 int offset = (char*)addr - (char*)pchunk;
3806 int page = offset / FREELIST_PAGESIZE;
3807 int slot = slot_for_size (pchunk->page_sizes [page]);
3809 *p = pchunk->free_list [slot];
3810 pchunk->free_list [slot] = p;
3812 small_internal_mem_bytes [type] -= freelist_sizes [slot];
3817 mh = (LargeInternalMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargeInternalMemHeader, data));
3818 g_assert (mh->magic == LARGE_INTERNAL_MEM_HEADER_MAGIC);
3819 large_internal_bytes_alloced -= mh->size;
3820 free_os_memory (mh, mh->size);
3824 * ######################################################################
3825 * ######## Object allocation
3826 * ######################################################################
3827 * This section of code deals with allocating memory for objects.
3828 * There are several ways:
3829 * *) allocate large objects
3830 * *) allocate normal objects
3831 * *) fast lock-free allocation
3832 * *) allocation of pinned objects
3836 free_large_object (LOSObject *obj)
3838 size_t size = obj->size;
3839 DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %zd\n", obj->data, obj->size));
3840 binary_protocol_empty (obj->data, obj->size);
3842 los_memory_usage -= size;
3843 size += sizeof (LOSObject);
3844 size += pagesize - 1;
3845 size &= ~(pagesize - 1);
3846 total_alloc -= size;
3848 free_os_memory (obj, size);
3852 * Objects with size >= 64KB are allocated in the large object space.
3853 * They are currently kept track of with a linked list.
3854 * They don't move, so there is no need to pin them during collection
3855 * and we avoid the memcpy overhead.
3857 static void* __attribute__((noinline))
3858 alloc_large_inner (MonoVTable *vtable, size_t size)
3864 g_assert (size > MAX_SMALL_OBJ_SIZE);
3866 if (los_memory_usage > next_los_collection) {
3867 static mword last_los_memory_usage = 0;
3869 mword los_memory_alloced;
3870 mword old_los_memory_usage;
3871 mword los_memory_saved;
3873 mword allowance_target;
3876 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));
3879 g_assert (los_memory_usage >= last_los_memory_usage);
3880 los_memory_alloced = los_memory_usage - last_los_memory_usage;
3881 old_los_memory_usage = los_memory_usage;
3883 major_collection ("LOS overflow");
3885 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
3886 save_target = los_memory_usage / 2;
3888 * see the comment at the end of major_collection()
3889 * for the explanation for this calculation.
3891 allowance_target = (mword)((double)save_target * (double)los_memory_alloced / (double)los_memory_saved);
3892 allowance = MAX (MIN (allowance_target, los_memory_usage), MIN_LOS_ALLOWANCE);
3893 next_los_collection = los_memory_usage + allowance;
3895 last_los_memory_usage = los_memory_usage;
3900 alloc_size += sizeof (LOSObject);
3901 alloc_size += pagesize - 1;
3902 alloc_size &= ~(pagesize - 1);
3903 /* FIXME: handle OOM */
3904 obj = get_os_memory (alloc_size, TRUE);
3905 g_assert (!((mword)obj->data & (ALLOC_ALIGN - 1)));
3907 vtslot = (void**)obj->data;
3909 total_alloc += alloc_size;
3910 UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
3911 obj->next = los_object_list;
3912 los_object_list = obj;
3913 los_memory_usage += size;
3915 DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %zd\n", obj->data, vtable, vtable->klass->name, size));
3916 binary_protocol_alloc (obj->data, vtable, size);
3920 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3921 * an object of size @size
3922 * Return FALSE if not found (which means we need a collection)
3925 search_fragment_for_size (size_t size)
3927 Fragment *frag, *prev;
3928 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3930 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3931 /* Clear the remaining space, pinning depends on this */
3932 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3935 for (frag = nursery_fragments; frag; frag = frag->next) {
3936 if (size <= (frag->fragment_end - frag->fragment_start)) {
3937 /* remove from the list */
3939 prev->next = frag->next;
3941 nursery_fragments = frag->next;
3942 nursery_next = frag->fragment_start;
3943 nursery_frag_real_end = frag->fragment_end;
3945 DEBUG (4, fprintf (gc_debug_file, "Using nursery fragment %p-%p, size: %zd (req: %zd)\n", nursery_next, nursery_frag_real_end, nursery_frag_real_end - nursery_next, size));
3946 frag->next = fragment_freelist;
3947 fragment_freelist = frag;
3956 * Provide a variant that takes just the vtable for small fixed-size objects.
3957 * The aligned size is already computed and stored in vt->gc_descr.
3958 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3959 * processing. We can keep track of where objects start, for example,
3960 * so when we scan the thread stacks for pinned objects, we can start
3961 * a search for the pinned object in SCAN_START_SIZE chunks.
3964 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3966 /* FIXME: handle OOM */
3972 HEAVY_STAT (++stat_objects_alloced);
3973 if (size <= MAX_SMALL_OBJ_SIZE)
3974 HEAVY_STAT (stat_bytes_alloced += size);
3976 HEAVY_STAT (stat_bytes_alloced_los += size);
3978 size += ALLOC_ALIGN - 1;
3979 size &= ~(ALLOC_ALIGN - 1);
3981 g_assert (vtable->gc_descr);
3983 if (G_UNLIKELY (collect_before_allocs)) {
3984 if (nursery_section) {
3986 collect_nursery (0);
3988 if (!degraded_mode && !search_fragment_for_size (size)) {
3990 g_assert_not_reached ();
3996 * We must already have the lock here instead of after the
3997 * fast path because we might be interrupted in the fast path
3998 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3999 * and we'll end up allocating an object in a fragment which
4000 * no longer belongs to us.
4002 * The managed allocator does not do this, but it's treated
4003 * specially by the world-stopping code.
4006 if (size > MAX_SMALL_OBJ_SIZE) {
4007 p = alloc_large_inner (vtable, size);
4009 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4011 p = (void**)TLAB_NEXT;
4012 /* FIXME: handle overflow */
4013 new_next = (char*)p + size;
4014 TLAB_NEXT = new_next;
4016 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4020 * FIXME: We might need a memory barrier here so the change to tlab_next is
4021 * visible before the vtable store.
4024 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4025 binary_protocol_alloc (p , vtable, size);
4026 g_assert (*p == NULL);
4029 g_assert (TLAB_NEXT == new_next);
4036 /* there are two cases: the object is too big or we run out of space in the TLAB */
4037 /* we also reach here when the thread does its first allocation after a minor
4038 * collection, since the tlab_ variables are initialized to NULL.
4039 * there can be another case (from ORP), if we cooperate with the runtime a bit:
4040 * objects that need finalizers can have the high bit set in their size
4041 * so the above check fails and we can readily add the object to the queue.
4042 * This avoids taking again the GC lock when registering, but this is moot when
4043 * doing thread-local allocation, so it may not be a good idea.
4045 g_assert (TLAB_NEXT == new_next);
4046 if (TLAB_NEXT >= TLAB_REAL_END) {
4048 * Run out of space in the TLAB. When this happens, some amount of space
4049 * remains in the TLAB, but not enough to satisfy the current allocation
4050 * request. Currently, we retire the TLAB in all cases, later we could
4051 * keep it if the remaining space is above a treshold, and satisfy the
4052 * allocation directly from the nursery.
4055 /* when running in degraded mode, we continue allocing that way
4056 * for a while, to decrease the number of useless nursery collections.
4058 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
4059 p = alloc_degraded (vtable, size);
4063 if (size > tlab_size) {
4064 /* Allocate directly from the nursery */
4065 if (nursery_next + size >= nursery_frag_real_end) {
4066 if (!search_fragment_for_size (size)) {
4067 minor_collect_or_expand_inner (size);
4068 if (degraded_mode) {
4069 p = alloc_degraded (vtable, size);
4075 p = (void*)nursery_next;
4076 nursery_next += size;
4077 if (nursery_next > nursery_frag_real_end) {
4082 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4083 memset (p, 0, size);
4086 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
4088 if (nursery_next + tlab_size >= nursery_frag_real_end) {
4089 res = search_fragment_for_size (tlab_size);
4091 minor_collect_or_expand_inner (tlab_size);
4092 if (degraded_mode) {
4093 p = alloc_degraded (vtable, size);
4099 /* Allocate a new TLAB from the current nursery fragment */
4100 TLAB_START = nursery_next;
4101 nursery_next += tlab_size;
4102 TLAB_NEXT = TLAB_START;
4103 TLAB_REAL_END = TLAB_START + tlab_size;
4104 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, tlab_size);
4106 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4107 memset (TLAB_START, 0, tlab_size);
4109 /* Allocate from the TLAB */
4110 p = (void*)TLAB_NEXT;
4112 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4114 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4117 /* Reached tlab_temp_end */
4119 /* record the scan start so we can find pinned objects more easily */
4120 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4121 /* we just bump tlab_temp_end as well */
4122 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4123 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4127 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4128 binary_protocol_alloc (p, vtable, size);
4135 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4141 size += ALLOC_ALIGN - 1;
4142 size &= ~(ALLOC_ALIGN - 1);
4144 g_assert (vtable->gc_descr);
4145 if (size <= MAX_SMALL_OBJ_SIZE) {
4146 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4148 p = (void**)TLAB_NEXT;
4149 /* FIXME: handle overflow */
4150 new_next = (char*)p + size;
4151 TLAB_NEXT = new_next;
4153 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4157 * FIXME: We might need a memory barrier here so the change to tlab_next is
4158 * visible before the vtable store.
4161 HEAVY_STAT (++stat_objects_alloced);
4162 HEAVY_STAT (stat_bytes_alloced += size);
4164 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4165 binary_protocol_alloc (p, vtable, size);
4166 g_assert (*p == NULL);
4169 g_assert (TLAB_NEXT == new_next);
4178 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4181 #ifndef DISABLE_CRITICAL_REGION
4183 ENTER_CRITICAL_REGION;
4184 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4186 EXIT_CRITICAL_REGION;
4189 EXIT_CRITICAL_REGION;
4192 res = mono_gc_alloc_obj_nolock (vtable, size);
4198 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4201 #ifndef DISABLE_CRITICAL_REGION
4203 ENTER_CRITICAL_REGION;
4204 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4206 arr->max_length = max_length;
4207 EXIT_CRITICAL_REGION;
4210 EXIT_CRITICAL_REGION;
4215 arr = mono_gc_alloc_obj_nolock (vtable, size);
4216 arr->max_length = max_length;
4224 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4227 MonoArrayBounds *bounds;
4231 arr = mono_gc_alloc_obj_nolock (vtable, size);
4232 arr->max_length = max_length;
4234 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4235 arr->bounds = bounds;
4243 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4246 #ifndef DISABLE_CRITICAL_REGION
4248 ENTER_CRITICAL_REGION;
4249 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4252 EXIT_CRITICAL_REGION;
4255 EXIT_CRITICAL_REGION;
4260 str = mono_gc_alloc_obj_nolock (vtable, size);
4269 * To be used for interned strings and possibly MonoThread, reflection handles.
4270 * We may want to explicitly free these objects.
4273 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4275 /* FIXME: handle OOM */
4277 size += ALLOC_ALIGN - 1;
4278 size &= ~(ALLOC_ALIGN - 1);
4280 if (size > MAX_SMALL_OBJ_SIZE) {
4281 /* large objects are always pinned anyway */
4282 p = alloc_large_inner (vtable, size);
4284 DEBUG (9, g_assert (vtable->klass->inited));
4285 p = major_alloc_small_pinned_obj (size, vtable->klass->has_references);
4287 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4288 binary_protocol_alloc (p, vtable, size);
4295 * ######################################################################
4296 * ######## Finalization support
4297 * ######################################################################
4301 * this is valid for the nursery: if the object has been forwarded it means it's
4302 * still refrenced from a root. If it is pinned it's still alive as well.
4303 * Return TRUE if @obj is ready to be finalized.
4305 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4308 is_critical_finalizer (FinalizeEntry *entry)
4313 if (!mono_defaults.critical_finalizer_object)
4316 obj = entry->object;
4317 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4319 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4323 queue_finalization_entry (FinalizeEntry *entry) {
4324 if (is_critical_finalizer (entry)) {
4325 entry->next = critical_fin_list;
4326 critical_fin_list = entry;
4328 entry->next = fin_ready_list;
4329 fin_ready_list = entry;
4333 /* LOCKING: requires that the GC lock is held */
4335 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4337 FinalizeEntry **finalizable_hash = hash_table->table;
4338 mword finalizable_hash_size = hash_table->size;
4341 FinalizeEntry **new_hash;
4342 FinalizeEntry *entry, *next;
4343 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4345 new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4346 for (i = 0; i < finalizable_hash_size; ++i) {
4347 for (entry = finalizable_hash [i]; entry; entry = next) {
4348 hash = mono_object_hash (entry->object) % new_size;
4350 entry->next = new_hash [hash];
4351 new_hash [hash] = entry;
4354 free_internal_mem (finalizable_hash, INTERNAL_MEM_FIN_TABLE);
4355 hash_table->table = new_hash;
4356 hash_table->size = new_size;
4359 /* LOCKING: requires that the GC lock is held */
4361 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4363 if (hash_table->num_registered >= hash_table->size * 2)
4364 rehash_fin_table (hash_table);
4367 /* LOCKING: requires that the GC lock is held */
4369 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4371 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4372 FinalizeEntry *entry, *prev;
4374 FinalizeEntry **finalizable_hash = hash_table->table;
4375 mword finalizable_hash_size = hash_table->size;
4379 for (i = 0; i < finalizable_hash_size; ++i) {
4381 for (entry = finalizable_hash [i]; entry;) {
4382 if ((char*)entry->object >= start && (char*)entry->object < end && !major_is_object_live (entry->object)) {
4383 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4384 char *copy = entry->object;
4385 copy_func ((void**)©);
4388 FinalizeEntry *next;
4389 /* remove and put in fin_ready_list */
4391 prev->next = entry->next;
4393 finalizable_hash [i] = entry->next;
4395 num_ready_finalizers++;
4396 hash_table->num_registered--;
4397 queue_finalization_entry (entry);
4398 /* Make it survive */
4399 from = entry->object;
4400 entry->object = copy;
4401 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));
4405 char *from = entry->object;
4406 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4407 FinalizeEntry *next = entry->next;
4408 unsigned int major_hash;
4409 /* remove from the list */
4411 prev->next = entry->next;
4413 finalizable_hash [i] = entry->next;
4414 hash_table->num_registered--;
4416 entry->object = copy;
4418 /* insert it into the major hash */
4419 rehash_fin_table_if_necessary (&major_finalizable_hash);
4420 major_hash = mono_object_hash ((MonoObject*) copy) %
4421 major_finalizable_hash.size;
4422 entry->next = major_finalizable_hash.table [major_hash];
4423 major_finalizable_hash.table [major_hash] = entry;
4424 major_finalizable_hash.num_registered++;
4426 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4431 /* update pointer */
4432 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4433 entry->object = copy;
4438 entry = entry->next;
4444 object_is_reachable (char *object, char *start, char *end)
4446 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4447 if (object < start || object >= end)
4449 return !object_is_fin_ready (object) || major_is_object_live (object);
4452 /* LOCKING: requires that the GC lock is held */
4454 null_ephemerons_for_domain (MonoDomain *domain)
4456 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4459 MonoObject *object = (MonoObject*)current->array;
4461 if (object && !object->vtable) {
4462 EphemeronLinkNode *tmp = current;
4465 prev->next = current->next;
4467 ephemeron_list = current->next;
4469 current = current->next;
4470 free_internal_mem (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4473 current = current->next;
4478 /* LOCKING: requires that the GC lock is held */
4480 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end)
4482 int was_in_nursery, was_promoted;
4483 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4485 Ephemeron *cur, *array_end;
4488 char *object = current->array;
4490 if (!object_is_reachable (object, start, end)) {
4491 EphemeronLinkNode *tmp = current;
4493 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4496 prev->next = current->next;
4498 ephemeron_list = current->next;
4500 current = current->next;
4501 free_internal_mem (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4506 was_in_nursery = ptr_in_nursery (object);
4507 copy_func ((void**)&object);
4508 current->array = object;
4510 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4511 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4513 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4515 array = (MonoArray*)object;
4516 cur = mono_array_addr (array, Ephemeron, 0);
4517 array_end = cur + mono_array_length (array);
4519 for (; cur < array_end; ++cur) {
4520 char *key = (char*)cur->key;
4525 DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4526 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4527 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4529 if (!object_is_reachable (key, start, end)) {
4536 if (ptr_in_nursery (key)) {/*key was not promoted*/
4537 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4538 add_to_global_remset (&cur->key);
4540 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4541 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4542 add_to_global_remset (&cur->value);
4547 current = current->next;
4551 /* LOCKING: requires that the GC lock is held */
4553 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end)
4555 int nothing_marked = 1;
4556 EphemeronLinkNode *current = ephemeron_list;
4558 Ephemeron *cur, *array_end;
4560 for (current = ephemeron_list; current; current = current->next) {
4561 char *object = current->array;
4562 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4564 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4565 if (object < start || object >= end)
4568 /*It has to be alive*/
4569 if (!object_is_reachable (object, start, end)) {
4570 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4574 copy_func ((void**)&object);
4576 array = (MonoArray*)object;
4577 cur = mono_array_addr (array, Ephemeron, 0);
4578 array_end = cur + mono_array_length (array);
4580 for (; cur < array_end; ++cur) {
4581 char *key = cur->key;
4586 DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4587 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4588 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4590 if (object_is_reachable (key, start, end)) {
4591 char *value = cur->value;
4593 copy_func ((void**)&cur->key);
4595 if (!object_is_reachable (value, start, end))
4597 copy_func ((void**)&cur->value);
4603 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4604 return nothing_marked;
4607 /* LOCKING: requires that the GC lock is held */
4609 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation)
4611 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4612 DisappearingLink **disappearing_link_hash = hash->table;
4613 int disappearing_link_hash_size = hash->size;
4614 DisappearingLink *entry, *prev;
4616 if (!hash->num_links)
4618 for (i = 0; i < disappearing_link_hash_size; ++i) {
4620 for (entry = disappearing_link_hash [i]; entry;) {
4621 char *object = DISLINK_OBJECT (entry);
4622 if (object >= start && object < end && !major_is_object_live (object)) {
4623 gboolean track = DISLINK_TRACK (entry);
4624 if (!track && object_is_fin_ready (object)) {
4625 void **p = entry->link;
4626 DisappearingLink *old;
4628 /* remove from list */
4630 prev->next = entry->next;
4632 disappearing_link_hash [i] = entry->next;
4633 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4635 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4640 char *copy = object;
4641 copy_func ((void**)©);
4643 /* Update pointer if it's moved. If the object
4644 * has been moved out of the nursery, we need to
4645 * remove the link from the minor hash table to
4648 * FIXME: what if an object is moved earlier?
4651 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4652 void **link = entry->link;
4653 DisappearingLink *old;
4654 /* remove from list */
4656 prev->next = entry->next;
4658 disappearing_link_hash [i] = entry->next;
4660 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4664 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4665 track, GENERATION_OLD);
4667 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4671 /* We set the track resurrection bit to
4672 * FALSE if the object is to be finalized
4673 * so that the object can be collected in
4674 * the next cycle (i.e. after it was
4677 *entry->link = HIDE_POINTER (copy,
4678 object_is_fin_ready (object) ? FALSE : track);
4679 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4684 entry = entry->next;
4689 /* LOCKING: requires that the GC lock is held */
4691 null_links_for_domain (MonoDomain *domain, int generation)
4693 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4694 DisappearingLink **disappearing_link_hash = hash->table;
4695 int disappearing_link_hash_size = hash->size;
4696 DisappearingLink *entry, *prev;
4698 for (i = 0; i < disappearing_link_hash_size; ++i) {
4700 for (entry = disappearing_link_hash [i]; entry; ) {
4701 char *object = DISLINK_OBJECT (entry);
4702 if (object && !((MonoObject*)object)->vtable) {
4703 DisappearingLink *next = entry->next;
4708 disappearing_link_hash [i] = next;
4710 if (*(entry->link)) {
4711 *(entry->link) = NULL;
4712 g_warning ("Disappearing link %p not freed", entry->link);
4714 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4721 entry = entry->next;
4726 /* LOCKING: requires that the GC lock is held */
4728 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4729 FinalizeEntryHashTable *hash_table)
4731 FinalizeEntry **finalizable_hash = hash_table->table;
4732 mword finalizable_hash_size = hash_table->size;
4733 FinalizeEntry *entry, *prev;
4736 if (no_finalize || !out_size || !out_array)
4739 for (i = 0; i < finalizable_hash_size; ++i) {
4741 for (entry = finalizable_hash [i]; entry;) {
4742 if (mono_object_domain (entry->object) == domain) {
4743 FinalizeEntry *next;
4744 /* remove and put in out_array */
4746 prev->next = entry->next;
4748 finalizable_hash [i] = entry->next;
4750 hash_table->num_registered--;
4751 out_array [count ++] = entry->object;
4752 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));
4754 if (count == out_size)
4759 entry = entry->next;
4766 * mono_gc_finalizers_for_domain:
4767 * @domain: the unloading appdomain
4768 * @out_array: output array
4769 * @out_size: size of output array
4771 * Store inside @out_array up to @out_size objects that belong to the unloading
4772 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4773 * until it returns 0.
4774 * The items are removed from the finalizer data structure, so the caller is supposed
4776 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4779 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4784 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4785 if (result < out_size) {
4786 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4787 &major_finalizable_hash);
4795 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4797 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4798 FinalizeEntry **finalizable_hash;
4799 mword finalizable_hash_size;
4800 FinalizeEntry *entry, *prev;
4804 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4805 hash = mono_object_hash (obj);
4807 rehash_fin_table_if_necessary (hash_table);
4808 finalizable_hash = hash_table->table;
4809 finalizable_hash_size = hash_table->size;
4810 hash %= finalizable_hash_size;
4812 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4813 if (entry->object == obj) {
4815 /* remove from the list */
4817 prev->next = entry->next;
4819 finalizable_hash [hash] = entry->next;
4820 hash_table->num_registered--;
4821 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));
4822 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4830 /* request to deregister, but already out of the list */
4834 entry = get_internal_mem (sizeof (FinalizeEntry), INTERNAL_MEM_FINALIZE_ENTRY);
4835 entry->object = obj;
4836 entry->next = finalizable_hash [hash];
4837 finalizable_hash [hash] = entry;
4838 hash_table->num_registered++;
4839 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)));
4844 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4846 if (ptr_in_nursery (obj))
4847 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4849 register_for_finalization (obj, user_data, GENERATION_OLD);
4853 rehash_dislink (DisappearingLinkHashTable *hash_table)
4855 DisappearingLink **disappearing_link_hash = hash_table->table;
4856 int disappearing_link_hash_size = hash_table->size;
4859 DisappearingLink **new_hash;
4860 DisappearingLink *entry, *next;
4861 int new_size = g_spaced_primes_closest (hash_table->num_links);
4863 new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4864 for (i = 0; i < disappearing_link_hash_size; ++i) {
4865 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4866 hash = mono_aligned_addr_hash (entry->link) % new_size;
4868 entry->next = new_hash [hash];
4869 new_hash [hash] = entry;
4872 free_internal_mem (disappearing_link_hash, INTERNAL_MEM_DISLINK_TABLE);
4873 hash_table->table = new_hash;
4874 hash_table->size = new_size;
4877 /* LOCKING: assumes the GC lock is held */
4879 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4881 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4882 DisappearingLink *entry, *prev;
4884 DisappearingLink **disappearing_link_hash = hash_table->table;
4885 int disappearing_link_hash_size = hash_table->size;
4887 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4888 rehash_dislink (hash_table);
4889 disappearing_link_hash = hash_table->table;
4890 disappearing_link_hash_size = hash_table->size;
4892 /* FIXME: add check that link is not in the heap */
4893 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4894 entry = disappearing_link_hash [hash];
4896 for (; entry; entry = entry->next) {
4897 /* link already added */
4898 if (link == entry->link) {
4899 /* NULL obj means remove */
4902 prev->next = entry->next;
4904 disappearing_link_hash [hash] = entry->next;
4905 hash_table->num_links--;
4906 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4907 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
4910 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4918 entry = get_internal_mem (sizeof (DisappearingLink), INTERNAL_MEM_DISLINK);
4919 *link = HIDE_POINTER (obj, track);
4921 entry->next = disappearing_link_hash [hash];
4922 disappearing_link_hash [hash] = entry;
4923 hash_table->num_links++;
4924 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)));
4927 /* LOCKING: assumes the GC lock is held */
4929 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4931 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4932 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4934 if (ptr_in_nursery (obj))
4935 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4937 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4942 mono_gc_invoke_finalizers (void)
4944 FinalizeEntry *entry = NULL;
4945 gboolean entry_is_critical = FALSE;
4948 /* FIXME: batch to reduce lock contention */
4949 while (fin_ready_list || critical_fin_list) {
4953 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4955 /* We have finalized entry in the last
4956 interation, now we need to remove it from
4959 *list = entry->next;
4961 FinalizeEntry *e = *list;
4962 while (e->next != entry)
4964 e->next = entry->next;
4966 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4970 /* Now look for the first non-null entry. */
4971 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4974 entry_is_critical = FALSE;
4976 entry_is_critical = TRUE;
4977 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4982 g_assert (entry->object);
4983 num_ready_finalizers--;
4984 obj = entry->object;
4985 entry->object = NULL;
4986 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4994 g_assert (entry->object == NULL);
4996 /* the object is on the stack so it is pinned */
4997 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4998 mono_gc_run_finalize (obj, NULL);
5005 mono_gc_pending_finalizers (void)
5007 return fin_ready_list || critical_fin_list;
5010 /* Negative value to remove */
5012 mono_gc_add_memory_pressure (gint64 value)
5014 /* FIXME: Use interlocked functions */
5016 memory_pressure += value;
5021 * ######################################################################
5022 * ######## registered roots support
5023 * ######################################################################
5027 rehash_roots (gboolean pinned)
5031 RootRecord **new_hash;
5032 RootRecord *entry, *next;
5035 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
5036 new_hash = get_internal_mem (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5037 for (i = 0; i < roots_hash_size [pinned]; ++i) {
5038 for (entry = roots_hash [pinned][i]; entry; entry = next) {
5039 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5041 entry->next = new_hash [hash];
5042 new_hash [hash] = entry;
5045 free_internal_mem (roots_hash [pinned], INTERNAL_MEM_ROOTS_TABLE);
5046 roots_hash [pinned] = new_hash;
5047 roots_hash_size [pinned] = new_size;
5051 find_root (int root_type, char *start, guint32 addr_hash)
5053 RootRecord *new_root;
5055 guint32 hash = addr_hash % roots_hash_size [root_type];
5056 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5057 /* we allow changing the size and the descriptor (for thread statics etc) */
5058 if (new_root->start_root == start) {
5067 * We do not coalesce roots.
5070 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5072 RootRecord *new_root;
5073 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5076 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5077 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5080 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5081 new_root = find_root (i, start, addr_hash);
5082 /* we allow changing the size and the descriptor (for thread statics etc) */
5084 size_t old_size = new_root->end_root - new_root->start_root;
5085 new_root->end_root = new_root->start_root + size;
5086 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5087 ((new_root->root_desc == 0) && (descr == NULL)));
5088 new_root->root_desc = (mword)descr;
5090 roots_size -= old_size;
5095 new_root = get_internal_mem (sizeof (RootRecord), INTERNAL_MEM_ROOT_RECORD);
5097 new_root->start_root = start;
5098 new_root->end_root = new_root->start_root + size;
5099 new_root->root_desc = (mword)descr;
5101 hash = addr_hash % roots_hash_size [root_type];
5102 num_roots_entries [root_type]++;
5103 new_root->next = roots_hash [root_type] [hash];
5104 roots_hash [root_type][hash] = new_root;
5105 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));
5115 mono_gc_register_root (char *start, size_t size, void *descr)
5117 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5121 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5123 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5127 mono_gc_deregister_root (char* addr)
5129 RootRecord *tmp, *prev;
5130 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5134 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5135 hash = addr_hash % roots_hash_size [root_type];
5136 tmp = roots_hash [root_type][hash];
5139 if (tmp->start_root == (char*)addr) {
5141 prev->next = tmp->next;
5143 roots_hash [root_type][hash] = tmp->next;
5144 roots_size -= (tmp->end_root - tmp->start_root);
5145 num_roots_entries [root_type]--;
5146 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5147 free_internal_mem (tmp, INTERNAL_MEM_ROOT_RECORD);
5158 * ######################################################################
5159 * ######## Thread handling (stop/start code)
5160 * ######################################################################
5163 /* FIXME: handle large/small config */
5164 #define THREAD_HASH_SIZE 11
5165 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5167 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5169 #if USE_SIGNAL_BASED_START_STOP_WORLD
5171 static MonoSemType suspend_ack_semaphore;
5172 static MonoSemType *suspend_ack_semaphore_ptr;
5173 static unsigned int global_stop_count = 0;
5175 static int suspend_signal_num = SIGXFSZ;
5177 static int suspend_signal_num = SIGPWR;
5179 static int restart_signal_num = SIGXCPU;
5180 static sigset_t suspend_signal_mask;
5181 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5183 /* LOCKING: assumes the GC lock is held */
5184 static SgenThreadInfo*
5185 thread_info_lookup (ARCH_THREAD_TYPE id)
5187 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5188 SgenThreadInfo *info;
5190 info = thread_table [hash];
5191 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5198 update_current_thread_stack (void *start)
5200 void *ptr = cur_thread_regs;
5201 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5203 info->stack_start = align_pointer (&ptr);
5204 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5205 ARCH_STORE_REGS (ptr);
5206 info->stopped_regs = ptr;
5207 if (gc_callbacks.thread_suspend_func)
5208 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5212 signal_desc (int signum)
5214 if (signum == suspend_signal_num)
5216 if (signum == restart_signal_num)
5222 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5223 * have cross-domain checks in the write barrier.
5225 //#define XDOMAIN_CHECKS_IN_WBARRIER
5227 #ifndef BINARY_PROTOCOL
5228 #ifndef HEAVY_STATISTICS
5229 #define MANAGED_ALLOCATION
5230 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5231 #define MANAGED_WBARRIER
5237 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5240 wait_for_suspend_ack (int count)
5244 for (i = 0; i < count; ++i) {
5245 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5246 if (errno != EINTR) {
5247 g_error ("sem_wait ()");
5253 /* LOCKING: assumes the GC lock is held */
5255 thread_handshake (int signum)
5257 int count, i, result;
5258 SgenThreadInfo *info;
5259 pthread_t me = pthread_self ();
5262 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5263 for (info = thread_table [i]; info; info = info->next) {
5264 DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
5265 if (ARCH_THREAD_EQUALS (info->id, me)) {
5266 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
5269 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
5271 result = pthread_kill (info->id, signum);
5273 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
5276 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
5282 wait_for_suspend_ack (count);
5288 restart_threads_until_none_in_managed_allocator (void)
5290 SgenThreadInfo *info;
5291 int i, result, num_threads_died = 0;
5292 int sleep_duration = -1;
5295 int restart_count = 0, restarted_count = 0;
5296 /* restart all threads that stopped in the
5298 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5299 for (info = thread_table [i]; info; info = info->next) {
5302 if (!info->stack_start || info->in_critical_region ||
5303 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5304 binary_protocol_thread_restart ((gpointer)info->id);
5305 result = pthread_kill (info->id, restart_signal_num);
5312 /* we set the stopped_ip to
5313 NULL for threads which
5314 we're not restarting so
5315 that we can easily identify
5317 info->stopped_ip = NULL;
5318 info->stopped_domain = NULL;
5322 /* if no threads were restarted, we're done */
5323 if (restart_count == 0)
5326 /* wait for the threads to signal their restart */
5327 wait_for_suspend_ack (restart_count);
5329 if (sleep_duration < 0) {
5333 g_usleep (sleep_duration);
5334 sleep_duration += 10;
5337 /* stop them again */
5338 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5339 for (info = thread_table [i]; info; info = info->next) {
5340 if (info->skip || info->stopped_ip == NULL)
5342 result = pthread_kill (info->id, suspend_signal_num);
5350 /* some threads might have died */
5351 num_threads_died += restart_count - restarted_count;
5352 /* wait for the threads to signal their suspension
5354 wait_for_suspend_ack (restart_count);
5357 return num_threads_died;
5360 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5362 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5364 SgenThreadInfo *info;
5367 int old_errno = errno;
5368 gpointer regs [ARCH_NUM_REGS];
5369 gpointer stack_start;
5371 id = pthread_self ();
5372 info = thread_info_lookup (id);
5373 info->stopped_domain = mono_domain_get ();
5374 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5375 stop_count = global_stop_count;
5376 /* duplicate signal */
5377 if (0 && info->stop_count == stop_count) {
5381 #ifdef HAVE_KW_THREAD
5382 /* update the remset info in the thread data structure */
5383 info->remset = remembered_set;
5385 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5386 /* If stack_start is not within the limits, then don't set it
5387 in info and we will be restarted. */
5388 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5389 info->stack_start = stack_start;
5391 ARCH_COPY_SIGCTX_REGS (regs, context);
5392 info->stopped_regs = regs;
5394 g_assert (!info->stack_start);
5397 /* Notify the JIT */
5398 if (gc_callbacks.thread_suspend_func)
5399 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5401 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5402 /* notify the waiting thread */
5403 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5404 info->stop_count = stop_count;
5406 /* wait until we receive the restart signal */
5409 sigsuspend (&suspend_signal_mask);
5410 } while (info->signal != restart_signal_num);
5412 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5413 /* notify the waiting thread */
5414 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5420 restart_handler (int sig)
5422 SgenThreadInfo *info;
5423 int old_errno = errno;
5425 info = thread_info_lookup (pthread_self ());
5426 info->signal = restart_signal_num;
5427 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5433 acquire_gc_locks (void)
5439 release_gc_locks (void)
5441 UNLOCK_INTERRUPTION;
5444 static TV_DECLARE (stop_world_time);
5445 static unsigned long max_pause_usec = 0;
5447 /* LOCKING: assumes the GC lock is held */
5453 acquire_gc_locks ();
5455 update_current_thread_stack (&count);
5457 global_stop_count++;
5458 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 ()));
5459 TV_GETTIME (stop_world_time);
5460 count = thread_handshake (suspend_signal_num);
5461 count -= restart_threads_until_none_in_managed_allocator ();
5462 g_assert (count >= 0);
5463 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5467 /* LOCKING: assumes the GC lock is held */
5469 restart_world (void)
5472 SgenThreadInfo *info;
5473 TV_DECLARE (end_sw);
5476 /* notify the profiler of the leftovers */
5477 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5478 if (moved_objects_idx) {
5479 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5480 moved_objects_idx = 0;
5483 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5484 for (info = thread_table [i]; info; info = info->next) {
5485 info->stack_start = NULL;
5486 info->stopped_regs = NULL;
5490 release_gc_locks ();
5492 count = thread_handshake (restart_signal_num);
5493 TV_GETTIME (end_sw);
5494 usec = TV_ELAPSED (stop_world_time, end_sw);
5495 max_pause_usec = MAX (usec, max_pause_usec);
5496 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5500 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5503 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5505 gc_callbacks = *callbacks;
5508 /* Variables holding start/end nursery so it won't have to be passed at every call */
5509 static void *scan_area_arg_start, *scan_area_arg_end;
5512 mono_gc_conservatively_scan_area (void *start, void *end)
5514 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5518 mono_gc_scan_object (void *obj)
5520 if (current_collection_generation == GENERATION_NURSERY)
5523 major_copy_or_mark_object (&obj);
5528 * Mark from thread stacks and registers.
5531 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5534 SgenThreadInfo *info;
5536 scan_area_arg_start = start_nursery;
5537 scan_area_arg_end = end_nursery;
5539 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5540 for (info = thread_table [i]; info; info = info->next) {
5542 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));
5545 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));
5546 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
5547 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5549 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5552 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5553 start_nursery, end_nursery, PIN_TYPE_STACK);
5559 find_pinning_ref_from_thread (char *obj, size_t size)
5562 SgenThreadInfo *info;
5563 char *endobj = obj + size;
5565 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5566 for (info = thread_table [i]; info; info = info->next) {
5567 char **start = (char**)info->stack_start;
5570 while (start < (char**)info->stack_end) {
5571 if (*start >= obj && *start < endobj) {
5572 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));
5577 /* FIXME: check info->stopped_regs */
5583 ptr_on_stack (void *ptr)
5585 gpointer stack_start = &stack_start;
5586 SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5588 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5594 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
5601 HEAVY_STAT (++stat_global_remsets_processed);
5603 /* FIXME: exclude stack locations */
5604 switch ((*p) & REMSET_TYPE_MASK) {
5605 case REMSET_LOCATION:
5607 //__builtin_prefetch (ptr);
5608 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5609 gpointer old = *ptr;
5611 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5613 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5614 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5616 * If the object is pinned, each reference to it from nonpinned objects
5617 * becomes part of the global remset, which can grow very large.
5619 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5620 add_to_global_remset (ptr);
5623 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5627 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5628 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5631 while (count-- > 0) {
5633 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5634 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5635 add_to_global_remset (ptr);
5640 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5641 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5643 scan_object ((char*)ptr);
5645 case REMSET_VTYPE: {
5646 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5647 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5652 ptr = (void**) scan_vtype ((char*)ptr, desc, start_nursery, end_nursery);
5656 g_assert_not_reached ();
5661 #ifdef HEAVY_STATISTICS
5663 collect_store_remsets (RememberedSet *remset, mword *bumper)
5665 mword *p = remset->data;
5670 while (p < remset->store_next) {
5671 switch ((*p) & REMSET_TYPE_MASK) {
5672 case REMSET_LOCATION:
5675 ++stat_saved_remsets_1;
5677 if (*p == last1 || *p == last2) {
5678 ++stat_saved_remsets_2;
5695 g_assert_not_reached ();
5705 RememberedSet *remset;
5707 SgenThreadInfo *info;
5709 mword *addresses, *bumper, *p, *r;
5711 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5712 for (info = thread_table [i]; info; info = info->next) {
5713 for (remset = info->remset; remset; remset = remset->next)
5714 size += remset->store_next - remset->data;
5717 for (remset = freed_thread_remsets; remset; remset = remset->next)
5718 size += remset->store_next - remset->data;
5719 for (remset = global_remset; remset; remset = remset->next)
5720 size += remset->store_next - remset->data;
5722 bumper = addresses = get_internal_mem (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5724 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5725 for (info = thread_table [i]; info; info = info->next) {
5726 for (remset = info->remset; remset; remset = remset->next)
5727 bumper = collect_store_remsets (remset, bumper);
5730 for (remset = global_remset; remset; remset = remset->next)
5731 bumper = collect_store_remsets (remset, bumper);
5732 for (remset = freed_thread_remsets; remset; remset = remset->next)
5733 bumper = collect_store_remsets (remset, bumper);
5735 g_assert (bumper <= addresses + size);
5737 stat_store_remsets += bumper - addresses;
5739 sort_addresses ((void**)addresses, bumper - addresses);
5742 while (r < bumper) {
5748 stat_store_remsets_unique += p - addresses;
5750 free_internal_mem (addresses, INTERNAL_MEM_STATISTICS);
5755 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5757 *info->store_remset_buffer_index_addr = 0;
5758 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5762 scan_from_remsets (void *start_nursery, void *end_nursery)
5765 SgenThreadInfo *info;
5766 RememberedSet *remset;
5767 GenericStoreRememberedSet *store_remset;
5768 mword *p, *next_p, *store_pos;
5770 #ifdef HEAVY_STATISTICS
5774 /* the global one */
5775 for (remset = global_remset; remset; remset = remset->next) {
5776 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));
5777 store_pos = remset->data;
5778 for (p = remset->data; p < remset->store_next; p = next_p) {
5781 next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
5784 * Clear global remsets of locations which no longer point to the
5785 * nursery. Otherwise, they could grow indefinitely between major
5788 ptr = (p [0] & ~REMSET_TYPE_MASK);
5789 if ((p [0] & REMSET_TYPE_MASK) == REMSET_LOCATION) {
5790 if (ptr_in_nursery (*(void**)ptr)) {
5791 *store_pos ++ = p [0];
5792 HEAVY_STAT (++stat_global_remsets_readded);
5795 g_assert_not_reached ();
5799 /* Truncate the remset */
5800 remset->store_next = store_pos;
5803 /* the generic store ones */
5804 store_remset = generic_store_remsets;
5805 while (store_remset) {
5806 GenericStoreRememberedSet *next = store_remset->next;
5808 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5809 gpointer addr = store_remset->data [i];
5811 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE);
5814 free_internal_mem (store_remset, INTERNAL_MEM_STORE_REMSET);
5816 store_remset = next;
5818 generic_store_remsets = NULL;
5820 /* the per-thread ones */
5821 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5822 for (info = thread_table [i]; info; info = info->next) {
5823 RememberedSet *next;
5825 for (remset = info->remset; remset; remset = next) {
5826 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));
5827 for (p = remset->data; p < remset->store_next;) {
5828 p = handle_remset (p, start_nursery, end_nursery, FALSE);
5830 remset->store_next = remset->data;
5831 next = remset->next;
5832 remset->next = NULL;
5833 if (remset != info->remset) {
5834 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5835 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5838 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5839 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE);
5840 clear_thread_store_remset_buffer (info);
5844 /* the freed thread ones */
5845 while (freed_thread_remsets) {
5846 RememberedSet *next;
5847 remset = freed_thread_remsets;
5848 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));
5849 for (p = remset->data; p < remset->store_next;) {
5850 p = handle_remset (p, start_nursery, end_nursery, FALSE);
5852 next = remset->next;
5853 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5854 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5855 freed_thread_remsets = next;
5860 * Clear the info in the remembered sets: we're doing a major collection, so
5861 * the per-thread ones are not needed and the global ones will be reconstructed
5865 clear_remsets (void)
5868 SgenThreadInfo *info;
5869 RememberedSet *remset, *next;
5871 /* the global list */
5872 for (remset = global_remset; remset; remset = next) {
5873 remset->store_next = remset->data;
5874 next = remset->next;
5875 remset->next = NULL;
5876 if (remset != global_remset) {
5877 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5878 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5881 /* the generic store ones */
5882 while (generic_store_remsets) {
5883 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5884 free_internal_mem (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5885 generic_store_remsets = gs_next;
5887 /* the per-thread ones */
5888 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5889 for (info = thread_table [i]; info; info = info->next) {
5890 for (remset = info->remset; remset; remset = next) {
5891 remset->store_next = remset->data;
5892 next = remset->next;
5893 remset->next = NULL;
5894 if (remset != info->remset) {
5895 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5896 free_internal_mem (remset, INTERNAL_MEM_REMSET);
5899 clear_thread_store_remset_buffer (info);
5903 /* the freed thread ones */
5904 while (freed_thread_remsets) {
5905 next = freed_thread_remsets->next;
5906 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5907 free_internal_mem (freed_thread_remsets, INTERNAL_MEM_REMSET);
5908 freed_thread_remsets = next;
5913 * Clear the thread local TLAB variables for all threads.
5918 SgenThreadInfo *info;
5921 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5922 for (info = thread_table [i]; info; info = info->next) {
5923 /* A new TLAB will be allocated when the thread does its first allocation */
5924 *info->tlab_start_addr = NULL;
5925 *info->tlab_next_addr = NULL;
5926 *info->tlab_temp_end_addr = NULL;
5927 *info->tlab_real_end_addr = NULL;
5932 /* LOCKING: assumes the GC lock is held */
5933 static SgenThreadInfo*
5934 gc_register_current_thread (void *addr)
5937 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5938 #ifndef HAVE_KW_THREAD
5939 SgenThreadInfo *__thread_info__ = info;
5945 memset (info, 0, sizeof (SgenThreadInfo));
5946 #ifndef HAVE_KW_THREAD
5947 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5949 g_assert (!pthread_getspecific (thread_info_key));
5950 pthread_setspecific (thread_info_key, info);
5955 info->id = ARCH_GET_THREAD ();
5956 info->stop_count = -1;
5959 info->stack_start = NULL;
5960 info->tlab_start_addr = &TLAB_START;
5961 info->tlab_next_addr = &TLAB_NEXT;
5962 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5963 info->tlab_real_end_addr = &TLAB_REAL_END;
5964 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5965 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5966 info->stopped_ip = NULL;
5967 info->stopped_domain = NULL;
5968 info->stopped_regs = NULL;
5970 binary_protocol_thread_register ((gpointer)info->id);
5972 #ifdef HAVE_KW_THREAD
5973 tlab_next_addr = &tlab_next;
5974 store_remset_buffer_index_addr = &store_remset_buffer_index;
5977 /* try to get it with attributes first */
5978 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5982 pthread_attr_t attr;
5983 pthread_getattr_np (pthread_self (), &attr);
5984 pthread_attr_getstack (&attr, &sstart, &size);
5985 info->stack_start_limit = sstart;
5986 info->stack_end = (char*)sstart + size;
5987 pthread_attr_destroy (&attr);
5989 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5990 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5991 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5994 /* FIXME: we assume the stack grows down */
5995 gsize stack_bottom = (gsize)addr;
5996 stack_bottom += 4095;
5997 stack_bottom &= ~4095;
5998 info->stack_end = (char*)stack_bottom;
6002 #ifdef HAVE_KW_THREAD
6003 stack_end = info->stack_end;
6006 /* hash into the table */
6007 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
6008 info->next = thread_table [hash];
6009 thread_table [hash] = info;
6011 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
6012 pthread_setspecific (remembered_set_key, info->remset);
6013 #ifdef HAVE_KW_THREAD
6014 remembered_set = info->remset;
6017 STORE_REMSET_BUFFER = get_internal_mem (sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE, INTERNAL_MEM_STORE_REMSET);
6018 STORE_REMSET_BUFFER_INDEX = 0;
6020 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
6022 if (gc_callbacks.thread_attach_func)
6023 info->runtime_data = gc_callbacks.thread_attach_func ();
6029 add_generic_store_remset_from_buffer (gpointer *buffer)
6031 GenericStoreRememberedSet *remset = get_internal_mem (sizeof (GenericStoreRememberedSet), INTERNAL_MEM_STORE_REMSET);
6032 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6033 remset->next = generic_store_remsets;
6034 generic_store_remsets = remset;
6038 unregister_current_thread (void)
6041 SgenThreadInfo *prev = NULL;
6043 RememberedSet *rset;
6044 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6046 binary_protocol_thread_unregister ((gpointer)id);
6048 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6049 p = thread_table [hash];
6051 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6052 while (!ARCH_THREAD_EQUALS (p->id, id)) {
6057 thread_table [hash] = p->next;
6059 prev->next = p->next;
6062 if (freed_thread_remsets) {
6063 for (rset = p->remset; rset->next; rset = rset->next)
6065 rset->next = freed_thread_remsets;
6066 freed_thread_remsets = p->remset;
6068 freed_thread_remsets = p->remset;
6071 if (*p->store_remset_buffer_index_addr)
6072 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6073 free_internal_mem (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6078 unregister_thread (void *k)
6080 g_assert (!mono_domain_get ());
6082 unregister_current_thread ();
6087 mono_gc_register_thread (void *baseptr)
6089 SgenThreadInfo *info;
6093 info = thread_info_lookup (ARCH_GET_THREAD ());
6095 info = gc_register_current_thread (baseptr);
6097 return info != NULL;
6100 #if USE_PTHREAD_INTERCEPT
6102 #undef pthread_create
6104 #undef pthread_detach
6107 void *(*start_routine) (void *);
6110 MonoSemType registered;
6111 } SgenThreadStartInfo;
6114 gc_start_thread (void *arg)
6116 SgenThreadStartInfo *start_info = arg;
6117 SgenThreadInfo* info;
6118 void *t_arg = start_info->arg;
6119 void *(*start_func) (void*) = start_info->start_routine;
6124 info = gc_register_current_thread (&result);
6126 post_result = MONO_SEM_POST (&(start_info->registered));
6127 g_assert (!post_result);
6128 result = start_func (t_arg);
6129 g_assert (!mono_domain_get ());
6131 * this is done by the pthread key dtor
6133 unregister_current_thread ();
6141 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6143 SgenThreadStartInfo *start_info;
6146 start_info = malloc (sizeof (SgenThreadStartInfo));
6149 result = MONO_SEM_INIT (&(start_info->registered), 0);
6151 start_info->arg = arg;
6152 start_info->start_routine = start_routine;
6154 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6156 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6157 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6160 MONO_SEM_DESTROY (&(start_info->registered));
6166 mono_gc_pthread_join (pthread_t thread, void **retval)
6168 return pthread_join (thread, retval);
6172 mono_gc_pthread_detach (pthread_t thread)
6174 return pthread_detach (thread);
6177 #endif /* USE_PTHREAD_INTERCEPT */
6180 * ######################################################################
6181 * ######## Write barriers
6182 * ######################################################################
6185 static RememberedSet*
6186 alloc_remset (int size, gpointer id) {
6187 RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6188 res->store_next = res->data;
6189 res->end_set = res->data + size;
6191 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6196 * Note: the write barriers first do the needed GC work and then do the actual store:
6197 * this way the value is visible to the conservative GC scan after the write barrier
6198 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6199 * the conservative scan, otherwise by the remembered set scan.
6202 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6206 HEAVY_STAT (++stat_wbarrier_set_field);
6207 if (ptr_in_nursery (field_ptr)) {
6208 *(void**)field_ptr = value;
6211 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6213 rs = REMEMBERED_SET;
6214 if (rs->store_next < rs->end_set) {
6215 *(rs->store_next++) = (mword)field_ptr;
6216 *(void**)field_ptr = value;
6220 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6221 rs->next = REMEMBERED_SET;
6222 REMEMBERED_SET = rs;
6223 #ifdef HAVE_KW_THREAD
6224 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6226 *(rs->store_next++) = (mword)field_ptr;
6227 *(void**)field_ptr = value;
6232 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6236 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6237 if (ptr_in_nursery (slot_ptr)) {
6238 *(void**)slot_ptr = value;
6241 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6243 rs = REMEMBERED_SET;
6244 if (rs->store_next < rs->end_set) {
6245 *(rs->store_next++) = (mword)slot_ptr;
6246 *(void**)slot_ptr = value;
6250 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6251 rs->next = REMEMBERED_SET;
6252 REMEMBERED_SET = rs;
6253 #ifdef HAVE_KW_THREAD
6254 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6256 *(rs->store_next++) = (mword)slot_ptr;
6257 *(void**)slot_ptr = value;
6262 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6266 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6268 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6269 if (ptr_in_nursery (dest_ptr)) {
6273 rs = REMEMBERED_SET;
6274 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6275 if (rs->store_next + 1 < rs->end_set) {
6276 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6277 *(rs->store_next++) = count;
6281 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6282 rs->next = REMEMBERED_SET;
6283 REMEMBERED_SET = rs;
6284 #ifdef HAVE_KW_THREAD
6285 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6287 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6288 *(rs->store_next++) = count;
6293 find_object_for_ptr_in_area (char *ptr, char *start, char *end)
6295 while (start < end) {
6298 if (!*(void**)start) {
6299 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
6305 #define SCAN_OBJECT_NOSCAN
6306 #include "sgen-scan-object.h"
6308 if (ptr >= old_start && ptr < start)
6315 static char *found_obj;
6318 find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
6320 if (ptr >= obj && ptr < obj + size) {
6321 g_assert (!found_obj);
6326 /* for use in the debugger */
6327 char* find_object_for_ptr (char *ptr);
6329 find_object_for_ptr (char *ptr)
6333 if (ptr >= nursery_section->data && ptr < nursery_section->end_data)
6334 return find_object_for_ptr_in_area (ptr, nursery_section->data, nursery_section->end_data);
6336 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
6337 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
6338 return bigobj->data;
6342 * Very inefficient, but this is debugging code, supposed to
6343 * be called from gdb, so we don't care.
6346 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
6351 evacuate_remset_buffer (void)
6356 buffer = STORE_REMSET_BUFFER;
6358 add_generic_store_remset_from_buffer (buffer);
6359 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6361 STORE_REMSET_BUFFER_INDEX = 0;
6365 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6371 HEAVY_STAT (++stat_wbarrier_generic_store);
6373 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6374 /* FIXME: ptr_in_heap must be called with the GC lock held */
6375 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6376 char *start = find_object_for_ptr (ptr);
6377 MonoObject *value = *(MonoObject**)ptr;
6381 MonoObject *obj = (MonoObject*)start;
6382 if (obj->vtable->domain != value->vtable->domain)
6383 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6391 if (*(gpointer*)ptr)
6392 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6394 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6395 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6400 buffer = STORE_REMSET_BUFFER;
6401 index = STORE_REMSET_BUFFER_INDEX;
6402 /* This simple optimization eliminates a sizable portion of
6403 entries. Comparing it to the last but one entry as well
6404 doesn't eliminate significantly more entries. */
6405 if (buffer [index] == ptr) {
6410 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6411 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6414 if (index >= STORE_REMSET_BUFFER_SIZE) {
6415 evacuate_remset_buffer ();
6416 index = STORE_REMSET_BUFFER_INDEX;
6417 g_assert (index == 0);
6420 buffer [index] = ptr;
6421 STORE_REMSET_BUFFER_INDEX = index;
6427 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6429 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6430 *(void**)ptr = value;
6431 if (ptr_in_nursery (value))
6432 mono_gc_wbarrier_generic_nostore (ptr);
6436 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6440 HEAVY_STAT (++stat_wbarrier_value_copy);
6441 g_assert (klass->valuetype);
6443 memmove (dest, src, count * mono_class_value_size (klass, NULL));
6444 rs = REMEMBERED_SET;
6445 if (ptr_in_nursery (dest) || ptr_on_stack (dest)) {
6449 g_assert (klass->gc_descr_inited);
6450 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));
6452 if (rs->store_next + 3 < rs->end_set) {
6453 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6454 *(rs->store_next++) = (mword)klass->gc_descr;
6455 *(rs->store_next++) = (mword)count;
6459 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6460 rs->next = REMEMBERED_SET;
6461 REMEMBERED_SET = rs;
6462 #ifdef HAVE_KW_THREAD
6463 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6465 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6466 *(rs->store_next++) = (mword)klass->gc_descr;
6467 *(rs->store_next++) = (mword)count;
6472 * mono_gc_wbarrier_object_copy:
6474 * Write barrier to call when obj is the result of a clone or copy of an object.
6477 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6483 HEAVY_STAT (++stat_wbarrier_object_copy);
6484 rs = REMEMBERED_SET;
6485 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6486 size = mono_object_class (obj)->instance_size;
6488 /* do not copy the sync state */
6489 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6490 size - sizeof (MonoObject));
6491 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6495 if (rs->store_next < rs->end_set) {
6496 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6500 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6501 rs->next = REMEMBERED_SET;
6502 REMEMBERED_SET = rs;
6503 #ifdef HAVE_KW_THREAD
6504 thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6506 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6511 * ######################################################################
6512 * ######## Collector debugging
6513 * ######################################################################
6516 const char*descriptor_types [] = {
6528 describe_ptr (char *ptr)
6534 if (ptr_in_nursery (ptr)) {
6535 printf ("Pointer inside nursery.\n");
6537 if (major_ptr_is_in_non_pinned_space (ptr)) {
6538 printf ("Pointer inside oldspace.\n");
6539 } else if (obj_is_from_pinned_alloc (ptr)) {
6540 printf ("Pointer is inside a pinned chunk.\n");
6542 printf ("Pointer unknown.\n");
6547 if (object_is_pinned (ptr))
6548 printf ("Object is pinned.\n");
6550 if (object_is_forwarded (ptr))
6551 printf ("Object is forwared.\n");
6553 // FIXME: Handle pointers to the inside of objects
6554 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6556 printf ("VTable: %p\n", vtable);
6557 if (vtable == NULL) {
6558 printf ("VTable is invalid (empty).\n");
6561 if (ptr_in_nursery (vtable)) {
6562 printf ("VTable is invalid (points inside nursery).\n");
6565 printf ("Class: %s\n", vtable->klass->name);
6567 desc = ((GCVTable*)vtable)->desc;
6568 printf ("Descriptor: %lx\n", (long)desc);
6571 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6575 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6581 switch ((*p) & REMSET_TYPE_MASK) {
6582 case REMSET_LOCATION:
6583 if (*p == (mword)addr)
6587 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6589 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6593 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6594 count = safe_object_get_size ((MonoObject*)ptr);
6595 count += (ALLOC_ALIGN - 1);
6596 count &= (ALLOC_ALIGN - 1);
6597 count /= sizeof (mword);
6598 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6602 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6606 switch (desc & 0x7) {
6607 case DESC_TYPE_RUN_LENGTH:
6608 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6610 case DESC_TYPE_SMALL_BITMAP:
6611 OBJ_BITMAP_SIZE (skip_size, desc, start);
6615 g_assert_not_reached ();
6618 /* The descriptor includes the size of MonoObject */
6619 skip_size -= sizeof (MonoObject);
6621 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6626 g_assert_not_reached ();
6632 * Return whenever ADDR occurs in the remembered sets
6635 find_in_remsets (char *addr)
6638 SgenThreadInfo *info;
6639 RememberedSet *remset;
6640 GenericStoreRememberedSet *store_remset;
6642 gboolean found = FALSE;
6644 /* the global one */
6645 for (remset = global_remset; remset; remset = remset->next) {
6646 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));
6647 for (p = remset->data; p < remset->store_next;) {
6648 p = find_in_remset_loc (p, addr, &found);
6654 /* the generic store ones */
6655 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6656 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6657 if (store_remset->data [i] == addr)
6662 /* the per-thread ones */
6663 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6664 for (info = thread_table [i]; info; info = info->next) {
6666 for (remset = info->remset; remset; remset = remset->next) {
6667 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));
6668 for (p = remset->data; p < remset->store_next;) {
6669 p = find_in_remset_loc (p, addr, &found);
6674 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6675 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6681 /* the freed thread ones */
6682 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6683 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));
6684 for (p = remset->data; p < remset->store_next;) {
6685 p = find_in_remset_loc (p, addr, &found);
6694 static gboolean missing_remsets;
6697 * We let a missing remset slide if the target object is pinned,
6698 * because the store might have happened but the remset not yet added,
6699 * but in that case the target must be pinned. We might theoretically
6700 * miss some missing remsets this way, but it's very unlikely.
6703 #define HANDLE_PTR(ptr,obj) do { \
6704 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6705 if (!find_in_remsets ((char*)(ptr))) { \
6706 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); \
6707 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6708 if (!object_is_pinned (*(ptr))) \
6709 missing_remsets = TRUE; \
6715 * Check that each object reference which points into the nursery can
6716 * be found in the remembered sets.
6719 check_consistency_callback (char *start, size_t size, void *dummy)
6721 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6722 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6724 #define SCAN_OBJECT_ACTION
6725 #include "sgen-scan-object.h"
6729 * Perform consistency check of the heap.
6731 * Assumes the world is stopped.
6734 check_consistency (void)
6736 // Need to add more checks
6738 missing_remsets = FALSE;
6740 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6742 // Check that oldspace->newspace pointers are registered with the collector
6743 major_iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6745 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6747 #ifdef BINARY_PROTOCOL
6748 if (!binary_protocol_file)
6750 g_assert (!missing_remsets);
6753 /* Check that the reference is valid */
6755 #define HANDLE_PTR(ptr,obj) do { \
6757 g_assert (safe_name (*(ptr)) != NULL); \
6764 * Perform consistency check on an object. Currently we only check that the
6765 * reference fields are valid.
6768 check_object (char *start)
6773 #include "sgen-scan-object.h"
6779 * ######################################################################
6780 * ######## Other mono public interface functions.
6781 * ######################################################################
6785 mono_gc_collect (int generation)
6789 if (generation == 0) {
6790 collect_nursery (0);
6792 major_collection ("user request");
6799 mono_gc_max_generation (void)
6805 mono_gc_collection_count (int generation)
6807 if (generation == 0)
6808 return num_minor_gcs;
6809 return num_major_gcs;
6813 mono_gc_get_used_size (void)
6817 tot = los_memory_usage;
6818 tot += nursery_section->next_data - nursery_section->data;
6819 tot += major_get_used_size ();
6820 /* FIXME: account for pinned objects */
6826 mono_gc_get_heap_size (void)
6832 mono_gc_disable (void)
6840 mono_gc_enable (void)
6848 mono_gc_get_los_limit (void)
6850 return MAX_SMALL_OBJ_SIZE;
6854 mono_object_is_alive (MonoObject* o)
6860 mono_gc_get_generation (MonoObject *obj)
6862 if (ptr_in_nursery (obj))
6868 mono_gc_enable_events (void)
6873 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6876 mono_gc_register_disappearing_link (obj, link_addr, track);
6881 mono_gc_weak_link_remove (void **link_addr)
6884 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
6889 mono_gc_weak_link_get (void **link_addr)
6893 return (MonoObject*) REVEAL_POINTER (*link_addr);
6897 mono_gc_ephemeron_array_add (MonoObject *obj)
6899 EphemeronLinkNode *node;
6903 node = get_internal_mem (sizeof (EphemeronLinkNode), INTERNAL_MEM_EPHEMERON_LINK);
6908 node->array = (char*)obj;
6909 node->next = ephemeron_list;
6910 ephemeron_list = node;
6912 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6919 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6921 if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6922 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6924 mword complex = alloc_complex_descriptor (bitmap, numbits);
6925 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6930 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6934 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6935 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6936 user_descriptors [user_descriptors_next ++] = marker;
6942 mono_gc_alloc_fixed (size_t size, void *descr)
6944 /* FIXME: do a single allocation */
6945 void *res = calloc (1, size);
6948 if (!mono_gc_register_root (res, size, descr)) {
6956 mono_gc_free_fixed (void* addr)
6958 mono_gc_deregister_root (addr);
6963 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6967 result = func (data);
6968 UNLOCK_INTERRUPTION;
6973 mono_gc_is_gc_thread (void)
6977 result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
6984 /* Tries to extract a number from the passed string, taking in to account m, k
6987 parse_environment_string_extract_number (gchar *str, glong *out)
6990 int len = strlen (str), shift = 0;
6992 gboolean is_suffix = FALSE;
6995 switch (str [len - 1]) {
7006 suffix = str [len - 1];
7011 val = strtol (str, &endptr, 10);
7013 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
7014 || (errno != 0 && val == 0) || (endptr == str))
7018 if (*(endptr + 1)) /* Invalid string. */
7030 mono_gc_base_init (void)
7034 struct sigaction sinfo;
7036 LOCK_INIT (gc_mutex);
7038 if (gc_initialized) {
7042 pagesize = mono_pagesize ();
7043 gc_debug_file = stderr;
7047 if ((env = getenv ("MONO_GC_PARAMS"))) {
7048 if (g_str_has_prefix (env, "nursery-size")) {
7051 while (env [index] && env [index++] != '=')
7053 if (env [index] && parse_environment_string_extract_number (env
7055 default_nursery_size = val;
7056 #ifdef ALIGN_NURSERY
7057 if ((val & (val - 1))) {
7058 fprintf (stderr, "The nursery size must be a power of two.\n");
7062 default_nursery_bits = 0;
7063 while (1 << (++ default_nursery_bits) != default_nursery_size)
7067 fprintf (stderr, "nursery-size must be an integer.\n");
7071 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");
7078 nursery_size = DEFAULT_NURSERY_SIZE;
7082 if ((env = getenv ("MONO_GC_DEBUG"))) {
7083 opts = g_strsplit (env, ",", -1);
7084 for (ptr = opts; ptr && *ptr; ptr ++) {
7086 if (opt [0] >= '0' && opt [0] <= '9') {
7087 gc_debug_level = atoi (opt);
7092 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7093 gc_debug_file = fopen (rf, "wb");
7095 gc_debug_file = stderr;
7098 } else if (!strcmp (opt, "collect-before-allocs")) {
7099 collect_before_allocs = TRUE;
7100 } else if (!strcmp (opt, "check-at-minor-collections")) {
7101 consistency_check_at_minor_collection = TRUE;
7102 } else if (!strcmp (opt, "xdomain-checks")) {
7103 xdomain_checks = TRUE;
7104 } else if (!strcmp (opt, "clear-at-gc")) {
7105 nursery_clear_policy = CLEAR_AT_GC;
7106 } else if (!strcmp (opt, "conservative-stack-mark")) {
7107 conservative_stack_mark = TRUE;
7108 } else if (!strcmp (opt, "check-scan-starts")) {
7109 do_scan_starts_check = TRUE;
7110 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7111 char *filename = strchr (opt, '=') + 1;
7112 nursery_clear_policy = CLEAR_AT_GC;
7113 heap_dump_file = fopen (filename, "w");
7115 fprintf (heap_dump_file, "<sgen-dump>\n");
7116 #ifdef BINARY_PROTOCOL
7117 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7118 char *filename = strchr (opt, '=') + 1;
7119 binary_protocol_file = fopen (filename, "w");
7122 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7123 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7124 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
7131 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7132 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7134 sigfillset (&sinfo.sa_mask);
7135 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7136 sinfo.sa_sigaction = suspend_handler;
7137 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7138 g_error ("failed sigaction");
7141 sinfo.sa_handler = restart_handler;
7142 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7143 g_error ("failed sigaction");
7146 sigfillset (&suspend_signal_mask);
7147 sigdelset (&suspend_signal_mask, restart_signal_num);
7149 global_remset = alloc_remset (1024, NULL);
7150 global_remset->next = NULL;
7152 pthread_key_create (&remembered_set_key, unregister_thread);
7154 #ifndef HAVE_KW_THREAD
7155 pthread_key_create (&thread_info_key, NULL);
7158 gc_initialized = TRUE;
7160 mono_gc_register_thread (&sinfo);
7164 mono_gc_get_suspend_signal (void)
7166 return suspend_signal_num;
7176 #ifdef HAVE_KW_THREAD
7177 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7178 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7179 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7180 mono_mb_emit_i4 ((mb), (offset)); \
7183 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7184 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7185 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7186 mono_mb_emit_i4 ((mb), thread_info_key); \
7187 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7188 mono_mb_emit_byte ((mb), CEE_ADD); \
7189 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7193 #ifdef MANAGED_ALLOCATION
7194 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7195 * for each class. This is currently not easy to do, as it is hard to generate basic
7196 * blocks + branches, but it is easy with the linear IL codebase.
7198 * For this to work we'd need to solve the TLAB race, first. Now we
7199 * require the allocator to be in a few known methods to make sure
7200 * that they are executed atomically via the restart mechanism.
7203 create_allocator (int atype)
7205 int p_var, size_var;
7206 guint32 slowpath_branch, max_size_branch;
7207 MonoMethodBuilder *mb;
7209 MonoMethodSignature *csig;
7210 static gboolean registered = FALSE;
7211 int tlab_next_addr_var, new_next_var;
7213 const char *name = NULL;
7214 AllocatorWrapperInfo *info;
7216 #ifdef HAVE_KW_THREAD
7217 int tlab_next_addr_offset = -1;
7218 int tlab_temp_end_offset = -1;
7220 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7221 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7223 g_assert (tlab_next_addr_offset != -1);
7224 g_assert (tlab_temp_end_offset != -1);
7228 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7229 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7233 if (atype == ATYPE_SMALL) {
7235 name = "AllocSmall";
7236 } else if (atype == ATYPE_NORMAL) {
7239 } else if (atype == ATYPE_VECTOR) {
7241 name = "AllocVector";
7243 g_assert_not_reached ();
7246 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7247 csig->ret = &mono_defaults.object_class->byval_arg;
7248 for (i = 0; i < num_params; ++i)
7249 csig->params [i] = &mono_defaults.int_class->byval_arg;
7251 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7252 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7253 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7254 /* size = vtable->klass->instance_size; */
7255 mono_mb_emit_ldarg (mb, 0);
7256 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7257 mono_mb_emit_byte (mb, CEE_ADD);
7258 mono_mb_emit_byte (mb, CEE_LDIND_I);
7259 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7260 mono_mb_emit_byte (mb, CEE_ADD);
7261 /* FIXME: assert instance_size stays a 4 byte integer */
7262 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7263 mono_mb_emit_stloc (mb, size_var);
7264 } else if (atype == ATYPE_VECTOR) {
7265 MonoExceptionClause *clause;
7267 MonoClass *oom_exc_class;
7270 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7271 mono_mb_emit_ldarg (mb, 1);
7272 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7273 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7274 mono_mb_emit_exception (mb, "OverflowException", NULL);
7275 mono_mb_patch_short_branch (mb, pos);
7277 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7278 clause->try_offset = mono_mb_get_label (mb);
7280 /* vtable->klass->sizes.element_size */
7281 mono_mb_emit_ldarg (mb, 0);
7282 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7283 mono_mb_emit_byte (mb, CEE_ADD);
7284 mono_mb_emit_byte (mb, CEE_LDIND_I);
7285 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7286 mono_mb_emit_byte (mb, CEE_ADD);
7287 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7290 mono_mb_emit_ldarg (mb, 1);
7291 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7292 /* + sizeof (MonoArray) */
7293 mono_mb_emit_icon (mb, sizeof (MonoArray));
7294 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7295 mono_mb_emit_stloc (mb, size_var);
7297 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7300 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7301 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7302 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7303 "System", "OverflowException");
7304 g_assert (clause->data.catch_class);
7305 clause->handler_offset = mono_mb_get_label (mb);
7307 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7308 "System", "OutOfMemoryException");
7309 g_assert (oom_exc_class);
7310 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7313 mono_mb_emit_byte (mb, CEE_POP);
7314 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7315 mono_mb_emit_byte (mb, CEE_THROW);
7317 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7318 mono_mb_set_clauses (mb, 1, clause);
7319 mono_mb_patch_branch (mb, pos_leave);
7322 g_assert_not_reached ();
7325 /* size += ALLOC_ALIGN - 1; */
7326 mono_mb_emit_ldloc (mb, size_var);
7327 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7328 mono_mb_emit_byte (mb, CEE_ADD);
7329 /* size &= ~(ALLOC_ALIGN - 1); */
7330 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7331 mono_mb_emit_byte (mb, CEE_AND);
7332 mono_mb_emit_stloc (mb, size_var);
7334 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7335 if (atype != ATYPE_SMALL) {
7336 mono_mb_emit_ldloc (mb, size_var);
7337 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7338 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7342 * We need to modify tlab_next, but the JIT only supports reading, so we read
7343 * another tls var holding its address instead.
7346 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7347 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7348 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7349 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7351 /* p = (void**)tlab_next; */
7352 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7353 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7354 mono_mb_emit_byte (mb, CEE_LDIND_I);
7355 mono_mb_emit_stloc (mb, p_var);
7357 /* new_next = (char*)p + size; */
7358 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7359 mono_mb_emit_ldloc (mb, p_var);
7360 mono_mb_emit_ldloc (mb, size_var);
7361 mono_mb_emit_byte (mb, CEE_CONV_I);
7362 mono_mb_emit_byte (mb, CEE_ADD);
7363 mono_mb_emit_stloc (mb, new_next_var);
7365 /* tlab_next = new_next */
7366 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7367 mono_mb_emit_ldloc (mb, new_next_var);
7368 mono_mb_emit_byte (mb, CEE_STIND_I);
7370 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7371 mono_mb_emit_ldloc (mb, new_next_var);
7372 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7373 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7376 if (atype != ATYPE_SMALL)
7377 mono_mb_patch_short_branch (mb, max_size_branch);
7379 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7380 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7382 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7383 mono_mb_emit_ldarg (mb, 0);
7384 mono_mb_emit_ldloc (mb, size_var);
7385 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7386 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7387 } else if (atype == ATYPE_VECTOR) {
7388 mono_mb_emit_ldarg (mb, 1);
7389 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7391 g_assert_not_reached ();
7393 mono_mb_emit_byte (mb, CEE_RET);
7396 mono_mb_patch_short_branch (mb, slowpath_branch);
7398 /* FIXME: Memory barrier */
7401 mono_mb_emit_ldloc (mb, p_var);
7402 mono_mb_emit_ldarg (mb, 0);
7403 mono_mb_emit_byte (mb, CEE_STIND_I);
7405 if (atype == ATYPE_VECTOR) {
7406 /* arr->max_length = max_length; */
7407 mono_mb_emit_ldloc (mb, p_var);
7408 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7409 mono_mb_emit_ldarg (mb, 1);
7410 mono_mb_emit_byte (mb, CEE_STIND_I);
7414 mono_mb_emit_ldloc (mb, p_var);
7415 mono_mb_emit_byte (mb, CEE_RET);
7417 res = mono_mb_create_method (mb, csig, 8);
7419 mono_method_get_header (res)->init_locals = FALSE;
7421 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7422 info->alloc_type = atype;
7423 mono_marshal_set_wrapper_info (res, info);
7429 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7430 static MonoMethod *write_barrier_method;
7433 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7441 ji = mono_jit_info_table_find (domain, ip);
7444 method = ji->method;
7446 if (method == write_barrier_method)
7448 for (i = 0; i < ATYPE_NUM; ++i)
7449 if (method == alloc_method_cache [i])
7455 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7456 * The signature of the called method is:
7457 * object allocate (MonoVTable *vtable)
7460 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7462 #ifdef MANAGED_ALLOCATION
7463 MonoClass *klass = vtable->klass;
7465 #ifdef HAVE_KW_THREAD
7466 int tlab_next_offset = -1;
7467 int tlab_temp_end_offset = -1;
7468 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7469 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7471 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7475 if (!mono_runtime_has_tls_get ())
7477 if (klass->instance_size > tlab_size)
7479 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7483 if (klass->byval_arg.type == MONO_TYPE_STRING)
7485 if (collect_before_allocs)
7488 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7489 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7491 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7498 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7500 #ifdef MANAGED_ALLOCATION
7501 MonoClass *klass = vtable->klass;
7503 #ifdef HAVE_KW_THREAD
7504 int tlab_next_offset = -1;
7505 int tlab_temp_end_offset = -1;
7506 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7507 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7509 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7515 if (!mono_runtime_has_tls_get ())
7517 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7519 if (collect_before_allocs)
7521 g_assert (!klass->has_finalize && !klass->marshalbyref);
7523 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7530 mono_gc_get_managed_allocator_by_type (int atype)
7532 #ifdef MANAGED_ALLOCATION
7535 if (!mono_runtime_has_tls_get ())
7538 mono_loader_lock ();
7539 res = alloc_method_cache [atype];
7541 res = alloc_method_cache [atype] = create_allocator (atype);
7542 mono_loader_unlock ();
7550 mono_gc_get_managed_allocator_types (void)
7557 mono_gc_get_write_barrier (void)
7560 MonoMethodBuilder *mb;
7561 MonoMethodSignature *sig;
7562 #ifdef MANAGED_WBARRIER
7563 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7564 #ifndef ALIGN_NURSERY
7565 int label_continue_1, label_continue_2, label_no_wb_5;
7566 int dereferenced_var;
7568 int buffer_var, buffer_index_var, dummy_var;
7570 #ifdef HAVE_KW_THREAD
7571 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7572 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7574 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7575 g_assert (stack_end_offset != -1);
7576 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7577 g_assert (store_remset_buffer_offset != -1);
7578 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7579 g_assert (store_remset_buffer_index_offset != -1);
7580 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7581 g_assert (store_remset_buffer_index_addr_offset != -1);
7585 // FIXME: Maybe create a separate version for ctors (the branch would be
7586 // correctly predicted more times)
7587 if (write_barrier_method)
7588 return write_barrier_method;
7590 /* Create the IL version of mono_gc_barrier_generic_store () */
7591 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7592 sig->ret = &mono_defaults.void_class->byval_arg;
7593 sig->params [0] = &mono_defaults.int_class->byval_arg;
7595 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7597 #ifdef MANAGED_WBARRIER
7598 if (mono_runtime_has_tls_get ()) {
7599 #ifdef ALIGN_NURSERY
7600 // if (ptr_in_nursery (ptr)) return;
7602 * Masking out the bits might be faster, but we would have to use 64 bit
7603 * immediates, which might be slower.
7605 mono_mb_emit_ldarg (mb, 0);
7606 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7607 mono_mb_emit_byte (mb, CEE_SHR_UN);
7608 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7609 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7611 // if (!ptr_in_nursery (*ptr)) return;
7612 mono_mb_emit_ldarg (mb, 0);
7613 mono_mb_emit_byte (mb, CEE_LDIND_I);
7614 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7615 mono_mb_emit_byte (mb, CEE_SHR_UN);
7616 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7617 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7620 // if (ptr < (nursery_start)) goto continue;
7621 mono_mb_emit_ldarg (mb, 0);
7622 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7623 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7625 // if (ptr >= nursery_real_end)) goto continue;
7626 mono_mb_emit_ldarg (mb, 0);
7627 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7628 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7631 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7634 mono_mb_patch_branch (mb, label_continue_1);
7635 mono_mb_patch_branch (mb, label_continue_2);
7637 // Dereference and store in local var
7638 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7639 mono_mb_emit_ldarg (mb, 0);
7640 mono_mb_emit_byte (mb, CEE_LDIND_I);
7641 mono_mb_emit_stloc (mb, dereferenced_var);
7643 // if (*ptr < nursery_start) return;
7644 mono_mb_emit_ldloc (mb, dereferenced_var);
7645 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7646 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7648 // if (*ptr >= nursery_end) return;
7649 mono_mb_emit_ldloc (mb, dereferenced_var);
7650 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7651 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7654 // if (ptr >= stack_end) goto need_wb;
7655 mono_mb_emit_ldarg (mb, 0);
7656 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7657 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7659 // if (ptr >= stack_start) return;
7660 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7661 mono_mb_emit_ldarg (mb, 0);
7662 mono_mb_emit_ldloc_addr (mb, dummy_var);
7663 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7666 mono_mb_patch_branch (mb, label_need_wb);
7668 // buffer = STORE_REMSET_BUFFER;
7669 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7670 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7671 mono_mb_emit_stloc (mb, buffer_var);
7673 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7674 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7675 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7676 mono_mb_emit_stloc (mb, buffer_index_var);
7678 // if (buffer [buffer_index] == ptr) return;
7679 mono_mb_emit_ldloc (mb, buffer_var);
7680 mono_mb_emit_ldloc (mb, buffer_index_var);
7681 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7682 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7683 mono_mb_emit_byte (mb, CEE_SHL);
7684 mono_mb_emit_byte (mb, CEE_ADD);
7685 mono_mb_emit_byte (mb, CEE_LDIND_I);
7686 mono_mb_emit_ldarg (mb, 0);
7687 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7690 mono_mb_emit_ldloc (mb, buffer_index_var);
7691 mono_mb_emit_icon (mb, 1);
7692 mono_mb_emit_byte (mb, CEE_ADD);
7693 mono_mb_emit_stloc (mb, buffer_index_var);
7695 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7696 mono_mb_emit_ldloc (mb, buffer_index_var);
7697 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7698 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7700 // buffer [buffer_index] = ptr;
7701 mono_mb_emit_ldloc (mb, buffer_var);
7702 mono_mb_emit_ldloc (mb, buffer_index_var);
7703 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7704 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7705 mono_mb_emit_byte (mb, CEE_SHL);
7706 mono_mb_emit_byte (mb, CEE_ADD);
7707 mono_mb_emit_ldarg (mb, 0);
7708 mono_mb_emit_byte (mb, CEE_STIND_I);
7710 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7711 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7712 mono_mb_emit_ldloc (mb, buffer_index_var);
7713 mono_mb_emit_byte (mb, CEE_STIND_I);
7716 mono_mb_patch_branch (mb, label_no_wb_1);
7717 mono_mb_patch_branch (mb, label_no_wb_2);
7718 mono_mb_patch_branch (mb, label_no_wb_3);
7719 mono_mb_patch_branch (mb, label_no_wb_4);
7720 #ifndef ALIGN_NURSERY
7721 mono_mb_patch_branch (mb, label_no_wb_5);
7723 mono_mb_emit_byte (mb, CEE_RET);
7726 mono_mb_patch_branch (mb, label_slow_path);
7730 mono_mb_emit_ldarg (mb, 0);
7731 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7732 mono_mb_emit_byte (mb, CEE_RET);
7734 res = mono_mb_create_method (mb, sig, 16);
7737 mono_loader_lock ();
7738 if (write_barrier_method) {
7739 /* Already created */
7740 mono_free_method (res);
7742 /* double-checked locking */
7743 mono_memory_barrier ();
7744 write_barrier_method = res;
7746 mono_loader_unlock ();
7748 return write_barrier_method;
7751 #endif /* HAVE_SGEN_GC */