2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 * Thread start/stop adapted from Boehm's GC:
12 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
13 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
14 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
15 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
17 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
18 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
20 * Permission is hereby granted to use or copy this program
21 * for any purpose, provided the above notices are retained on all copies.
22 * Permission to modify the code and to distribute modified code is granted,
23 * provided the above notices are retained, and a notice that the code was
24 * modified is included with the above copyright notice.
27 * Copyright 2001-2003 Ximian, Inc
28 * Copyright 2003-2010 Novell, Inc.
29 * Copyright 2011 Xamarin, Inc.
31 * Permission is hereby granted, free of charge, to any person obtaining
32 * a copy of this software and associated documentation files (the
33 * "Software"), to deal in the Software without restriction, including
34 * without limitation the rights to use, copy, modify, merge, publish,
35 * distribute, sublicense, and/or sell copies of the Software, and to
36 * permit persons to whom the Software is furnished to do so, subject to
37 * the following conditions:
39 * The above copyright notice and this permission notice shall be
40 * included in all copies or substantial portions of the Software.
42 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
45 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
46 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
47 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
48 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51 * Important: allocation provides always zeroed memory, having to do
52 * a memset after allocation is deadly for performance.
53 * Memory usage at startup is currently as follows:
55 * 64 KB internal space
57 * We should provide a small memory config with half the sizes
59 * We currently try to make as few mono assumptions as possible:
60 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
62 * 2) gc descriptor is the second word in the vtable (first word in the class)
63 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
64 * 4) there is a function to get an object's size and the number of
65 * elements in an array.
66 * 5) we know the special way bounds are allocated for complex arrays
67 * 6) we know about proxies and how to treat them when domains are unloaded
69 * Always try to keep stack usage to a minimum: no recursive behaviour
70 * and no large stack allocs.
72 * General description.
73 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
74 * When the nursery is full we start a nursery collection: this is performed with a
76 * When the old generation is full we start a copying GC of the old generation as well:
77 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
78 * in the future. Maybe we'll even do both during the same collection like IMMIX.
80 * The things that complicate this description are:
81 * *) pinned objects: we can't move them so we need to keep track of them
82 * *) no precise info of the thread stacks and registers: we need to be able to
83 * quickly find the objects that may be referenced conservatively and pin them
84 * (this makes the first issues more important)
85 * *) large objects are too expensive to be dealt with using copying GC: we handle them
86 * with mark/sweep during major collections
87 * *) some objects need to not move even if they are small (interned strings, Type handles):
88 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
89 * PinnedChunks regions
95 *) we could have a function pointer in MonoClass to implement
96 customized write barriers for value types
98 *) investigate the stuff needed to advance a thread to a GC-safe
99 point (single-stepping, read from unmapped memory etc) and implement it.
100 This would enable us to inline allocations and write barriers, for example,
101 or at least parts of them, like the write barrier checks.
102 We may need this also for handling precise info on stacks, even simple things
103 as having uninitialized data on the stack and having to wait for the prolog
104 to zero it. Not an issue for the last frame that we scan conservatively.
105 We could always not trust the value in the slots anyway.
107 *) modify the jit to save info about references in stack locations:
108 this can be done just for locals as a start, so that at least
109 part of the stack is handled precisely.
111 *) test/fix endianess issues
113 *) Implement a card table as the write barrier instead of remembered
114 sets? Card tables are not easy to implement with our current
115 memory layout. We have several different kinds of major heap
116 objects: Small objects in regular blocks, small objects in pinned
117 chunks and LOS objects. If we just have a pointer we have no way
118 to tell which kind of object it points into, therefore we cannot
119 know where its card table is. The least we have to do to make
120 this happen is to get rid of write barriers for indirect stores.
123 *) Get rid of write barriers for indirect stores. We can do this by
124 telling the GC to wbarrier-register an object once we do an ldloca
125 or ldelema on it, and to unregister it once it's not used anymore
126 (it can only travel downwards on the stack). The problem with
127 unregistering is that it needs to happen eventually no matter
128 what, even if exceptions are thrown, the thread aborts, etc.
129 Rodrigo suggested that we could do only the registering part and
130 let the collector find out (pessimistically) when it's safe to
131 unregister, namely when the stack pointer of the thread that
132 registered the object is higher than it was when the registering
133 happened. This might make for a good first implementation to get
134 some data on performance.
136 *) Some sort of blacklist support? Blacklists is a concept from the
137 Boehm GC: if during a conservative scan we find pointers to an
138 area which we might use as heap, we mark that area as unusable, so
139 pointer retention by random pinning pointers is reduced.
141 *) experiment with max small object size (very small right now - 2kb,
142 because it's tied to the max freelist size)
144 *) add an option to mmap the whole heap in one chunk: it makes for many
145 simplifications in the checks (put the nursery at the top and just use a single
146 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
147 not flexible (too much of the address space may be used by default or we can't
148 increase the heap as needed) and we'd need a race-free mechanism to return memory
149 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
150 was written to, munmap is needed, but the following mmap may not find the same segment
153 *) memzero the major fragments after restarting the world and optionally a smaller
156 *) investigate having fragment zeroing threads
158 *) separate locks for finalization and other minor stuff to reduce
161 *) try a different copying order to improve memory locality
163 *) a thread abort after a store but before the write barrier will
164 prevent the write barrier from executing
166 *) specialized dynamically generated markers/copiers
168 *) Dynamically adjust TLAB size to the number of threads. If we have
169 too many threads that do allocation, we might need smaller TLABs,
170 and we might get better performance with larger TLABs if we only
171 have a handful of threads. We could sum up the space left in all
172 assigned TLABs and if that's more than some percentage of the
173 nursery size, reduce the TLAB size.
175 *) Explore placing unreachable objects on unused nursery memory.
176 Instead of memset'ng a region to zero, place an int[] covering it.
177 A good place to start is add_nursery_frag. The tricky thing here is
178 placing those objects atomically outside of a collection.
180 *) Allocation should use asymmetric Dekker synchronization:
181 http://blogs.oracle.com/dave/resource/Asymmetric-Dekker-Synchronization.txt
182 This should help weak consistency archs.
190 #include <semaphore.h>
199 #define _XOPEN_SOURCE
202 #include "metadata/sgen-gc.h"
203 #include "metadata/metadata-internals.h"
204 #include "metadata/class-internals.h"
205 #include "metadata/gc-internal.h"
206 #include "metadata/object-internals.h"
207 #include "metadata/threads.h"
208 #include "metadata/sgen-cardtable.h"
209 #include "metadata/sgen-protocol.h"
210 #include "metadata/sgen-archdep.h"
211 #include "metadata/sgen-bridge.h"
212 #include "metadata/mono-gc.h"
213 #include "metadata/method-builder.h"
214 #include "metadata/profiler-private.h"
215 #include "metadata/monitor.h"
216 #include "metadata/threadpool-internals.h"
217 #include "metadata/mempool-internals.h"
218 #include "metadata/marshal.h"
219 #include "metadata/runtime.h"
220 #include "utils/mono-mmap.h"
221 #include "utils/mono-time.h"
222 #include "utils/mono-semaphore.h"
223 #include "utils/mono-counters.h"
224 #include "utils/mono-proclib.h"
225 #include "utils/mono-memory-model.h"
226 #include "utils/mono-logger-internal.h"
228 #include <mono/utils/mono-logger-internal.h>
229 #include <mono/utils/memcheck.h>
231 #if defined(__MACH__)
232 #include "utils/mach-support.h"
235 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
239 #include "mono/cil/opcode.def"
245 #undef pthread_create
247 #undef pthread_detach
250 * ######################################################################
251 * ######## Types and constants used by the GC.
252 * ######################################################################
255 /* 0 means not initialized, 1 is initialized, -1 means in progress */
256 static gint32 gc_initialized = 0;
257 /* If set, do a minor collection before every X allocation */
258 static guint32 collect_before_allocs = 0;
259 /* If set, do a heap consistency check before each minor collection */
260 static gboolean consistency_check_at_minor_collection = FALSE;
261 /* If set, check that there are no references to the domain left at domain unload */
262 static gboolean xdomain_checks = FALSE;
263 /* If not null, dump the heap after each collection into this file */
264 static FILE *heap_dump_file = NULL;
265 /* If set, mark stacks conservatively, even if precise marking is possible */
266 static gboolean conservative_stack_mark = FALSE;
267 /* If set, do a plausibility check on the scan_starts before and after
269 static gboolean do_scan_starts_check = FALSE;
270 static gboolean nursery_collection_is_parallel = FALSE;
271 static gboolean disable_minor_collections = FALSE;
272 static gboolean disable_major_collections = FALSE;
273 static gboolean do_pin_stats = FALSE;
275 #ifdef HEAVY_STATISTICS
276 static long long stat_objects_alloced = 0;
277 static long long stat_bytes_alloced = 0;
278 long long stat_objects_alloced_degraded = 0;
279 long long stat_bytes_alloced_degraded = 0;
280 static long long stat_bytes_alloced_los = 0;
282 long long stat_copy_object_called_nursery = 0;
283 long long stat_objects_copied_nursery = 0;
284 long long stat_copy_object_called_major = 0;
285 long long stat_objects_copied_major = 0;
287 long long stat_scan_object_called_nursery = 0;
288 long long stat_scan_object_called_major = 0;
290 long long stat_nursery_copy_object_failed_from_space = 0;
291 long long stat_nursery_copy_object_failed_forwarded = 0;
292 long long stat_nursery_copy_object_failed_pinned = 0;
294 static long long stat_store_remsets = 0;
295 static long long stat_store_remsets_unique = 0;
296 static long long stat_saved_remsets_1 = 0;
297 static long long stat_saved_remsets_2 = 0;
298 static long long stat_local_remsets_processed = 0;
299 static long long stat_global_remsets_added = 0;
300 static long long stat_global_remsets_readded = 0;
301 static long long stat_global_remsets_processed = 0;
302 static long long stat_global_remsets_discarded = 0;
304 static int stat_wbarrier_set_field = 0;
305 static int stat_wbarrier_set_arrayref = 0;
306 static int stat_wbarrier_arrayref_copy = 0;
307 static int stat_wbarrier_generic_store = 0;
308 static int stat_wbarrier_generic_store_remset = 0;
309 static int stat_wbarrier_set_root = 0;
310 static int stat_wbarrier_value_copy = 0;
311 static int stat_wbarrier_object_copy = 0;
314 static long long stat_pinned_objects = 0;
316 static long long time_minor_pre_collection_fragment_clear = 0;
317 static long long time_minor_pinning = 0;
318 static long long time_minor_scan_remsets = 0;
319 static long long time_minor_scan_card_table = 0;
320 static long long time_minor_scan_pinned = 0;
321 static long long time_minor_scan_registered_roots = 0;
322 static long long time_minor_scan_thread_data = 0;
323 static long long time_minor_finish_gray_stack = 0;
324 static long long time_minor_fragment_creation = 0;
326 static long long time_major_pre_collection_fragment_clear = 0;
327 static long long time_major_pinning = 0;
328 static long long time_major_scan_pinned = 0;
329 static long long time_major_scan_registered_roots = 0;
330 static long long time_major_scan_thread_data = 0;
331 static long long time_major_scan_alloc_pinned = 0;
332 static long long time_major_scan_finalized = 0;
333 static long long time_major_scan_big_objects = 0;
334 static long long time_major_finish_gray_stack = 0;
335 static long long time_major_free_bigobjs = 0;
336 static long long time_major_los_sweep = 0;
337 static long long time_major_sweep = 0;
338 static long long time_major_fragment_creation = 0;
340 int gc_debug_level = 0;
342 static gboolean debug_print_allowance = FALSE;
346 mono_gc_flush_info (void)
348 fflush (gc_debug_file);
353 * Define this to allow the user to change the nursery size by
354 * specifying its value in the MONO_GC_PARAMS environmental
355 * variable. See mono_gc_base_init for details.
357 #define USER_CONFIG 1
359 #define TV_DECLARE SGEN_TV_DECLARE
360 #define TV_GETTIME SGEN_TV_GETTIME
361 #define TV_ELAPSED SGEN_TV_ELAPSED
362 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
364 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
366 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
368 /* the runtime can register areas of memory as roots: we keep two lists of roots,
369 * a pinned root set for conservatively scanned roots and a normal one for
370 * precisely scanned roots (currently implemented as a single list).
372 typedef struct _RootRecord RootRecord;
379 * We're never actually using the first element. It's always set to
380 * NULL to simplify the elimination of consecutive duplicate
383 #define STORE_REMSET_BUFFER_SIZE 1023
385 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
386 struct _GenericStoreRememberedSet {
387 GenericStoreRememberedSet *next;
388 /* We need one entry less because the first entry of store
389 remset buffers is always a dummy and we don't copy it. */
390 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
393 /* we have 4 possible values in the low 2 bits */
395 REMSET_LOCATION, /* just a pointer to the exact location */
396 REMSET_RANGE, /* range of pointer fields */
397 REMSET_OBJECT, /* mark all the object for scanning */
398 REMSET_VTYPE, /* a valuetype array described by a gc descriptor, a count and a size */
399 REMSET_TYPE_MASK = 0x3
402 #ifdef HAVE_KW_THREAD
403 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
405 static MonoNativeTlsKey remembered_set_key;
406 static RememberedSet *global_remset;
407 static RememberedSet *freed_thread_remsets;
408 static GenericStoreRememberedSet *generic_store_remsets = NULL;
410 /*A two slots cache for recently inserted remsets */
411 static gpointer global_remset_cache [2];
413 /* FIXME: later choose a size that takes into account the RememberedSet struct
414 * and doesn't waste any alloc paddin space.
416 #define DEFAULT_REMSET_SIZE 1024
417 static RememberedSet* alloc_remset (int size, gpointer id, gboolean global);
419 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
420 #define object_is_pinned SGEN_OBJECT_IS_PINNED
421 #define pin_object SGEN_PIN_OBJECT
422 #define unpin_object SGEN_UNPIN_OBJECT
424 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_end))
426 #define LOAD_VTABLE SGEN_LOAD_VTABLE
429 safe_name (void* obj)
431 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
432 return vt->klass->name;
435 #define safe_object_get_size mono_sgen_safe_object_get_size
438 mono_sgen_safe_name (void* obj)
440 return safe_name (obj);
444 * ######################################################################
445 * ######## Global data.
446 * ######################################################################
448 static LOCK_DECLARE (gc_mutex);
449 static int gc_disabled = 0;
450 static int num_minor_gcs = 0;
451 static int num_major_gcs = 0;
453 static gboolean use_cardtable;
457 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
458 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
459 static int default_nursery_size = (1 << 22);
460 #ifdef SGEN_ALIGN_NURSERY
461 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
462 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
463 static int default_nursery_bits = 22;
468 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
469 #ifdef SGEN_ALIGN_NURSERY
470 #define DEFAULT_NURSERY_BITS 22
475 #ifndef SGEN_ALIGN_NURSERY
476 #define DEFAULT_NURSERY_BITS -1
479 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
481 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
483 static mword pagesize = 4096;
484 static mword nursery_size;
485 static int degraded_mode = 0;
487 static mword bytes_pinned_from_failed_allocation = 0;
489 static mword total_alloc = 0;
490 /* use this to tune when to do a major/minor collection */
491 static mword memory_pressure = 0;
492 static mword minor_collection_allowance;
493 static int minor_collection_sections_alloced = 0;
496 /* GC Logging stats */
497 static int last_major_num_sections = 0;
498 static int last_los_memory_usage = 0;
499 static gboolean major_collection_happened = FALSE;
501 static GCMemSection *nursery_section = NULL;
502 static mword lowest_heap_address = ~(mword)0;
503 static mword highest_heap_address = 0;
505 static LOCK_DECLARE (interruption_mutex);
506 static LOCK_DECLARE (global_remset_mutex);
507 static LOCK_DECLARE (pin_queue_mutex);
509 #define LOCK_GLOBAL_REMSET mono_mutex_lock (&global_remset_mutex)
510 #define UNLOCK_GLOBAL_REMSET mono_mutex_unlock (&global_remset_mutex)
512 #define LOCK_PIN_QUEUE mono_mutex_lock (&pin_queue_mutex)
513 #define UNLOCK_PIN_QUEUE mono_mutex_unlock (&pin_queue_mutex)
515 typedef struct _FinalizeReadyEntry FinalizeReadyEntry;
516 struct _FinalizeReadyEntry {
517 FinalizeReadyEntry *next;
521 typedef struct _EphemeronLinkNode EphemeronLinkNode;
523 struct _EphemeronLinkNode {
524 EphemeronLinkNode *next;
533 int current_collection_generation = -1;
536 * The link pointer is hidden by negating each bit. We use the lowest
537 * bit of the link (before negation) to store whether it needs
538 * resurrection tracking.
540 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
541 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
543 /* objects that are ready to be finalized */
544 static FinalizeReadyEntry *fin_ready_list = NULL;
545 static FinalizeReadyEntry *critical_fin_list = NULL;
547 static EphemeronLinkNode *ephemeron_list;
549 static int num_ready_finalizers = 0;
550 static int no_finalize = 0;
553 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
554 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
555 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
559 /* registered roots: the key to the hash is the root start address */
561 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
563 static SgenHashTable roots_hash [ROOT_TYPE_NUM] = {
564 SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
565 SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
566 SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL)
568 static mword roots_size = 0; /* amount of memory in the root set */
570 #define GC_ROOT_NUM 32
573 void *objects [GC_ROOT_NUM];
574 int root_types [GC_ROOT_NUM];
575 uintptr_t extra_info [GC_ROOT_NUM];
579 notify_gc_roots (GCRootReport *report)
583 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
588 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
590 if (report->count == GC_ROOT_NUM)
591 notify_gc_roots (report);
592 report->objects [report->count] = object;
593 report->root_types [report->count] = rtype;
594 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
598 * The current allocation cursors
599 * We allocate objects in the nursery.
600 * The nursery is the area between nursery_start and nursery_end.
601 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
602 * from nursery fragments.
603 * tlab_next is the pointer to the space inside the TLAB where the next object will
605 * tlab_temp_end is the pointer to the end of the temporary space reserved for
606 * the allocation: it allows us to set the scan starts at reasonable intervals.
607 * tlab_real_end points to the end of the TLAB.
608 * nursery_frag_real_end points to the end of the currently used nursery fragment.
609 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
610 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
611 * At the next allocation, the area of the nursery where objects can be present is
612 * between MIN(nursery_first_pinned_start, first_fragment_start) and
613 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
615 static char *nursery_start = NULL;
616 static char *nursery_end = NULL;
617 static char *nursery_alloc_bound = NULL;
619 #ifdef HAVE_KW_THREAD
620 #define TLAB_ACCESS_INIT
621 #define TLAB_START tlab_start
622 #define TLAB_NEXT tlab_next
623 #define TLAB_TEMP_END tlab_temp_end
624 #define TLAB_REAL_END tlab_real_end
625 #define REMEMBERED_SET remembered_set
626 #define STORE_REMSET_BUFFER store_remset_buffer
627 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
628 #define IN_CRITICAL_REGION thread_info->in_critical_region
630 static MonoNativeTlsKey thread_info_key;
631 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = mono_native_tls_get_value (thread_info_key)
632 #define TLAB_START (__thread_info__->tlab_start)
633 #define TLAB_NEXT (__thread_info__->tlab_next)
634 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
635 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
636 #define REMEMBERED_SET (__thread_info__->remset)
637 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
638 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
639 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
642 #ifndef DISABLE_CRITICAL_REGION
644 /* Enter must be visible before anything is done in the critical region. */
645 #define ENTER_CRITICAL_REGION do { mono_atomic_store_acquire (&IN_CRITICAL_REGION, 1); } while (0)
647 /* Exit must make sure all critical regions stores are visible before it signal the end of the region.
648 * We don't need to emit a full barrier since we
650 #define EXIT_CRITICAL_REGION do { mono_atomic_store_release (&IN_CRITICAL_REGION, 0); } while (0)
656 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
657 * variables for next+temp_end ?
659 #ifdef HAVE_KW_THREAD
660 static __thread SgenThreadInfo *thread_info;
661 static __thread char *tlab_start;
662 static __thread char *tlab_next;
663 static __thread char *tlab_temp_end;
664 static __thread char *tlab_real_end;
665 static __thread gpointer *store_remset_buffer;
666 static __thread long store_remset_buffer_index;
667 /* Used by the managed allocator/wbarrier */
668 static __thread char **tlab_next_addr;
669 static __thread char *stack_end;
670 static __thread long *store_remset_buffer_index_addr;
673 /* The size of a TLAB */
674 /* The bigger the value, the less often we have to go to the slow path to allocate a new
675 * one, but the more space is wasted by threads not allocating much memory.
677 * FIXME: Make this self-tuning for each thread.
679 static guint32 tlab_size = (1024 * 4);
681 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
683 /* Functions supplied by the runtime to be called by the GC */
684 static MonoGCCallbacks gc_callbacks;
686 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
687 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
689 #define ALIGN_UP SGEN_ALIGN_UP
691 #define MOVED_OBJECTS_NUM 64
692 static void *moved_objects [MOVED_OBJECTS_NUM];
693 static int moved_objects_idx = 0;
695 /* Vtable of the objects used to fill out nursery fragments before a collection */
696 static MonoVTable *array_fill_vtable;
698 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
699 MonoNativeThreadId main_gc_thread = NULL;
703 * ######################################################################
704 * ######## Heap size accounting
705 * ######################################################################
708 static mword max_heap_size = ((mword)0)- ((mword)1);
709 static mword soft_heap_limit = ((mword)0) - ((mword)1);
710 static mword allocated_heap;
712 /*Object was pinned during the current collection*/
713 static mword objects_pinned;
716 mono_sgen_release_space (mword size, int space)
718 allocated_heap -= size;
722 available_free_space (void)
724 return max_heap_size - MIN (allocated_heap, max_heap_size);
728 mono_sgen_try_alloc_space (mword size, int space)
730 if (available_free_space () < size)
733 allocated_heap += size;
734 mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
739 init_heap_size_limits (glong max_heap, glong soft_limit)
742 soft_heap_limit = soft_limit;
747 if (max_heap < soft_limit) {
748 fprintf (stderr, "max-heap-size must be at least as large as soft-heap-limit.\n");
752 if (max_heap < nursery_size * 4) {
753 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
756 max_heap_size = max_heap - nursery_size;
760 * ######################################################################
761 * ######## Macros and function declarations.
762 * ######################################################################
766 align_pointer (void *ptr)
768 mword p = (mword)ptr;
769 p += sizeof (gpointer) - 1;
770 p &= ~ (sizeof (gpointer) - 1);
774 typedef SgenGrayQueue GrayQueue;
776 /* forward declarations */
777 static int stop_world (int generation);
778 static int restart_world (int generation);
779 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
780 static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
781 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
782 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
783 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeReadyEntry *list, GrayQueue *queue);
784 static void report_finalizer_roots (void);
785 static void report_registered_roots (void);
786 static void find_pinning_ref_from_thread (char *obj, size_t size);
787 static void update_current_thread_stack (void *start);
788 static void collect_bridge_objects (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
789 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
790 static void process_fin_stage_entries (void);
791 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
792 static void null_links_for_domain (MonoDomain *domain, int generation);
793 static void process_dislink_stage_entries (void);
795 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
796 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
797 static void optimize_pin_queue (int start_slot);
798 static void clear_remsets (void);
799 static void clear_tlabs (void);
800 static void sort_addresses (void **array, int size);
801 static gboolean drain_gray_stack (GrayQueue *queue, int max_objs);
802 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
803 static gboolean need_major_collection (mword space_needed);
804 static void major_collection (const char *reason);
806 static gboolean collection_is_parallel (void);
808 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc);
809 static gboolean mono_gc_is_critical_method (MonoMethod *method);
811 void describe_ptr (char *ptr);
812 void check_object (char *start);
814 static void check_consistency (void);
815 static void check_major_refs (void);
816 static void check_scan_starts (void);
817 static void check_for_xdomain_refs (void);
818 static void dump_heap (const char *type, int num, const char *reason);
820 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
822 static void init_stats (void);
824 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
825 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
826 static void null_ephemerons_for_domain (MonoDomain *domain);
828 SgenMajorCollector major_collector;
830 #include "sgen-pinning.c"
831 #include "sgen-pinning-stats.c"
832 #include "sgen-gray.c"
833 #include "sgen-workers.c"
834 #include "sgen-cardtable.c"
836 /* Root bitmap descriptors are simpler: the lower three bits describe the type
837 * and we either have 30/62 bitmap bits or nibble-based run-length,
838 * or a complex descriptor, or a user defined marker function.
841 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
846 ROOT_DESC_TYPE_MASK = 0x7,
847 ROOT_DESC_TYPE_SHIFT = 3,
850 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
852 #define MAX_USER_DESCRIPTORS 16
854 static gsize* complex_descriptors = NULL;
855 static int complex_descriptors_size = 0;
856 static int complex_descriptors_next = 0;
857 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
858 static int user_descriptors_next = 0;
861 alloc_complex_descriptor (gsize *bitmap, int numbits)
865 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
866 nwords = numbits / GC_BITS_PER_WORD + 1;
869 res = complex_descriptors_next;
870 /* linear search, so we don't have duplicates with domain load/unload
871 * this should not be performance critical or we'd have bigger issues
872 * (the number and size of complex descriptors should be small).
874 for (i = 0; i < complex_descriptors_next; ) {
875 if (complex_descriptors [i] == nwords) {
877 for (j = 0; j < nwords - 1; ++j) {
878 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
888 i += complex_descriptors [i];
890 if (complex_descriptors_next + nwords > complex_descriptors_size) {
891 int new_size = complex_descriptors_size * 2 + nwords;
892 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
893 complex_descriptors_size = new_size;
895 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
896 complex_descriptors_next += nwords;
897 complex_descriptors [res] = nwords;
898 for (i = 0; i < nwords - 1; ++i) {
899 complex_descriptors [res + 1 + i] = bitmap [i];
900 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
907 mono_sgen_get_complex_descriptor (mword desc)
909 return complex_descriptors + (desc >> LOW_TYPE_BITS);
913 * Descriptor builders.
916 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
918 return (void*) DESC_TYPE_RUN_LENGTH;
922 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
924 int first_set = -1, num_set = 0, last_set = -1, i;
926 size_t stored_size = obj_size;
927 for (i = 0; i < numbits; ++i) {
928 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
936 * We don't encode the size of types that don't contain
937 * references because they might not be aligned, i.e. the
938 * bottom two bits might be set, which would clash with the
939 * bits we need to encode the descriptor type. Since we don't
940 * use the encoded size to skip objects, other than for
941 * processing remsets, in which case only the positions of
942 * references are relevant, this is not a problem.
945 return (void*)DESC_TYPE_RUN_LENGTH;
946 g_assert (!(stored_size & 0x3));
947 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
948 /* check run-length encoding first: one byte offset, one byte number of pointers
949 * on 64 bit archs, we can have 3 runs, just one on 32.
950 * It may be better to use nibbles.
953 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
954 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
956 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
957 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
958 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));
962 /* we know the 2-word header is ptr-free */
963 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
964 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
965 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
968 /* it's a complex object ... */
969 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
973 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
975 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
977 int first_set = -1, num_set = 0, last_set = -1, i;
978 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
979 for (i = 0; i < numbits; ++i) {
980 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
987 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
989 return (void*)DESC_TYPE_RUN_LENGTH;
990 if (elem_size <= MAX_ELEMENT_SIZE) {
991 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
993 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
995 /* Note: we also handle structs with just ref fields */
996 if (num_set * sizeof (gpointer) == elem_size) {
997 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
999 /* FIXME: try run-len first */
1000 /* Note: we can't skip the object header here, because it's not present */
1001 if (last_set <= SMALL_BITMAP_SIZE) {
1002 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1005 /* it's am array of complex structs ... */
1006 desc = DESC_TYPE_COMPLEX_ARR;
1007 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1008 return (void*) desc;
1011 /* Return the bitmap encoded by a descriptor */
1013 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1015 mword d = (mword)descr;
1019 case DESC_TYPE_RUN_LENGTH: {
1020 int first_set = (d >> 16) & 0xff;
1021 int num_set = (d >> 24) & 0xff;
1024 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1026 for (i = first_set; i < first_set + num_set; ++i)
1027 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1029 *numbits = first_set + num_set;
1033 case DESC_TYPE_LARGE_BITMAP: {
1034 gsize bmap = (d >> LOW_TYPE_BITS) << OBJECT_HEADER_WORDS;
1036 bitmap = g_new0 (gsize, 1);
1046 g_assert_not_reached ();
1051 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1053 MonoObject *o = (MonoObject*)(obj);
1054 MonoObject *ref = (MonoObject*)*(ptr);
1055 int offset = (char*)(ptr) - (char*)o;
1057 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1059 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1061 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1062 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1064 /* Thread.cached_culture_info */
1065 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1066 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1067 !strcmp(o->vtable->klass->name_space, "System") &&
1068 !strcmp(o->vtable->klass->name, "Object[]"))
1071 * 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
1072 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1073 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1074 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1075 * 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
1076 * 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
1077 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1078 * 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
1079 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1081 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1082 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1083 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1084 !strcmp (o->vtable->klass->name, "MemoryStream"))
1086 /* append_job() in threadpool.c */
1087 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1088 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1089 !strcmp (o->vtable->klass->name_space, "System") &&
1090 !strcmp (o->vtable->klass->name, "Object[]") &&
1091 mono_thread_pool_is_queue_array ((MonoArray*) o))
1097 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1099 MonoObject *o = (MonoObject*)(obj);
1100 MonoObject *ref = (MonoObject*)*(ptr);
1101 int offset = (char*)(ptr) - (char*)o;
1103 MonoClassField *field;
1106 if (!ref || ref->vtable->domain == domain)
1108 if (is_xdomain_ref_allowed (ptr, obj, domain))
1112 for (class = o->vtable->klass; class; class = class->parent) {
1115 for (i = 0; i < class->field.count; ++i) {
1116 if (class->fields[i].offset == offset) {
1117 field = &class->fields[i];
1125 if (ref->vtable->klass == mono_defaults.string_class)
1126 str = mono_string_to_utf8 ((MonoString*)ref);
1129 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1130 o, o->vtable->klass->name_space, o->vtable->klass->name,
1131 offset, field ? field->name : "",
1132 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1133 mono_gc_scan_for_specific_ref (o, TRUE);
1139 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1142 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1144 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1146 #include "sgen-scan-object.h"
1149 static gboolean scan_object_for_specific_ref_precise = TRUE;
1152 #define HANDLE_PTR(ptr,obj) do { \
1153 if ((MonoObject*)*(ptr) == key) { \
1154 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1155 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1160 scan_object_for_specific_ref (char *start, MonoObject *key)
1164 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1167 if (scan_object_for_specific_ref_precise) {
1168 #include "sgen-scan-object.h"
1170 mword *words = (mword*)start;
1171 size_t size = safe_object_get_size ((MonoObject*)start);
1173 for (i = 0; i < size / sizeof (mword); ++i) {
1174 if (words [i] == (mword)key) {
1175 g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
1176 key, start, safe_name (start), i * sizeof (mword));
1183 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1185 while (start < end) {
1189 if (!*(void**)start) {
1190 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1195 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1201 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1203 callback (obj, size, data);
1210 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1212 scan_object_for_specific_ref (obj, key);
1216 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1220 g_print ("found ref to %p in root record %p\n", key, root);
1223 static MonoObject *check_key = NULL;
1224 static RootRecord *check_root = NULL;
1227 check_root_obj_specific_ref_from_marker (void **obj)
1229 check_root_obj_specific_ref (check_root, check_key, *obj);
1233 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1239 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1240 mword desc = root->root_desc;
1244 switch (desc & ROOT_DESC_TYPE_MASK) {
1245 case ROOT_DESC_BITMAP:
1246 desc >>= ROOT_DESC_TYPE_SHIFT;
1249 check_root_obj_specific_ref (root, key, *start_root);
1254 case ROOT_DESC_COMPLEX: {
1255 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1256 int bwords = (*bitmap_data) - 1;
1257 void **start_run = start_root;
1259 while (bwords-- > 0) {
1260 gsize bmap = *bitmap_data++;
1261 void **objptr = start_run;
1264 check_root_obj_specific_ref (root, key, *objptr);
1268 start_run += GC_BITS_PER_WORD;
1272 case ROOT_DESC_USER: {
1273 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1274 marker (start_root, check_root_obj_specific_ref_from_marker);
1277 case ROOT_DESC_RUN_LEN:
1278 g_assert_not_reached ();
1280 g_assert_not_reached ();
1282 } SGEN_HASH_TABLE_FOREACH_END;
1289 mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
1294 scan_object_for_specific_ref_precise = precise;
1296 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1297 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1299 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1301 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1303 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1304 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1306 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], ptr, root) {
1307 while (ptr < (void**)root->end_root) {
1308 check_root_obj_specific_ref (root, *ptr, key);
1311 } SGEN_HASH_TABLE_FOREACH_END;
1315 need_remove_object_for_domain (char *start, MonoDomain *domain)
1317 if (mono_object_domain (start) == domain) {
1318 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1319 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1326 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1328 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1329 if (vt->klass == mono_defaults.internal_thread_class)
1330 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1331 /* The object could be a proxy for an object in the domain
1333 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1334 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1336 /* The server could already have been zeroed out, so
1337 we need to check for that, too. */
1338 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1339 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1341 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1346 static MonoDomain *check_domain = NULL;
1349 check_obj_not_in_domain (void **o)
1351 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1355 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1359 check_domain = domain;
1360 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1361 mword desc = root->root_desc;
1363 /* The MonoDomain struct is allowed to hold
1364 references to objects in its own domain. */
1365 if (start_root == (void**)domain)
1368 switch (desc & ROOT_DESC_TYPE_MASK) {
1369 case ROOT_DESC_BITMAP:
1370 desc >>= ROOT_DESC_TYPE_SHIFT;
1372 if ((desc & 1) && *start_root)
1373 check_obj_not_in_domain (*start_root);
1378 case ROOT_DESC_COMPLEX: {
1379 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1380 int bwords = (*bitmap_data) - 1;
1381 void **start_run = start_root;
1383 while (bwords-- > 0) {
1384 gsize bmap = *bitmap_data++;
1385 void **objptr = start_run;
1387 if ((bmap & 1) && *objptr)
1388 check_obj_not_in_domain (*objptr);
1392 start_run += GC_BITS_PER_WORD;
1396 case ROOT_DESC_USER: {
1397 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1398 marker (start_root, check_obj_not_in_domain);
1401 case ROOT_DESC_RUN_LEN:
1402 g_assert_not_reached ();
1404 g_assert_not_reached ();
1406 } SGEN_HASH_TABLE_FOREACH_END;
1408 check_domain = NULL;
1412 check_for_xdomain_refs (void)
1416 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1417 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1419 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1421 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1422 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1426 clear_domain_process_object (char *obj, MonoDomain *domain)
1430 process_object_for_domain_clearing (obj, domain);
1431 remove = need_remove_object_for_domain (obj, domain);
1433 if (remove && ((MonoObject*)obj)->synchronisation) {
1434 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1436 mono_gc_register_disappearing_link (NULL, dislink, FALSE, TRUE);
1443 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1445 if (clear_domain_process_object (obj, domain))
1446 memset (obj, 0, size);
1450 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1452 clear_domain_process_object (obj, domain);
1456 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1458 if (need_remove_object_for_domain (obj, domain))
1459 major_collector.free_non_pinned_object (obj, size);
1463 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1465 if (need_remove_object_for_domain (obj, domain))
1466 major_collector.free_pinned_object (obj, size);
1470 * When appdomains are unloaded we can easily remove objects that have finalizers,
1471 * but all the others could still be present in random places on the heap.
1472 * We need a sweep to get rid of them even though it's going to be costly
1474 * The reason we need to remove them is because we access the vtable and class
1475 * structures to know the object size and the reference bitmap: once the domain is
1476 * unloaded the point to random memory.
1479 mono_gc_clear_domain (MonoDomain * domain)
1481 LOSObject *bigobj, *prev;
1486 process_fin_stage_entries ();
1487 process_dislink_stage_entries ();
1489 mono_sgen_clear_nursery_fragments ();
1491 if (xdomain_checks && domain != mono_get_root_domain ()) {
1492 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1493 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1494 check_for_xdomain_refs ();
1497 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1498 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1500 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1501 to memory returned to the OS.*/
1502 null_ephemerons_for_domain (domain);
1504 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1505 null_links_for_domain (domain, i);
1507 /* We need two passes over major and large objects because
1508 freeing such objects might give their memory back to the OS
1509 (in the case of large objects) or obliterate its vtable
1510 (pinned objects with major-copying or pinned and non-pinned
1511 objects with major-mark&sweep), but we might need to
1512 dereference a pointer from an object to another object if
1513 the first object is a proxy. */
1514 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1515 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1516 clear_domain_process_object (bigobj->data, domain);
1519 for (bigobj = los_object_list; bigobj;) {
1520 if (need_remove_object_for_domain (bigobj->data, domain)) {
1521 LOSObject *to_free = bigobj;
1523 prev->next = bigobj->next;
1525 los_object_list = bigobj->next;
1526 bigobj = bigobj->next;
1527 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1529 mono_sgen_los_free_object (to_free);
1533 bigobj = bigobj->next;
1535 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1536 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1538 if (do_pin_stats && domain == mono_get_root_domain ())
1539 mono_sgen_pin_stats_print_class_stats ();
1545 global_remset_cache_clear (void)
1547 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1551 * Tries to check if a given remset location was already added to the global remset.
1554 * A 2 entry, LRU cache of recently saw location remsets.
1556 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1558 * Returns TRUE is the element was added..
1561 global_remset_location_was_not_added (gpointer ptr)
1564 gpointer first = global_remset_cache [0], second;
1566 HEAVY_STAT (++stat_global_remsets_discarded);
1570 second = global_remset_cache [1];
1572 if (second == ptr) {
1573 /*Move the second to the front*/
1574 global_remset_cache [0] = second;
1575 global_remset_cache [1] = first;
1577 HEAVY_STAT (++stat_global_remsets_discarded);
1581 global_remset_cache [0] = second;
1582 global_remset_cache [1] = ptr;
1587 * mono_sgen_add_to_global_remset:
1589 * The global remset contains locations which point into newspace after
1590 * a minor collection. This can happen if the objects they point to are pinned.
1592 * LOCKING: If called from a parallel collector, the global remset
1593 * lock must be held. For serial collectors that is not necessary.
1596 mono_sgen_add_to_global_remset (gpointer ptr)
1599 gboolean lock = collection_is_parallel ();
1600 gpointer obj = *(gpointer*)ptr;
1602 if (use_cardtable) {
1603 sgen_card_table_mark_address ((mword)ptr);
1607 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (obj));
1612 if (!global_remset_location_was_not_added (ptr))
1616 mono_sgen_pin_stats_register_global_remset (obj);
1618 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1619 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (obj));
1621 HEAVY_STAT (++stat_global_remsets_added);
1624 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1625 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1627 if (global_remset->store_next + 3 < global_remset->end_set) {
1628 *(global_remset->store_next++) = (mword)ptr;
1631 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL, TRUE);
1632 rs->next = global_remset;
1634 *(global_remset->store_next++) = (mword)ptr;
1637 int global_rs_size = 0;
1639 for (rs = global_remset; rs; rs = rs->next) {
1640 global_rs_size += rs->store_next - rs->data;
1642 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1647 UNLOCK_GLOBAL_REMSET;
1653 * Scan objects in the gray stack until the stack is empty. This should be called
1654 * frequently after each object is copied, to achieve better locality and cache
1658 drain_gray_stack (GrayQueue *queue, int max_objs)
1662 if (current_collection_generation == GENERATION_NURSERY) {
1663 ScanObjectFunc scan_func = mono_sgen_get_minor_scan_object ();
1666 GRAY_OBJECT_DEQUEUE (queue, obj);
1669 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1670 scan_func (obj, queue);
1675 if (collection_is_parallel () && queue == &workers_distribute_gray_queue)
1679 for (i = 0; i != max_objs; ++i) {
1680 GRAY_OBJECT_DEQUEUE (queue, obj);
1683 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1684 major_collector.major_scan_object (obj, queue);
1686 } while (max_objs < 0);
1692 * Addresses from start to end are already sorted. This function finds
1693 * the object header for each address and pins the object. The
1694 * addresses must be inside the passed section. The (start of the)
1695 * address array is overwritten with the addresses of the actually
1696 * pinned objects. Return the number of pinned objects.
1699 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1704 void *last_obj = NULL;
1705 size_t last_obj_size = 0;
1708 void **definitely_pinned = start;
1710 mono_sgen_nursery_allocator_prepare_for_pinning ();
1712 while (start < end) {
1714 /* the range check should be reduntant */
1715 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1716 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1717 /* multiple pointers to the same object */
1718 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1722 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1723 g_assert (idx < section->num_scan_start);
1724 search_start = (void*)section->scan_starts [idx];
1725 if (!search_start || search_start > addr) {
1728 search_start = section->scan_starts [idx];
1729 if (search_start && search_start <= addr)
1732 if (!search_start || search_start > addr)
1733 search_start = start_nursery;
1735 if (search_start < last_obj)
1736 search_start = (char*)last_obj + last_obj_size;
1737 /* now addr should be in an object a short distance from search_start
1738 * Note that search_start must point to zeroed mem or point to an object.
1742 if (!*(void**)search_start) {
1743 /* Consistency check */
1745 for (frag = nursery_fragments; frag; frag = frag->next) {
1746 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1747 g_assert_not_reached ();
1751 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1754 last_obj = search_start;
1755 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1757 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1758 /* Marks the beginning of a nursery fragment, skip */
1760 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1761 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1762 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));
1763 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1764 pin_object (search_start);
1765 GRAY_OBJECT_ENQUEUE (queue, search_start);
1767 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1768 definitely_pinned [count] = search_start;
1773 /* skip to the next object */
1774 search_start = (void*)((char*)search_start + last_obj_size);
1775 } while (search_start <= addr);
1776 /* we either pinned the correct object or we ignored the addr because
1777 * it points to unused zeroed memory.
1783 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1784 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1785 GCRootReport report;
1787 for (idx = 0; idx < count; ++idx)
1788 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
1789 notify_gc_roots (&report);
1791 stat_pinned_objects += count;
1796 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1798 int num_entries = section->pin_queue_num_entries;
1800 void **start = section->pin_queue_start;
1802 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1803 section->data, section->next_data, queue);
1804 section->pin_queue_num_entries = reduced_to;
1806 section->pin_queue_start = NULL;
1812 mono_sgen_pin_object (void *object, GrayQueue *queue)
1814 if (collection_is_parallel ()) {
1816 /*object arrives pinned*/
1817 pin_stage_ptr (object);
1821 SGEN_PIN_OBJECT (object);
1822 pin_stage_ptr (object);
1825 mono_sgen_pin_stats_register_object (object, safe_object_get_size (object));
1827 GRAY_OBJECT_ENQUEUE (queue, object);
1828 binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
1831 /* Sort the addresses in array in increasing order.
1832 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1835 sort_addresses (void **array, int size)
1840 for (i = 1; i < size; ++i) {
1843 int parent = (child - 1) / 2;
1845 if (array [parent] >= array [child])
1848 tmp = array [parent];
1849 array [parent] = array [child];
1850 array [child] = tmp;
1856 for (i = size - 1; i > 0; --i) {
1859 array [i] = array [0];
1865 while (root * 2 + 1 <= end) {
1866 int child = root * 2 + 1;
1868 if (child < end && array [child] < array [child + 1])
1870 if (array [root] >= array [child])
1874 array [root] = array [child];
1875 array [child] = tmp;
1882 static G_GNUC_UNUSED void
1883 print_nursery_gaps (void* start_nursery, void *end_nursery)
1886 gpointer first = start_nursery;
1888 for (i = 0; i < next_pin_slot; ++i) {
1889 next = pin_queue [i];
1890 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1894 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1897 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1899 optimize_pin_queue (int start_slot)
1901 void **start, **cur, **end;
1902 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1903 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1904 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1905 if ((next_pin_slot - start_slot) > 1)
1906 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1907 start = cur = pin_queue + start_slot;
1908 end = pin_queue + next_pin_slot;
1911 while (*start == *cur && cur < end)
1915 next_pin_slot = start - pin_queue;
1916 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1917 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1922 * Scan the memory between start and end and queue values which could be pointers
1923 * to the area between start_nursery and end_nursery for later consideration.
1924 * Typically used for thread stacks.
1927 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1930 while (start < end) {
1931 if (*start >= start_nursery && *start < end_nursery) {
1933 * *start can point to the middle of an object
1934 * note: should we handle pointing at the end of an object?
1935 * pinning in C# code disallows pointing at the end of an object
1936 * but there is some small chance that an optimizing C compiler
1937 * may keep the only reference to an object by pointing
1938 * at the end of it. We ignore this small chance for now.
1939 * Pointers to the end of an object are indistinguishable
1940 * from pointers to the start of the next object in memory
1941 * so if we allow that we'd need to pin two objects...
1942 * We queue the pointer in an array, the
1943 * array will then be sorted and uniqued. This way
1944 * we can coalesce several pinning pointers and it should
1945 * be faster since we'd do a memory scan with increasing
1946 * addresses. Note: we can align the address to the allocation
1947 * alignment, so the unique process is more effective.
1949 mword addr = (mword)*start;
1950 addr &= ~(ALLOC_ALIGN - 1);
1951 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
1952 pin_stage_ptr ((void*)addr);
1953 if (do_pin_stats && ptr_in_nursery (addr))
1954 pin_stats_register_address ((char*)addr, pin_type);
1955 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
1960 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1964 * Debugging function: find in the conservative roots where @obj is being pinned.
1966 static G_GNUC_UNUSED void
1967 find_pinning_reference (char *obj, size_t size)
1971 char *endobj = obj + size;
1973 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
1974 /* if desc is non-null it has precise info */
1975 if (!root->root_desc) {
1976 while (start < (char**)root->end_root) {
1977 if (*start >= obj && *start < endobj) {
1978 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root));
1983 } SGEN_HASH_TABLE_FOREACH_END;
1985 find_pinning_ref_from_thread (obj, size);
1989 * The first thing we do in a collection is to identify pinned objects.
1990 * This function considers all the areas of memory that need to be
1991 * conservatively scanned.
1994 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
1998 DEBUG (2, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d/%d entries)\n", (int)roots_size, roots_hash [ROOT_TYPE_NORMAL].num_entries, roots_hash [ROOT_TYPE_PINNED].num_entries));
1999 /* objects pinned from the API are inside these roots */
2000 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], start_root, root) {
2001 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", start_root, root->end_root));
2002 conservatively_pin_objects_from (start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2003 } SGEN_HASH_TABLE_FOREACH_END;
2004 /* now deal with the thread stacks
2005 * in the future we should be able to conservatively scan only:
2006 * *) the cpu registers
2007 * *) the unmanaged stack frames
2008 * *) the _last_ managed stack frame
2009 * *) pointers slots in managed frames
2011 scan_thread_data (start_nursery, end_nursery, FALSE, queue);
2013 evacuate_pin_staging_area ();
2017 CopyOrMarkObjectFunc func;
2019 } UserCopyOrMarkData;
2021 static MonoNativeTlsKey user_copy_or_mark_key;
2024 init_user_copy_or_mark_key (void)
2026 mono_native_tls_alloc (&user_copy_or_mark_key, NULL);
2030 set_user_copy_or_mark_data (UserCopyOrMarkData *data)
2032 mono_native_tls_set_value (user_copy_or_mark_key, data);
2036 single_arg_user_copy_or_mark (void **obj)
2038 UserCopyOrMarkData *data = mono_native_tls_get_value (user_copy_or_mark_key);
2040 data->func (obj, data->queue);
2044 * The memory area from start_root to end_root contains pointers to objects.
2045 * Their position is precisely described by @desc (this means that the pointer
2046 * can be either NULL or the pointer to the start of an object).
2047 * This functions copies them to to_space updates them.
2049 * This function is not thread-safe!
2052 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2054 switch (desc & ROOT_DESC_TYPE_MASK) {
2055 case ROOT_DESC_BITMAP:
2056 desc >>= ROOT_DESC_TYPE_SHIFT;
2058 if ((desc & 1) && *start_root) {
2059 copy_func (start_root, queue);
2060 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2061 drain_gray_stack (queue, -1);
2067 case ROOT_DESC_COMPLEX: {
2068 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2069 int bwords = (*bitmap_data) - 1;
2070 void **start_run = start_root;
2072 while (bwords-- > 0) {
2073 gsize bmap = *bitmap_data++;
2074 void **objptr = start_run;
2076 if ((bmap & 1) && *objptr) {
2077 copy_func (objptr, queue);
2078 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2079 drain_gray_stack (queue, -1);
2084 start_run += GC_BITS_PER_WORD;
2088 case ROOT_DESC_USER: {
2089 UserCopyOrMarkData data = { copy_func, queue };
2090 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2091 set_user_copy_or_mark_data (&data);
2092 marker (start_root, single_arg_user_copy_or_mark);
2093 set_user_copy_or_mark_data (NULL);
2096 case ROOT_DESC_RUN_LEN:
2097 g_assert_not_reached ();
2099 g_assert_not_reached ();
2104 reset_heap_boundaries (void)
2106 lowest_heap_address = ~(mword)0;
2107 highest_heap_address = 0;
2111 mono_sgen_update_heap_boundaries (mword low, mword high)
2116 old = lowest_heap_address;
2119 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2122 old = highest_heap_address;
2125 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2128 static unsigned long
2129 prot_flags_for_activate (int activate)
2131 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
2132 return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
2136 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
2137 * This must not require any lock.
2140 mono_sgen_alloc_os_memory (size_t size, int activate)
2142 void *ptr = mono_valloc (0, size, prot_flags_for_activate (activate));
2145 total_alloc += size;
2150 /* size must be a power of 2 */
2152 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2154 void *ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (activate));
2157 total_alloc += size;
2163 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
2166 mono_sgen_free_os_memory (void *addr, size_t size)
2168 mono_vfree (addr, size);
2170 total_alloc -= size;
2174 * Allocate and setup the data structures needed to be able to allocate objects
2175 * in the nursery. The nursery is stored in nursery_section.
2178 alloc_nursery (void)
2180 GCMemSection *section;
2185 if (nursery_section)
2187 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2188 /* later we will alloc a larger area for the nursery but only activate
2189 * what we need. The rest will be used as expansion if we have too many pinned
2190 * objects in the existing nursery.
2192 /* FIXME: handle OOM */
2193 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2195 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2196 alloc_size = nursery_size;
2197 #ifdef SGEN_ALIGN_NURSERY
2198 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2200 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2202 nursery_start = data;
2203 nursery_end = nursery_start + nursery_size;
2204 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
2205 DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %lu, total: %lu\n", data, data + alloc_size, (unsigned long)nursery_size, (unsigned long)total_alloc));
2206 section->data = section->next_data = data;
2207 section->size = alloc_size;
2208 section->end_data = nursery_end;
2209 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2210 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2211 section->num_scan_start = scan_starts;
2212 section->block.role = MEMORY_ROLE_GEN0;
2213 section->block.next = NULL;
2215 nursery_section = section;
2217 mono_sgen_nursery_allocator_set_nursery_bounds (nursery_start, nursery_end);
2221 mono_gc_get_nursery (int *shift_bits, size_t *size)
2223 *size = nursery_size;
2224 #ifdef SGEN_ALIGN_NURSERY
2225 *shift_bits = DEFAULT_NURSERY_BITS;
2229 return nursery_start;
2233 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
2235 SgenThreadInfo *info = mono_thread_info_current ();
2237 /* Could be called from sgen_thread_unregister () with a NULL info */
2240 info->stopped_domain = domain;
2245 mono_gc_precise_stack_mark_enabled (void)
2247 return !conservative_stack_mark;
2251 mono_gc_get_logfile (void)
2253 return mono_sgen_get_logfile ();
2257 report_finalizer_roots_list (FinalizeReadyEntry *list)
2259 GCRootReport report;
2260 FinalizeReadyEntry *fin;
2263 for (fin = list; fin; fin = fin->next) {
2266 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2268 notify_gc_roots (&report);
2272 report_finalizer_roots (void)
2274 report_finalizer_roots_list (fin_ready_list);
2275 report_finalizer_roots_list (critical_fin_list);
2278 static GCRootReport *root_report;
2281 single_arg_report_root (void **obj)
2284 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2288 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2290 switch (desc & ROOT_DESC_TYPE_MASK) {
2291 case ROOT_DESC_BITMAP:
2292 desc >>= ROOT_DESC_TYPE_SHIFT;
2294 if ((desc & 1) && *start_root) {
2295 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2301 case ROOT_DESC_COMPLEX: {
2302 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2303 int bwords = (*bitmap_data) - 1;
2304 void **start_run = start_root;
2306 while (bwords-- > 0) {
2307 gsize bmap = *bitmap_data++;
2308 void **objptr = start_run;
2310 if ((bmap & 1) && *objptr) {
2311 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2316 start_run += GC_BITS_PER_WORD;
2320 case ROOT_DESC_USER: {
2321 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2322 root_report = report;
2323 marker (start_root, single_arg_report_root);
2326 case ROOT_DESC_RUN_LEN:
2327 g_assert_not_reached ();
2329 g_assert_not_reached ();
2334 report_registered_roots_by_type (int root_type)
2336 GCRootReport report;
2340 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
2341 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", start_root, root->end_root, (void*)root->root_desc));
2342 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
2343 } SGEN_HASH_TABLE_FOREACH_END;
2344 notify_gc_roots (&report);
2348 report_registered_roots (void)
2350 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2351 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2355 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeReadyEntry *list, GrayQueue *queue)
2357 FinalizeReadyEntry *fin;
2359 for (fin = list; fin; fin = fin->next) {
2362 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2363 copy_func (&fin->object, queue);
2368 generation_name (int generation)
2370 switch (generation) {
2371 case GENERATION_NURSERY: return "nursery";
2372 case GENERATION_OLD: return "old";
2373 default: g_assert_not_reached ();
2377 static MonoObject **finalized_array = NULL;
2378 static int finalized_array_capacity = 0;
2379 static int finalized_array_entries = 0;
2382 bridge_register_finalized_object (MonoObject *object)
2384 if (!finalized_array)
2387 if (finalized_array_entries >= finalized_array_capacity) {
2388 MonoObject **new_array;
2389 g_assert (finalized_array_entries == finalized_array_capacity);
2390 finalized_array_capacity *= 2;
2391 new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2392 memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
2393 mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
2394 finalized_array = new_array;
2396 finalized_array [finalized_array_entries++] = object;
2400 bridge_process (void)
2402 if (finalized_array_entries <= 0)
2405 g_assert (mono_sgen_need_bridge_processing ());
2406 mono_sgen_bridge_processing_finish (finalized_array_entries, finalized_array);
2408 finalized_array_entries = 0;
2411 CopyOrMarkObjectFunc
2412 mono_sgen_get_copy_object (void)
2414 if (current_collection_generation == GENERATION_NURSERY) {
2415 if (collection_is_parallel ())
2416 return major_collector.copy_object;
2418 return major_collector.nopar_copy_object;
2420 return major_collector.copy_or_mark_object;
2425 mono_sgen_get_minor_scan_object (void)
2427 g_assert (current_collection_generation == GENERATION_NURSERY);
2429 if (collection_is_parallel ())
2430 return major_collector.minor_scan_object;
2432 return major_collector.nopar_minor_scan_object;
2436 mono_sgen_get_minor_scan_vtype (void)
2438 g_assert (current_collection_generation == GENERATION_NURSERY);
2440 if (collection_is_parallel ())
2441 return major_collector.minor_scan_vtype;
2443 return major_collector.nopar_minor_scan_vtype;
2447 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2452 int done_with_ephemerons, ephemeron_rounds = 0;
2454 CopyOrMarkObjectFunc copy_func = mono_sgen_get_copy_object ();
2457 * We copied all the reachable objects. Now it's the time to copy
2458 * the objects that were not referenced by the roots, but by the copied objects.
2459 * we built a stack of objects pointed to by gray_start: they are
2460 * additional roots and we may add more items as we go.
2461 * We loop until gray_start == gray_objects which means no more objects have
2462 * been added. Note this is iterative: no recursion is involved.
2463 * We need to walk the LO list as well in search of marked big objects
2464 * (use a flag since this is needed only on major collections). We need to loop
2465 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2466 * To achieve better cache locality and cache usage, we drain the gray stack
2467 * frequently, after each object is copied, and just finish the work here.
2469 drain_gray_stack (queue, -1);
2471 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2474 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2475 * before processing finalizable objects or non-tracking weak hamdle to avoid finalizing/clearing
2476 * objects that are in fact reachable.
2478 done_with_ephemerons = 0;
2480 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2481 drain_gray_stack (queue, -1);
2483 } while (!done_with_ephemerons);
2485 mono_sgen_scan_togglerefs (copy_func, start_addr, end_addr, queue);
2486 if (generation == GENERATION_OLD)
2487 mono_sgen_scan_togglerefs (copy_func, nursery_start, nursery_end, queue);
2489 if (mono_sgen_need_bridge_processing ()) {
2490 if (finalized_array == NULL) {
2491 finalized_array_capacity = 32;
2492 finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2494 finalized_array_entries = 0;
2496 collect_bridge_objects (copy_func, start_addr, end_addr, generation, queue);
2497 if (generation == GENERATION_OLD)
2498 collect_bridge_objects (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
2500 if (finalized_array_entries > 0)
2501 mono_sgen_bridge_processing_start (finalized_array_entries, finalized_array);
2502 drain_gray_stack (queue, -1);
2506 We must clear weak links that don't track resurrection before processing object ready for
2507 finalization so they can be cleared before that.
2509 null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
2510 if (generation == GENERATION_OLD)
2511 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
2514 /* walk the finalization queue and move also the objects that need to be
2515 * finalized: use the finalized objects as new roots so the objects they depend
2516 * on are also not reclaimed. As with the roots above, only objects in the nursery
2517 * are marked/copied.
2518 * We need a loop here, since objects ready for finalizers may reference other objects
2519 * that are fin-ready. Speedup with a flag?
2523 fin_ready = num_ready_finalizers;
2524 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2525 if (generation == GENERATION_OLD)
2526 finalize_in_range (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
2528 if (fin_ready != num_ready_finalizers)
2531 /* drain the new stack that might have been created */
2532 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2533 drain_gray_stack (queue, -1);
2534 } while (fin_ready != num_ready_finalizers);
2536 if (mono_sgen_need_bridge_processing ())
2537 g_assert (num_loops <= 1);
2540 * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
2542 done_with_ephemerons = 0;
2544 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2545 drain_gray_stack (queue, -1);
2547 } while (!done_with_ephemerons);
2550 * Clear ephemeron pairs with unreachable keys.
2551 * We pass the copy func so we can figure out if an array was promoted or not.
2553 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2556 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));
2559 * handle disappearing links
2560 * Note we do this after checking the finalization queue because if an object
2561 * survives (at least long enough to be finalized) we don't clear the link.
2562 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2563 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2566 g_assert (gray_object_queue_is_empty (queue));
2568 null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
2569 if (generation == GENERATION_OLD)
2570 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
2571 if (gray_object_queue_is_empty (queue))
2573 drain_gray_stack (queue, -1);
2576 g_assert (gray_object_queue_is_empty (queue));
2580 mono_sgen_check_section_scan_starts (GCMemSection *section)
2583 for (i = 0; i < section->num_scan_start; ++i) {
2584 if (section->scan_starts [i]) {
2585 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2586 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2592 check_scan_starts (void)
2594 if (!do_scan_starts_check)
2596 mono_sgen_check_section_scan_starts (nursery_section);
2597 major_collector.check_scan_starts ();
2600 static int last_num_pinned = 0;
2603 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2607 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
2608 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", start_root, root->end_root, (void*)root->root_desc));
2609 precisely_scan_objects_from (copy_func, start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2610 } SGEN_HASH_TABLE_FOREACH_END;
2614 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2616 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2620 mono_sgen_dump_section (GCMemSection *section, const char *type)
2622 char *start = section->data;
2623 char *end = section->data + section->size;
2624 char *occ_start = NULL;
2626 char *old_start = NULL; /* just for debugging */
2628 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2630 while (start < end) {
2634 if (!*(void**)start) {
2636 mono_sgen_dump_occupied (occ_start, start, section->data);
2639 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2642 g_assert (start < section->next_data);
2647 vt = (GCVTable*)LOAD_VTABLE (start);
2650 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2653 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2654 start - section->data,
2655 vt->klass->name_space, vt->klass->name,
2663 mono_sgen_dump_occupied (occ_start, start, section->data);
2665 fprintf (heap_dump_file, "</section>\n");
2669 dump_object (MonoObject *obj, gboolean dump_location)
2671 static char class_name [1024];
2673 MonoClass *class = mono_object_class (obj);
2677 * Python's XML parser is too stupid to parse angle brackets
2678 * in strings, so we just ignore them;
2681 while (class->name [i] && j < sizeof (class_name) - 1) {
2682 if (!strchr ("<>\"", class->name [i]))
2683 class_name [j++] = class->name [i];
2686 g_assert (j < sizeof (class_name));
2689 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2690 class->name_space, class_name,
2691 safe_object_get_size (obj));
2692 if (dump_location) {
2693 const char *location;
2694 if (ptr_in_nursery (obj))
2695 location = "nursery";
2696 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2700 fprintf (heap_dump_file, " location=\"%s\"", location);
2702 fprintf (heap_dump_file, "/>\n");
2706 dump_heap (const char *type, int num, const char *reason)
2711 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2713 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2714 fprintf (heap_dump_file, ">\n");
2715 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2716 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2717 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2718 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2719 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2721 fprintf (heap_dump_file, "<pinned-objects>\n");
2722 for (list = pinned_objects; list; list = list->next)
2723 dump_object (list->obj, TRUE);
2724 fprintf (heap_dump_file, "</pinned-objects>\n");
2726 mono_sgen_dump_section (nursery_section, "nursery");
2728 major_collector.dump_heap (heap_dump_file);
2730 fprintf (heap_dump_file, "<los>\n");
2731 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2732 dump_object ((MonoObject*)bigobj->data, FALSE);
2733 fprintf (heap_dump_file, "</los>\n");
2735 fprintf (heap_dump_file, "</collection>\n");
2739 mono_sgen_register_moved_object (void *obj, void *destination)
2741 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2743 /* FIXME: handle this for parallel collector */
2744 g_assert (!collection_is_parallel ());
2746 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2747 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2748 moved_objects_idx = 0;
2750 moved_objects [moved_objects_idx++] = obj;
2751 moved_objects [moved_objects_idx++] = destination;
2757 static gboolean inited = FALSE;
2762 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2763 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2764 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2765 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2766 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2767 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2768 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2769 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2770 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2772 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2773 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2774 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2775 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2776 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2777 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2778 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2779 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2780 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2781 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2782 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2783 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2784 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2786 mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2788 #ifdef HEAVY_STATISTICS
2789 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2790 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2791 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2792 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2793 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2794 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2795 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2796 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2798 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2799 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2800 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2801 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2802 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2804 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2805 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2806 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2807 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2809 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2810 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2812 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2813 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2814 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2816 mono_sgen_nursery_allocator_init_heavy_stats ();
2818 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2819 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2820 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2821 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2822 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2823 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2824 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2825 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2826 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2832 static gboolean need_calculate_minor_collection_allowance;
2834 static int last_collection_old_num_major_sections;
2835 static mword last_collection_los_memory_usage = 0;
2836 static mword last_collection_old_los_memory_usage;
2837 static mword last_collection_los_memory_alloced;
2840 reset_minor_collection_allowance (void)
2842 need_calculate_minor_collection_allowance = TRUE;
2846 try_calculate_minor_collection_allowance (gboolean overwrite)
2848 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2849 mword los_memory_saved, new_major, new_heap_size;
2852 g_assert (need_calculate_minor_collection_allowance);
2854 if (!need_calculate_minor_collection_allowance)
2857 if (!*major_collector.have_swept) {
2859 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2863 num_major_sections = major_collector.get_num_major_sections ();
2865 num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
2866 los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
2868 new_major = num_major_sections * major_collector.section_size;
2869 new_heap_size = new_major + last_collection_los_memory_usage;
2872 * FIXME: Why is save_target half the major memory plus half the
2873 * LOS memory saved? Shouldn't it be half the major memory
2874 * saved plus half the LOS memory saved? Or half the whole heap
2877 save_target = (new_major + los_memory_saved) / 2;
2880 * We aim to allow the allocation of as many sections as is
2881 * necessary to reclaim save_target sections in the next
2882 * collection. We assume the collection pattern won't change.
2883 * In the last cycle, we had num_major_sections_saved for
2884 * minor_collection_sections_alloced. Assuming things won't
2885 * change, this must be the same ratio as save_target for
2886 * allowance_target, i.e.
2888 * num_major_sections_saved save_target
2889 * --------------------------------- == ----------------
2890 * minor_collection_sections_alloced allowance_target
2894 allowance_target = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major_collector.section_size + last_collection_los_memory_alloced) / (double)(num_major_sections_saved * major_collector.section_size + los_memory_saved));
2896 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2898 if (new_heap_size + minor_collection_allowance > soft_heap_limit) {
2899 if (new_heap_size > soft_heap_limit)
2900 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2902 minor_collection_allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
2905 if (debug_print_allowance) {
2906 mword old_major = last_collection_old_num_major_sections * major_collector.section_size;
2908 fprintf (gc_debug_file, "Before collection: %ld bytes (%ld major, %ld LOS)\n",
2909 old_major + last_collection_old_los_memory_usage, old_major, last_collection_old_los_memory_usage);
2910 fprintf (gc_debug_file, "After collection: %ld bytes (%ld major, %ld LOS)\n",
2911 new_heap_size, new_major, last_collection_los_memory_usage);
2912 fprintf (gc_debug_file, "Allowance: %ld bytes\n", minor_collection_allowance);
2915 if (major_collector.have_computed_minor_collection_allowance)
2916 major_collector.have_computed_minor_collection_allowance ();
2918 need_calculate_minor_collection_allowance = FALSE;
2922 need_major_collection (mword space_needed)
2924 mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
2925 return (space_needed > available_free_space ()) ||
2926 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
2930 mono_sgen_need_major_collection (mword space_needed)
2932 return need_major_collection (space_needed);
2936 reset_pinned_from_failed_allocation (void)
2938 bytes_pinned_from_failed_allocation = 0;
2942 mono_sgen_set_pinned_from_failed_allocation (mword objsize)
2944 bytes_pinned_from_failed_allocation += objsize;
2948 collection_is_parallel (void)
2950 switch (current_collection_generation) {
2951 case GENERATION_NURSERY:
2952 return nursery_collection_is_parallel;
2953 case GENERATION_OLD:
2954 return major_collector.is_parallel;
2956 g_assert_not_reached ();
2961 mono_sgen_nursery_collection_is_parallel (void)
2963 return nursery_collection_is_parallel;
2967 job_gray_queue (WorkerData *worker_data)
2969 return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
2976 } ScanFromRemsetsJobData;
2979 job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
2981 ScanFromRemsetsJobData *job_data = job_data_untyped;
2983 scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
2988 CopyOrMarkObjectFunc func;
2992 } ScanFromRegisteredRootsJobData;
2995 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
2997 ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
2999 scan_from_registered_roots (job_data->func,
3000 job_data->heap_start, job_data->heap_end,
3001 job_data->root_type,
3002 job_gray_queue (worker_data));
3009 } ScanThreadDataJobData;
3012 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
3014 ScanThreadDataJobData *job_data = job_data_untyped;
3016 scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
3017 job_gray_queue (worker_data));
3021 * Collect objects in the nursery. Returns whether to trigger a major
3025 collect_nursery (size_t requested_size)
3027 gboolean needs_major;
3028 size_t max_garbage_amount;
3030 ScanFromRemsetsJobData sfrjd;
3031 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3032 ScanThreadDataJobData stdjd;
3033 mword fragment_total;
3034 TV_DECLARE (all_atv);
3035 TV_DECLARE (all_btv);
3039 if (disable_minor_collections)
3042 mono_perfcounters->gc_collections0++;
3044 current_collection_generation = GENERATION_NURSERY;
3046 reset_pinned_from_failed_allocation ();
3048 binary_protocol_collection (GENERATION_NURSERY);
3049 check_scan_starts ();
3053 nursery_next = mono_sgen_nursery_alloc_get_upper_alloc_bound ();
3054 /* FIXME: optimize later to use the higher address where an object can be present */
3055 nursery_next = MAX (nursery_next, nursery_end);
3057 nursery_alloc_bound = nursery_next;
3059 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)));
3060 max_garbage_amount = nursery_next - nursery_start;
3061 g_assert (nursery_section->size >= max_garbage_amount);
3063 /* world must be stopped already */
3064 TV_GETTIME (all_atv);
3067 /* Pinning no longer depends on clearing all nursery fragments */
3068 mono_sgen_clear_current_nursery_fragment ();
3071 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3074 check_for_xdomain_refs ();
3076 nursery_section->next_data = nursery_next;
3078 major_collector.start_nursery_collection ();
3080 try_calculate_minor_collection_allowance (FALSE);
3082 gray_object_queue_init (&gray_queue);
3083 workers_init_distribute_gray_queue ();
3086 mono_stats.minor_gc_count ++;
3088 global_remset_cache_clear ();
3090 process_fin_stage_entries ();
3091 process_dislink_stage_entries ();
3093 /* pin from pinned handles */
3095 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
3096 pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3097 /* identify pinned objects */
3098 optimize_pin_queue (0);
3099 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3100 nursery_section->pin_queue_start = pin_queue;
3101 nursery_section->pin_queue_num_entries = next_pin_slot;
3103 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3104 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3105 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3107 if (consistency_check_at_minor_collection)
3108 check_consistency ();
3110 workers_start_all_workers ();
3113 * Walk all the roots and copy the young objects to the old
3114 * generation, starting from to_space.
3116 * The global remsets must be processed before the workers start
3117 * marking because they might add global remsets.
3119 scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3121 workers_start_marking ();
3123 sfrjd.heap_start = nursery_start;
3124 sfrjd.heap_end = nursery_next;
3125 workers_enqueue_job (job_scan_from_remsets, &sfrjd);
3127 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3129 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3130 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3132 if (use_cardtable) {
3134 card_tables_collect_stats (TRUE);
3135 scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3137 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
3140 if (!collection_is_parallel ())
3141 drain_gray_stack (&gray_queue, -1);
3143 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3144 report_registered_roots ();
3145 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3146 report_finalizer_roots ();
3148 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3150 /* registered roots, this includes static fields */
3151 scrrjd_normal.func = collection_is_parallel () ? major_collector.copy_object : major_collector.nopar_copy_object;
3152 scrrjd_normal.heap_start = nursery_start;
3153 scrrjd_normal.heap_end = nursery_next;
3154 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3155 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
3157 scrrjd_wbarrier.func = collection_is_parallel () ? major_collector.copy_object : major_collector.nopar_copy_object;
3158 scrrjd_wbarrier.heap_start = nursery_start;
3159 scrrjd_wbarrier.heap_end = nursery_next;
3160 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3161 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
3164 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3167 stdjd.heap_start = nursery_start;
3168 stdjd.heap_end = nursery_next;
3169 workers_enqueue_job (job_scan_thread_data, &stdjd);
3172 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3175 if (collection_is_parallel ()) {
3176 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3177 workers_distribute_gray_queue_sections ();
3183 if (collection_is_parallel ())
3184 g_assert (gray_object_queue_is_empty (&gray_queue));
3186 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3188 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3189 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3192 * The (single-threaded) finalization code might have done
3193 * some copying/marking so we can only reset the GC thread's
3194 * worker data here instead of earlier when we joined the
3197 if (major_collector.reset_worker_data)
3198 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3200 if (objects_pinned) {
3201 evacuate_pin_staging_area ();
3202 optimize_pin_queue (0);
3203 nursery_section->pin_queue_start = pin_queue;
3204 nursery_section->pin_queue_num_entries = next_pin_slot;
3207 /* walk the pin_queue, build up the fragment list of free memory, unmark
3208 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3211 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3212 fragment_total = mono_sgen_build_nursery_fragments (nursery_section, pin_queue, next_pin_slot);
3213 if (!fragment_total)
3216 /* Clear TLABs for all threads */
3219 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3221 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3222 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3224 if (consistency_check_at_minor_collection)
3225 check_major_refs ();
3227 major_collector.finish_nursery_collection ();
3229 TV_GETTIME (all_btv);
3230 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3233 dump_heap ("minor", num_minor_gcs - 1, NULL);
3235 /* prepare the pin queue for the next collection */
3236 last_num_pinned = next_pin_slot;
3238 if (fin_ready_list || critical_fin_list) {
3239 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3240 mono_gc_finalize_notify ();
3244 g_assert (gray_object_queue_is_empty (&gray_queue));
3247 card_tables_collect_stats (FALSE);
3249 check_scan_starts ();
3251 binary_protocol_flush_buffers (FALSE);
3253 /*objects are late pinned because of lack of memory, so a major is a good call*/
3254 needs_major = need_major_collection (0) || objects_pinned;
3255 current_collection_generation = -1;
3263 FinalizeReadyEntry *list;
3264 } ScanFinalizerEntriesJobData;
3267 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
3269 ScanFinalizerEntriesJobData *job_data = job_data_untyped;
3271 scan_finalizer_entries (major_collector.copy_or_mark_object,
3273 job_gray_queue (worker_data));
3277 major_do_collection (const char *reason)
3279 LOSObject *bigobj, *prevbo;
3280 TV_DECLARE (all_atv);
3281 TV_DECLARE (all_btv);
3284 /* FIXME: only use these values for the precise scan
3285 * note that to_space pointers should be excluded anyway...
3287 char *heap_start = NULL;
3288 char *heap_end = (char*)-1;
3289 int old_next_pin_slot;
3290 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3291 ScanThreadDataJobData stdjd;
3292 ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
3294 mono_perfcounters->gc_collections1++;
3296 reset_pinned_from_failed_allocation ();
3298 last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3301 * A domain could have been freed, resulting in
3302 * los_memory_usage being less than last_collection_los_memory_usage.
3304 last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3305 last_collection_old_los_memory_usage = los_memory_usage;
3308 //count_ref_nonref_objs ();
3309 //consistency_check ();
3311 binary_protocol_collection (GENERATION_OLD);
3312 check_scan_starts ();
3313 gray_object_queue_init (&gray_queue);
3314 workers_init_distribute_gray_queue ();
3317 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3319 mono_stats.major_gc_count ++;
3321 /* world must be stopped already */
3322 TV_GETTIME (all_atv);
3325 /* Pinning depends on this */
3326 mono_sgen_clear_nursery_fragments ();
3329 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3331 nursery_section->next_data = nursery_end;
3332 /* we should also coalesce scanning from sections close to each other
3333 * and deal with pointers outside of the sections later.
3336 if (major_collector.start_major_collection)
3337 major_collector.start_major_collection ();
3339 *major_collector.have_swept = FALSE;
3340 reset_minor_collection_allowance ();
3343 check_for_xdomain_refs ();
3345 /* The remsets are not useful for a major collection */
3347 global_remset_cache_clear ();
3349 card_table_clear ();
3351 process_fin_stage_entries ();
3352 process_dislink_stage_entries ();
3356 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3357 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3358 optimize_pin_queue (0);
3361 * pin_queue now contains all candidate pointers, sorted and
3362 * uniqued. We must do two passes now to figure out which
3363 * objects are pinned.
3365 * The first is to find within the pin_queue the area for each
3366 * section. This requires that the pin_queue be sorted. We
3367 * also process the LOS objects and pinned chunks here.
3369 * The second, destructive, pass is to reduce the section
3370 * areas to pointers to the actually pinned objects.
3372 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3373 /* first pass for the sections */
3374 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3375 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3376 /* identify possible pointers to the insize of large objects */
3377 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3378 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3380 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3381 binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (bigobj->data));
3382 pin_object (bigobj->data);
3383 /* FIXME: only enqueue if object has references */
3384 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3386 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3387 DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %lu from roots\n", bigobj->data, safe_name (bigobj->data), (unsigned long)bigobj->size));
3390 /* second pass for the sections */
3391 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3392 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3393 old_next_pin_slot = next_pin_slot;
3396 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3397 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3398 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3400 major_collector.init_to_space ();
3402 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3403 main_gc_thread = mono_native_thread_self ();
3406 workers_start_all_workers ();
3407 workers_start_marking ();
3409 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3410 report_registered_roots ();
3412 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3414 /* registered roots, this includes static fields */
3415 scrrjd_normal.func = major_collector.copy_or_mark_object;
3416 scrrjd_normal.heap_start = heap_start;
3417 scrrjd_normal.heap_end = heap_end;
3418 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3419 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
3421 scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
3422 scrrjd_wbarrier.heap_start = heap_start;
3423 scrrjd_wbarrier.heap_end = heap_end;
3424 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3425 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
3428 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3431 stdjd.heap_start = heap_start;
3432 stdjd.heap_end = heap_end;
3433 workers_enqueue_job (job_scan_thread_data, &stdjd);
3436 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3439 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3441 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3442 report_finalizer_roots ();
3444 /* scan the list of objects ready for finalization */
3445 sfejd_fin_ready.list = fin_ready_list;
3446 workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
3448 sfejd_critical_fin.list = critical_fin_list;
3449 workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
3452 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3453 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3456 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3458 if (major_collector.is_parallel) {
3459 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3460 workers_distribute_gray_queue_sections ();
3466 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3467 main_gc_thread = NULL;
3470 if (major_collector.is_parallel)
3471 g_assert (gray_object_queue_is_empty (&gray_queue));
3473 /* all the objects in the heap */
3474 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3476 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3479 * The (single-threaded) finalization code might have done
3480 * some copying/marking so we can only reset the GC thread's
3481 * worker data here instead of earlier when we joined the
3484 if (major_collector.reset_worker_data)
3485 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3487 if (objects_pinned) {
3488 /*This is slow, but we just OOM'd*/
3489 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3490 evacuate_pin_staging_area ();
3491 optimize_pin_queue (0);
3492 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3496 reset_heap_boundaries ();
3497 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
3499 /* sweep the big objects list */
3501 for (bigobj = los_object_list; bigobj;) {
3502 if (object_is_pinned (bigobj->data)) {
3503 unpin_object (bigobj->data);
3504 mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3507 /* not referenced anywhere, so we can free it */
3509 prevbo->next = bigobj->next;
3511 los_object_list = bigobj->next;
3513 bigobj = bigobj->next;
3514 mono_sgen_los_free_object (to_free);
3518 bigobj = bigobj->next;
3522 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3524 mono_sgen_los_sweep ();
3527 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3529 major_collector.sweep ();
3532 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3534 /* walk the pin_queue, build up the fragment list of free memory, unmark
3535 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3538 if (!mono_sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries))
3541 /* Clear TLABs for all threads */
3545 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3547 TV_GETTIME (all_btv);
3548 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3551 dump_heap ("major", num_major_gcs - 1, reason);
3553 /* prepare the pin queue for the next collection */
3555 if (fin_ready_list || critical_fin_list) {
3556 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3557 mono_gc_finalize_notify ();
3561 g_assert (gray_object_queue_is_empty (&gray_queue));
3563 try_calculate_minor_collection_allowance (TRUE);
3565 minor_collection_sections_alloced = 0;
3566 last_collection_los_memory_usage = los_memory_usage;
3568 major_collector.finish_major_collection ();
3570 check_scan_starts ();
3572 binary_protocol_flush_buffers (FALSE);
3574 //consistency_check ();
3576 return bytes_pinned_from_failed_allocation > 0;
3580 major_collection (const char *reason)
3582 gboolean need_minor_collection;
3584 if (disable_major_collections) {
3585 collect_nursery (0);
3589 major_collection_happened = TRUE;
3590 current_collection_generation = GENERATION_OLD;
3591 need_minor_collection = major_do_collection (reason);
3592 current_collection_generation = -1;
3594 if (need_minor_collection)
3595 collect_nursery (0);
3599 sgen_collect_major_no_lock (const char *reason)
3601 gint64 gc_start_time;
3603 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3604 gc_start_time = mono_100ns_ticks ();
3606 major_collection (reason);
3608 mono_trace_message (MONO_TRACE_GC, "major gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
3609 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3613 * When deciding if it's better to collect or to expand, keep track
3614 * of how much garbage was reclaimed with the last collection: if it's too
3616 * This is called when we could not allocate a small object.
3618 static void __attribute__((noinline))
3619 minor_collect_or_expand_inner (size_t size)
3621 int do_minor_collection = 1;
3623 g_assert (nursery_section);
3624 if (do_minor_collection) {
3625 gint64 total_gc_time, major_gc_time = 0;
3627 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3628 total_gc_time = mono_100ns_ticks ();
3631 if (collect_nursery (size)) {
3632 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3633 major_gc_time = mono_100ns_ticks ();
3635 major_collection ("minor overflow");
3637 /* keep events symmetric */
3638 major_gc_time = mono_100ns_ticks () - major_gc_time;
3639 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3641 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3644 total_gc_time = mono_100ns_ticks () - total_gc_time;
3646 mono_trace_message (MONO_TRACE_GC, "overflow major gc took %d usecs minor gc took %d usecs", total_gc_time / 10, (total_gc_time - major_gc_time) / 10);
3648 mono_trace_message (MONO_TRACE_GC, "minor gc took %d usecs", total_gc_time / 10);
3650 /* this also sets the proper pointers for the next allocation */
3651 if (!mono_sgen_can_alloc_size (size)) {
3653 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3654 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3655 for (i = 0; i < last_num_pinned; ++i) {
3656 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])));
3660 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3662 //report_internal_mem_usage ();
3666 * ######################################################################
3667 * ######## Memory allocation from the OS
3668 * ######################################################################
3669 * This section of code deals with getting memory from the OS and
3670 * allocating memory for GC-internal data structures.
3671 * Internal memory can be handled with a freelist for small objects.
3677 G_GNUC_UNUSED static void
3678 report_internal_mem_usage (void)
3680 printf ("Internal memory usage:\n");
3681 mono_sgen_report_internal_mem_usage ();
3682 printf ("Pinned memory usage:\n");
3683 major_collector.report_pinned_memory_usage ();
3687 * ######################################################################
3688 * ######## Object allocation
3689 * ######################################################################
3690 * This section of code deals with allocating memory for objects.
3691 * There are several ways:
3692 * *) allocate large objects
3693 * *) allocate normal objects
3694 * *) fast lock-free allocation
3695 * *) allocation of pinned objects
3699 alloc_degraded (MonoVTable *vtable, size_t size, gboolean for_mature)
3701 static int last_major_gc_warned = -1;
3702 static int num_degraded = 0;
3705 if (last_major_gc_warned < num_major_gcs) {
3707 if (num_degraded == 1 || num_degraded == 3)
3708 fprintf (stderr, "Warning: Degraded allocation. Consider increasing nursery-size if the warning persists.\n");
3709 else if (num_degraded == 10)
3710 fprintf (stderr, "Warning: Repeated degraded allocation. Consider increasing nursery-size.\n");
3711 last_major_gc_warned = num_major_gcs;
3715 if (need_major_collection (0)) {
3716 gint64 gc_start_time;
3718 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3719 gc_start_time = mono_100ns_ticks ();
3722 major_collection ("degraded overflow");
3725 mono_trace_message (MONO_TRACE_GC, "major gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
3726 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3729 return major_collector.alloc_degraded (vtable, size);
3733 * Provide a variant that takes just the vtable for small fixed-size objects.
3734 * The aligned size is already computed and stored in vt->gc_descr.
3735 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3736 * processing. We can keep track of where objects start, for example,
3737 * so when we scan the thread stacks for pinned objects, we can start
3738 * a search for the pinned object in SCAN_START_SIZE chunks.
3741 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3743 /* FIXME: handle OOM */
3748 HEAVY_STAT (++stat_objects_alloced);
3749 if (size <= MAX_SMALL_OBJ_SIZE)
3750 HEAVY_STAT (stat_bytes_alloced += size);
3752 HEAVY_STAT (stat_bytes_alloced_los += size);
3754 size = ALIGN_UP (size);
3756 g_assert (vtable->gc_descr);
3758 if (G_UNLIKELY (collect_before_allocs)) {
3759 static int alloc_count;
3761 InterlockedIncrement (&alloc_count);
3762 if (((alloc_count % collect_before_allocs) == 0) && nursery_section) {
3763 gint64 gc_start_time;
3765 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3766 gc_start_time = mono_100ns_ticks ();
3769 collect_nursery (0);
3772 mono_trace_message (MONO_TRACE_GC, "minor gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
3773 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3774 if (!degraded_mode && !mono_sgen_can_alloc_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
3776 g_assert_not_reached ();
3782 * We must already have the lock here instead of after the
3783 * fast path because we might be interrupted in the fast path
3784 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3785 * and we'll end up allocating an object in a fragment which
3786 * no longer belongs to us.
3788 * The managed allocator does not do this, but it's treated
3789 * specially by the world-stopping code.
3792 if (size > MAX_SMALL_OBJ_SIZE) {
3793 p = mono_sgen_los_alloc_large_inner (vtable, size);
3795 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3797 p = (void**)TLAB_NEXT;
3798 /* FIXME: handle overflow */
3799 new_next = (char*)p + size;
3800 TLAB_NEXT = new_next;
3802 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3806 * FIXME: We might need a memory barrier here so the change to tlab_next is
3807 * visible before the vtable store.
3810 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3811 binary_protocol_alloc (p , vtable, size);
3812 g_assert (*p == NULL);
3813 mono_atomic_store_seq (p, vtable);
3820 /* there are two cases: the object is too big or we run out of space in the TLAB */
3821 /* we also reach here when the thread does its first allocation after a minor
3822 * collection, since the tlab_ variables are initialized to NULL.
3823 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3824 * objects that need finalizers can have the high bit set in their size
3825 * so the above check fails and we can readily add the object to the queue.
3826 * This avoids taking again the GC lock when registering, but this is moot when
3827 * doing thread-local allocation, so it may not be a good idea.
3829 if (TLAB_NEXT >= TLAB_REAL_END) {
3830 int available_in_tlab;
3832 * Run out of space in the TLAB. When this happens, some amount of space
3833 * remains in the TLAB, but not enough to satisfy the current allocation
3834 * request. Currently, we retire the TLAB in all cases, later we could
3835 * keep it if the remaining space is above a treshold, and satisfy the
3836 * allocation directly from the nursery.
3839 /* when running in degraded mode, we continue allocing that way
3840 * for a while, to decrease the number of useless nursery collections.
3842 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3843 p = alloc_degraded (vtable, size, FALSE);
3844 binary_protocol_alloc_degraded (p, vtable, size);
3848 available_in_tlab = TLAB_REAL_END - TLAB_NEXT;
3849 if (size > tlab_size || available_in_tlab > SGEN_MAX_NURSERY_WASTE) {
3850 /* Allocate directly from the nursery */
3852 p = mono_sgen_nursery_alloc (size);
3854 minor_collect_or_expand_inner (size);
3855 if (degraded_mode) {
3856 p = alloc_degraded (vtable, size, FALSE);
3857 binary_protocol_alloc_degraded (p, vtable, size);
3860 p = mono_sgen_nursery_alloc (size);
3869 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3870 memset (p, 0, size);
3875 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3876 mono_sgen_nursery_retire_region (p, available_in_tlab);
3879 p = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3881 minor_collect_or_expand_inner (tlab_size);
3882 if (degraded_mode) {
3883 p = alloc_degraded (vtable, size, FALSE);
3884 binary_protocol_alloc_degraded (p, vtable, size);
3887 p = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3897 /* Allocate a new TLAB from the current nursery fragment */
3898 TLAB_START = (char*)p;
3899 TLAB_NEXT = TLAB_START;
3900 TLAB_REAL_END = TLAB_START + alloc_size;
3901 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3903 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3904 memset (TLAB_START, 0, alloc_size);
3907 /* Allocate from the TLAB */
3908 p = (void*)TLAB_NEXT;
3911 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3914 /* Reached tlab_temp_end */
3916 /* record the scan start so we can find pinned objects more easily */
3917 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3918 /* we just bump tlab_temp_end as well */
3919 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3920 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3925 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3926 binary_protocol_alloc (p, vtable, size);
3927 mono_atomic_store_seq (p, vtable);
3934 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3940 size = ALIGN_UP (size);
3942 g_assert (vtable->gc_descr);
3943 if (size > MAX_SMALL_OBJ_SIZE)
3946 if (G_UNLIKELY (size > tlab_size)) {
3947 /* Allocate directly from the nursery */
3948 p = mono_sgen_nursery_alloc (size);
3952 /*FIXME we should use weak memory ops here. Should help specially on x86. */
3953 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3954 memset (p, 0, size);
3956 int available_in_tlab;
3958 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3960 p = (void**)TLAB_NEXT;
3961 /* FIXME: handle overflow */
3962 new_next = (char*)p + size;
3964 real_end = TLAB_REAL_END;
3965 available_in_tlab = real_end - (char*)p;
3967 if (G_LIKELY (new_next < real_end)) {
3968 TLAB_NEXT = new_next;
3969 } else if (available_in_tlab > SGEN_MAX_NURSERY_WASTE) {
3970 /* Allocate directly from the nursery */
3971 p = mono_sgen_nursery_alloc (size);
3975 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3976 memset (p, 0, size);
3980 mono_sgen_nursery_retire_region (p, available_in_tlab);
3981 new_next = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3982 p = (void**)new_next;
3986 TLAB_START = (char*)new_next;
3987 TLAB_NEXT = new_next + size;
3988 TLAB_REAL_END = new_next + alloc_size;
3989 TLAB_TEMP_END = new_next + MIN (SCAN_START_SIZE, alloc_size);
3991 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3992 memset (new_next, 0, alloc_size);
3996 /* Second case, we overflowed temp end */
3997 if (G_UNLIKELY (new_next >= TLAB_TEMP_END)) {
3998 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3999 /* we just bump tlab_temp_end as well */
4000 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4001 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4005 HEAVY_STAT (++stat_objects_alloced);
4006 HEAVY_STAT (stat_bytes_alloced += size);
4008 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4009 binary_protocol_alloc (p, vtable, size);
4010 g_assert (*p == NULL); /* FIXME disable this in non debug builds */
4012 mono_atomic_store_seq (p, vtable);
4018 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4021 #ifndef DISABLE_CRITICAL_REGION
4023 ENTER_CRITICAL_REGION;
4024 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4026 EXIT_CRITICAL_REGION;
4029 EXIT_CRITICAL_REGION;
4032 res = mono_gc_alloc_obj_nolock (vtable, size);
4034 if (G_UNLIKELY (!res))
4035 return mono_gc_out_of_memory (size);
4040 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4043 #ifndef DISABLE_CRITICAL_REGION
4045 ENTER_CRITICAL_REGION;
4046 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4048 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
4049 arr->max_length = max_length;
4050 EXIT_CRITICAL_REGION;
4053 EXIT_CRITICAL_REGION;
4058 arr = mono_gc_alloc_obj_nolock (vtable, size);
4059 if (G_UNLIKELY (!arr)) {
4061 return mono_gc_out_of_memory (size);
4064 arr->max_length = max_length;
4072 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4075 MonoArrayBounds *bounds;
4079 arr = mono_gc_alloc_obj_nolock (vtable, size);
4080 if (G_UNLIKELY (!arr)) {
4082 return mono_gc_out_of_memory (size);
4085 arr->max_length = max_length;
4087 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4088 arr->bounds = bounds;
4096 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4099 #ifndef DISABLE_CRITICAL_REGION
4101 ENTER_CRITICAL_REGION;
4102 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4104 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
4106 EXIT_CRITICAL_REGION;
4109 EXIT_CRITICAL_REGION;
4114 str = mono_gc_alloc_obj_nolock (vtable, size);
4115 if (G_UNLIKELY (!str)) {
4117 return mono_gc_out_of_memory (size);
4128 * To be used for interned strings and possibly MonoThread, reflection handles.
4129 * We may want to explicitly free these objects.
4132 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4135 size = ALIGN_UP (size);
4138 if (size > MAX_SMALL_OBJ_SIZE) {
4139 /* large objects are always pinned anyway */
4140 p = mono_sgen_los_alloc_large_inner (vtable, size);
4142 DEBUG (9, g_assert (vtable->klass->inited));
4143 p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
4146 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4147 binary_protocol_alloc_pinned (p, vtable, size);
4148 mono_atomic_store_seq (p, vtable);
4155 mono_gc_alloc_mature (MonoVTable *vtable)
4158 size_t size = ALIGN_UP (vtable->klass->instance_size);
4160 res = alloc_degraded (vtable, size, TRUE);
4161 mono_atomic_store_seq (res, vtable);
4163 if (G_UNLIKELY (vtable->klass->has_finalize))
4164 mono_object_register_finalizer ((MonoObject*)res);
4170 * ######################################################################
4171 * ######## Finalization support
4172 * ######################################################################
4176 * this is valid for the nursery: if the object has been forwarded it means it's
4177 * still refrenced from a root. If it is pinned it's still alive as well.
4178 * Return TRUE if @obj is ready to be finalized.
4180 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4184 mono_sgen_gc_is_object_ready_for_finalization (void *object)
4186 return !major_collector.is_object_live (object) && object_is_fin_ready (object);
4190 has_critical_finalizer (MonoObject *obj)
4194 if (!mono_defaults.critical_finalizer_object)
4197 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4199 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4203 queue_finalization_entry (MonoObject *obj) {
4204 FinalizeReadyEntry *entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
4205 entry->object = obj;
4206 if (has_critical_finalizer (obj)) {
4207 entry->next = critical_fin_list;
4208 critical_fin_list = entry;
4210 entry->next = fin_ready_list;
4211 fin_ready_list = entry;
4216 object_is_reachable (char *object, char *start, char *end)
4218 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4219 if (object < start || object >= end)
4221 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4224 #include "sgen-fin-weak-hash.c"
4227 mono_sgen_object_is_live (void *obj)
4229 if (ptr_in_nursery (obj))
4230 return object_is_pinned (obj);
4231 if (current_collection_generation == GENERATION_NURSERY)
4233 return major_collector.is_object_live (obj);
4236 /* LOCKING: requires that the GC lock is held */
4238 null_ephemerons_for_domain (MonoDomain *domain)
4240 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4243 MonoObject *object = (MonoObject*)current->array;
4245 if (object && !object->vtable) {
4246 EphemeronLinkNode *tmp = current;
4249 prev->next = current->next;
4251 ephemeron_list = current->next;
4253 current = current->next;
4254 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4257 current = current->next;
4262 /* LOCKING: requires that the GC lock is held */
4264 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4266 int was_in_nursery, was_promoted;
4267 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4269 Ephemeron *cur, *array_end;
4273 char *object = current->array;
4275 if (!object_is_reachable (object, start, end)) {
4276 EphemeronLinkNode *tmp = current;
4278 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4281 prev->next = current->next;
4283 ephemeron_list = current->next;
4285 current = current->next;
4286 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4291 was_in_nursery = ptr_in_nursery (object);
4292 copy_func ((void**)&object, queue);
4293 current->array = object;
4295 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4296 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4298 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4300 array = (MonoArray*)object;
4301 cur = mono_array_addr (array, Ephemeron, 0);
4302 array_end = cur + mono_array_length_fast (array);
4303 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4305 for (; cur < array_end; ++cur) {
4306 char *key = (char*)cur->key;
4308 if (!key || key == tombstone)
4311 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4312 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4313 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4315 if (!object_is_reachable (key, start, end)) {
4316 cur->key = tombstone;
4322 if (ptr_in_nursery (key)) {/*key was not promoted*/
4323 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4324 mono_sgen_add_to_global_remset (&cur->key);
4326 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4327 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4328 mono_sgen_add_to_global_remset (&cur->value);
4333 current = current->next;
4337 /* LOCKING: requires that the GC lock is held */
4339 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4341 int nothing_marked = 1;
4342 EphemeronLinkNode *current = ephemeron_list;
4344 Ephemeron *cur, *array_end;
4347 for (current = ephemeron_list; current; current = current->next) {
4348 char *object = current->array;
4349 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4351 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4352 if (object < start || object >= end)
4355 /*It has to be alive*/
4356 if (!object_is_reachable (object, start, end)) {
4357 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4361 copy_func ((void**)&object, queue);
4363 array = (MonoArray*)object;
4364 cur = mono_array_addr (array, Ephemeron, 0);
4365 array_end = cur + mono_array_length_fast (array);
4366 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4368 for (; cur < array_end; ++cur) {
4369 char *key = cur->key;
4371 if (!key || key == tombstone)
4374 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4375 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4376 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4378 if (object_is_reachable (key, start, end)) {
4379 char *value = cur->value;
4381 copy_func ((void**)&cur->key, queue);
4383 if (!object_is_reachable (value, start, end))
4385 copy_func ((void**)&cur->value, queue);
4391 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4392 return nothing_marked;
4396 mono_gc_invoke_finalizers (void)
4398 FinalizeReadyEntry *entry = NULL;
4399 gboolean entry_is_critical = FALSE;
4402 /* FIXME: batch to reduce lock contention */
4403 while (fin_ready_list || critical_fin_list) {
4407 FinalizeReadyEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4409 /* We have finalized entry in the last
4410 interation, now we need to remove it from
4413 *list = entry->next;
4415 FinalizeReadyEntry *e = *list;
4416 while (e->next != entry)
4418 e->next = entry->next;
4420 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
4424 /* Now look for the first non-null entry. */
4425 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4428 entry_is_critical = FALSE;
4430 entry_is_critical = TRUE;
4431 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4436 g_assert (entry->object);
4437 num_ready_finalizers--;
4438 obj = entry->object;
4439 entry->object = NULL;
4440 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4448 g_assert (entry->object == NULL);
4450 /* the object is on the stack so it is pinned */
4451 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4452 mono_gc_run_finalize (obj, NULL);
4459 mono_gc_pending_finalizers (void)
4461 return fin_ready_list || critical_fin_list;
4464 /* Negative value to remove */
4466 mono_gc_add_memory_pressure (gint64 value)
4468 /* FIXME: Use interlocked functions */
4470 memory_pressure += value;
4475 mono_sgen_register_major_sections_alloced (int num_sections)
4477 minor_collection_sections_alloced += num_sections;
4481 mono_sgen_get_minor_collection_allowance (void)
4483 return minor_collection_allowance;
4487 * ######################################################################
4488 * ######## registered roots support
4489 * ######################################################################
4493 * We do not coalesce roots.
4496 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4498 RootRecord new_root;
4501 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4502 RootRecord *root = mono_sgen_hash_table_lookup (&roots_hash [i], start);
4503 /* we allow changing the size and the descriptor (for thread statics etc) */
4505 size_t old_size = root->end_root - start;
4506 root->end_root = start + size;
4507 g_assert (((root->root_desc != 0) && (descr != NULL)) ||
4508 ((root->root_desc == 0) && (descr == NULL)));
4509 root->root_desc = (mword)descr;
4511 roots_size -= old_size;
4517 new_root.end_root = start + size;
4518 new_root.root_desc = (mword)descr;
4520 mono_sgen_hash_table_replace (&roots_hash [root_type], start, &new_root);
4523 DEBUG (3, fprintf (gc_debug_file, "Added root for range: %p-%p, descr: %p (%d/%d bytes)\n", start, new_root.end_root, descr, (int)size, (int)roots_size));
4530 mono_gc_register_root (char *start, size_t size, void *descr)
4532 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4536 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4538 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4542 mono_gc_deregister_root (char* addr)
4548 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4549 if (mono_sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
4550 roots_size -= (root.end_root - addr);
4556 * ######################################################################
4557 * ######## Thread handling (stop/start code)
4558 * ######################################################################
4561 unsigned int mono_sgen_global_stop_count = 0;
4564 static MonoContext cur_thread_ctx = {0};
4566 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4570 update_current_thread_stack (void *start)
4572 int stack_guard = 0;
4573 #ifndef USE_MONO_CTX
4574 void *ptr = cur_thread_regs;
4576 SgenThreadInfo *info = mono_thread_info_current ();
4578 info->stack_start = align_pointer (&stack_guard);
4579 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
4581 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
4582 info->monoctx = &cur_thread_ctx;
4584 ARCH_STORE_REGS (ptr);
4585 info->stopped_regs = ptr;
4587 if (gc_callbacks.thread_suspend_func)
4588 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
4592 mono_sgen_fill_thread_info_for_suspend (SgenThreadInfo *info)
4594 #ifdef HAVE_KW_THREAD
4595 /* update the remset info in the thread data structure */
4596 info->remset = remembered_set;
4601 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
4602 * have cross-domain checks in the write barrier.
4604 //#define XDOMAIN_CHECKS_IN_WBARRIER
4606 #ifndef SGEN_BINARY_PROTOCOL
4607 #ifndef HEAVY_STATISTICS
4608 #define MANAGED_ALLOCATION
4609 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
4610 #define MANAGED_WBARRIER
4616 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
4619 restart_threads_until_none_in_managed_allocator (void)
4621 SgenThreadInfo *info;
4622 int num_threads_died = 0;
4623 int sleep_duration = -1;
4626 int restart_count = 0, restarted_count = 0;
4627 /* restart all threads that stopped in the
4629 FOREACH_THREAD_SAFE (info) {
4633 if (!info->thread_is_dying && (!info->stack_start || info->in_critical_region ||
4634 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
4635 binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
4636 result = mono_sgen_resume_thread (info);
4643 /* we set the stopped_ip to
4644 NULL for threads which
4645 we're not restarting so
4646 that we can easily identify
4648 info->stopped_ip = NULL;
4649 info->stopped_domain = NULL;
4651 } END_FOREACH_THREAD_SAFE
4652 /* if no threads were restarted, we're done */
4653 if (restart_count == 0)
4656 /* wait for the threads to signal their restart */
4657 mono_sgen_wait_for_suspend_ack (restart_count);
4659 if (sleep_duration < 0) {
4667 g_usleep (sleep_duration);
4668 sleep_duration += 10;
4671 /* stop them again */
4672 FOREACH_THREAD (info) {
4674 if (info->skip || info->stopped_ip == NULL)
4676 result = mono_sgen_suspend_thread (info);
4683 } END_FOREACH_THREAD
4684 /* some threads might have died */
4685 num_threads_died += restart_count - restarted_count;
4686 /* wait for the threads to signal their suspension
4688 mono_sgen_wait_for_suspend_ack (restart_count);
4691 return num_threads_died;
4695 acquire_gc_locks (void)
4698 mono_thread_info_suspend_lock ();
4702 release_gc_locks (void)
4704 mono_thread_info_suspend_unlock ();
4705 UNLOCK_INTERRUPTION;
4708 static TV_DECLARE (stop_world_time);
4709 static unsigned long max_pause_usec = 0;
4711 /* LOCKING: assumes the GC lock is held */
4713 stop_world (int generation)
4717 /*XXX this is the right stop, thought might not be the nicest place to put it*/
4718 mono_sgen_process_togglerefs ();
4720 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
4721 acquire_gc_locks ();
4723 update_current_thread_stack (&count);
4725 mono_sgen_global_stop_count++;
4726 DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", mono_sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ()));
4727 TV_GETTIME (stop_world_time);
4728 count = mono_sgen_thread_handshake (TRUE);
4729 count -= restart_threads_until_none_in_managed_allocator ();
4730 g_assert (count >= 0);
4731 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
4732 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
4734 last_major_num_sections = major_collector.get_num_major_sections ();
4735 last_los_memory_usage = los_memory_usage;
4736 major_collection_happened = FALSE;
4740 /* LOCKING: assumes the GC lock is held */
4742 restart_world (int generation)
4744 int count, num_major_sections;
4745 SgenThreadInfo *info;
4746 TV_DECLARE (end_sw);
4747 TV_DECLARE (end_bridge);
4748 unsigned long usec, bridge_usec;
4750 /* notify the profiler of the leftovers */
4751 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
4752 if (moved_objects_idx) {
4753 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
4754 moved_objects_idx = 0;
4757 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
4758 FOREACH_THREAD (info) {
4759 info->stack_start = NULL;
4761 info->monoctx = NULL;
4763 info->stopped_regs = NULL;
4765 } END_FOREACH_THREAD
4767 release_gc_locks ();
4769 count = mono_sgen_thread_handshake (FALSE);
4770 TV_GETTIME (end_sw);
4771 usec = TV_ELAPSED (stop_world_time, end_sw);
4772 max_pause_usec = MAX (usec, max_pause_usec);
4773 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
4774 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
4778 TV_GETTIME (end_bridge);
4779 bridge_usec = TV_ELAPSED (end_sw, end_bridge);
4781 num_major_sections = major_collector.get_num_major_sections ();
4782 if (major_collection_happened)
4783 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR: %s pause %.2fms, bridge %.2fms major %dK/%dK los %dK/%dK",
4784 generation ? "" : "(minor overflow)",
4785 (int)usec / 1000.0f, (int)bridge_usec / 1000.0f,
4786 major_collector.section_size * num_major_sections / 1024,
4787 major_collector.section_size * last_major_num_sections / 1024,
4788 los_memory_usage / 1024,
4789 last_los_memory_usage / 1024);
4791 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR: pause %.2fms, bridge %.2fms promoted %dK major %dK los %dK",
4792 (int)usec / 1000.0f, (int)bridge_usec / 1000.0f,
4793 (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
4794 major_collector.section_size * num_major_sections / 1024,
4795 los_memory_usage / 1024);
4801 mono_sgen_get_current_collection_generation (void)
4803 return current_collection_generation;
4807 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
4809 gc_callbacks = *callbacks;
4813 mono_gc_get_gc_callbacks ()
4815 return &gc_callbacks;
4818 /* Variables holding start/end nursery so it won't have to be passed at every call */
4819 static void *scan_area_arg_start, *scan_area_arg_end;
4822 mono_gc_conservatively_scan_area (void *start, void *end)
4824 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
4828 mono_gc_scan_object (void *obj)
4830 UserCopyOrMarkData *data = mono_native_tls_get_value (user_copy_or_mark_key);
4832 if (current_collection_generation == GENERATION_NURSERY) {
4833 if (collection_is_parallel ())
4834 major_collector.copy_object (&obj, data->queue);
4836 major_collector.nopar_copy_object (&obj, data->queue);
4838 major_collector.copy_or_mark_object (&obj, data->queue);
4844 * Mark from thread stacks and registers.
4847 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
4849 SgenThreadInfo *info;
4851 scan_area_arg_start = start_nursery;
4852 scan_area_arg_end = end_nursery;
4854 FOREACH_THREAD (info) {
4856 DEBUG (3, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
4859 DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %ld, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
4860 if (!info->thread_is_dying) {
4861 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
4862 UserCopyOrMarkData data = { NULL, queue };
4863 set_user_copy_or_mark_data (&data);
4864 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
4865 set_user_copy_or_mark_data (NULL);
4866 } else if (!precise) {
4867 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
4872 if (!info->thread_is_dying && !precise)
4873 conservatively_pin_objects_from ((void**)info->monoctx, (void**)info->monoctx + ARCH_NUM_REGS,
4874 start_nursery, end_nursery, PIN_TYPE_STACK);
4876 if (!info->thread_is_dying && !precise)
4877 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
4878 start_nursery, end_nursery, PIN_TYPE_STACK);
4880 } END_FOREACH_THREAD
4884 find_pinning_ref_from_thread (char *obj, size_t size)
4887 SgenThreadInfo *info;
4888 char *endobj = obj + size;
4890 FOREACH_THREAD (info) {
4891 char **start = (char**)info->stack_start;
4894 while (start < (char**)info->stack_end) {
4895 if (*start >= obj && *start < endobj) {
4896 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)mono_thread_info_get_tid (info), start, info->stack_start, info->stack_end));
4901 for (j = 0; j < ARCH_NUM_REGS; ++j) {
4903 mword w = ((mword*)info->monoctx) [j];
4905 mword w = (mword)info->stopped_regs [j];
4908 if (w >= (mword)obj && w < (mword)obj + size)
4909 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)mono_thread_info_get_tid (info)));
4910 } END_FOREACH_THREAD
4915 ptr_on_stack (void *ptr)
4917 gpointer stack_start = &stack_start;
4918 SgenThreadInfo *info = mono_thread_info_current ();
4920 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
4926 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
4933 HEAVY_STAT (++stat_global_remsets_processed);
4935 HEAVY_STAT (++stat_local_remsets_processed);
4937 /* FIXME: exclude stack locations */
4938 switch ((*p) & REMSET_TYPE_MASK) {
4939 case REMSET_LOCATION:
4941 //__builtin_prefetch (ptr);
4942 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
4943 gpointer old = *ptr;
4944 major_collector.copy_object (ptr, queue);
4945 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
4947 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
4948 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4950 * If the object is pinned, each reference to it from nonpinned objects
4951 * becomes part of the global remset, which can grow very large.
4953 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4954 mono_sgen_add_to_global_remset (ptr);
4957 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
4961 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4962 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4965 while (count-- > 0) {
4966 major_collector.copy_object (ptr, queue);
4967 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
4968 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
4969 mono_sgen_add_to_global_remset (ptr);
4974 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4975 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4977 mono_sgen_get_minor_scan_object () ((char*)ptr, queue);
4979 case REMSET_VTYPE: {
4980 ScanVTypeFunc scan_vtype = mono_sgen_get_minor_scan_vtype ();
4983 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4984 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4989 while (count-- > 0) {
4990 scan_vtype ((char*)ptr, desc, queue);
4991 ptr = (void**)((char*)ptr + skip_size);
4996 g_assert_not_reached ();
5001 #ifdef HEAVY_STATISTICS
5003 collect_store_remsets (RememberedSet *remset, mword *bumper)
5005 mword *p = remset->data;
5010 while (p < remset->store_next) {
5011 switch ((*p) & REMSET_TYPE_MASK) {
5012 case REMSET_LOCATION:
5015 ++stat_saved_remsets_1;
5017 if (*p == last1 || *p == last2) {
5018 ++stat_saved_remsets_2;
5035 g_assert_not_reached ();
5045 RememberedSet *remset;
5047 SgenThreadInfo *info;
5049 mword *addresses, *bumper, *p, *r;
5051 FOREACH_THREAD (info) {
5052 for (remset = info->remset; remset; remset = remset->next)
5053 size += remset->store_next - remset->data;
5054 } END_FOREACH_THREAD
5055 for (remset = freed_thread_remsets; remset; remset = remset->next)
5056 size += remset->store_next - remset->data;
5057 for (remset = global_remset; remset; remset = remset->next)
5058 size += remset->store_next - remset->data;
5060 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5062 FOREACH_THREAD (info) {
5063 for (remset = info->remset; remset; remset = remset->next)
5064 bumper = collect_store_remsets (remset, bumper);
5065 } END_FOREACH_THREAD
5066 for (remset = global_remset; remset; remset = remset->next)
5067 bumper = collect_store_remsets (remset, bumper);
5068 for (remset = freed_thread_remsets; remset; remset = remset->next)
5069 bumper = collect_store_remsets (remset, bumper);
5071 g_assert (bumper <= addresses + size);
5073 stat_store_remsets += bumper - addresses;
5075 sort_addresses ((void**)addresses, bumper - addresses);
5078 while (r < bumper) {
5084 stat_store_remsets_unique += p - addresses;
5086 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5091 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5093 *info->store_remset_buffer_index_addr = 0;
5094 /* See the comment at the end of sgen_thread_unregister() */
5095 if (*info->store_remset_buffer_addr)
5096 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5100 remset_byte_size (RememberedSet *remset)
5102 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5106 scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5108 RememberedSet *remset;
5109 mword *p, *next_p, *store_pos;
5111 /* the global one */
5112 for (remset = global_remset; remset; remset = remset->next) {
5113 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
5114 store_pos = remset->data;
5115 for (p = remset->data; p < remset->store_next; p = next_p) {
5116 void **ptr = (void**)p [0];
5118 /*Ignore previously processed remset.*/
5119 if (!global_remset_location_was_not_added (ptr)) {
5124 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5127 * Clear global remsets of locations which no longer point to the
5128 * nursery. Otherwise, they could grow indefinitely between major
5131 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5133 if (ptr_in_nursery (*ptr)) {
5134 *store_pos ++ = p [0];
5135 HEAVY_STAT (++stat_global_remsets_readded);
5139 /* Truncate the remset */
5140 remset->store_next = store_pos;
5145 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5148 SgenThreadInfo *info;
5149 RememberedSet *remset;
5150 GenericStoreRememberedSet *store_remset;
5153 #ifdef HEAVY_STATISTICS
5157 /* the generic store ones */
5158 store_remset = generic_store_remsets;
5159 while (store_remset) {
5160 GenericStoreRememberedSet *next = store_remset->next;
5162 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5163 gpointer addr = store_remset->data [i];
5165 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5168 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5170 store_remset = next;
5172 generic_store_remsets = NULL;
5174 /* the per-thread ones */
5175 FOREACH_THREAD (info) {
5176 RememberedSet *next;
5178 for (remset = info->remset; remset; remset = next) {
5179 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
5180 for (p = remset->data; p < remset->store_next;)
5181 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5182 remset->store_next = remset->data;
5183 next = remset->next;
5184 remset->next = NULL;
5185 if (remset != info->remset) {
5186 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5187 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5190 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5191 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5192 clear_thread_store_remset_buffer (info);
5193 } END_FOREACH_THREAD
5195 /* the freed thread ones */
5196 while (freed_thread_remsets) {
5197 RememberedSet *next;
5198 remset = freed_thread_remsets;
5199 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
5200 for (p = remset->data; p < remset->store_next;)
5201 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5202 next = remset->next;
5203 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5204 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5205 freed_thread_remsets = next;
5210 * Clear the info in the remembered sets: we're doing a major collection, so
5211 * the per-thread ones are not needed and the global ones will be reconstructed
5215 clear_remsets (void)
5217 SgenThreadInfo *info;
5218 RememberedSet *remset, *next;
5220 /* the global list */
5221 for (remset = global_remset; remset; remset = next) {
5222 remset->store_next = remset->data;
5223 next = remset->next;
5224 remset->next = NULL;
5225 if (remset != global_remset) {
5226 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5227 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5230 /* the generic store ones */
5231 while (generic_store_remsets) {
5232 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5233 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5234 generic_store_remsets = gs_next;
5236 /* the per-thread ones */
5237 FOREACH_THREAD (info) {
5238 for (remset = info->remset; remset; remset = next) {
5239 remset->store_next = remset->data;
5240 next = remset->next;
5241 remset->next = NULL;
5242 if (remset != info->remset) {
5243 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5244 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5247 clear_thread_store_remset_buffer (info);
5248 } END_FOREACH_THREAD
5250 /* the freed thread ones */
5251 while (freed_thread_remsets) {
5252 next = freed_thread_remsets->next;
5253 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5254 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5255 freed_thread_remsets = next;
5260 * Clear the thread local TLAB variables for all threads.
5265 SgenThreadInfo *info;
5267 FOREACH_THREAD (info) {
5268 /* A new TLAB will be allocated when the thread does its first allocation */
5269 *info->tlab_start_addr = NULL;
5270 *info->tlab_next_addr = NULL;
5271 *info->tlab_temp_end_addr = NULL;
5272 *info->tlab_real_end_addr = NULL;
5273 } END_FOREACH_THREAD
5277 sgen_thread_register (SgenThreadInfo* info, void *addr)
5279 #ifndef HAVE_KW_THREAD
5280 SgenThreadInfo *__thread_info__ = info;
5284 #ifndef HAVE_KW_THREAD
5285 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5287 g_assert (!mono_native_tls_get_value (thread_info_key));
5288 mono_native_tls_set_value (thread_info_key, info);
5293 #if !defined(__MACH__)
5294 info->stop_count = -1;
5298 info->doing_handshake = FALSE;
5299 info->thread_is_dying = FALSE;
5300 info->stack_start = NULL;
5301 info->tlab_start_addr = &TLAB_START;
5302 info->tlab_next_addr = &TLAB_NEXT;
5303 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5304 info->tlab_real_end_addr = &TLAB_REAL_END;
5305 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5306 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5307 info->stopped_ip = NULL;
5308 info->stopped_domain = NULL;
5310 info->monoctx = NULL;
5312 info->stopped_regs = NULL;
5315 binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
5317 #ifdef HAVE_KW_THREAD
5318 tlab_next_addr = &tlab_next;
5319 store_remset_buffer_index_addr = &store_remset_buffer_index;
5322 #if defined(__MACH__)
5323 info->mach_port = mach_thread_self ();
5326 /* try to get it with attributes first */
5327 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5331 pthread_attr_t attr;
5332 pthread_getattr_np (pthread_self (), &attr);
5333 pthread_attr_getstack (&attr, &sstart, &size);
5334 info->stack_start_limit = sstart;
5335 info->stack_end = (char*)sstart + size;
5336 pthread_attr_destroy (&attr);
5338 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5339 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5340 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5343 /* FIXME: we assume the stack grows down */
5344 gsize stack_bottom = (gsize)addr;
5345 stack_bottom += 4095;
5346 stack_bottom &= ~4095;
5347 info->stack_end = (char*)stack_bottom;
5351 #ifdef HAVE_KW_THREAD
5352 stack_end = info->stack_end;
5355 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info, FALSE);
5356 mono_native_tls_set_value (remembered_set_key, info->remset);
5357 #ifdef HAVE_KW_THREAD
5358 remembered_set = info->remset;
5361 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5362 STORE_REMSET_BUFFER_INDEX = 0;
5364 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) stack end %p\n", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end));
5366 if (gc_callbacks.thread_attach_func)
5367 info->runtime_data = gc_callbacks.thread_attach_func ();
5374 add_generic_store_remset_from_buffer (gpointer *buffer)
5376 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5377 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5378 remset->next = generic_store_remsets;
5379 generic_store_remsets = remset;
5383 sgen_thread_unregister (SgenThreadInfo *p)
5385 RememberedSet *rset;
5387 /* If a delegate is passed to native code and invoked on a thread we dont
5388 * know about, the jit will register it with mono_jit_thread_attach, but
5389 * we have no way of knowing when that thread goes away. SGen has a TSD
5390 * so we assume that if the domain is still registered, we can detach
5393 if (mono_domain_get ())
5394 mono_thread_detach (mono_thread_current ());
5396 p->thread_is_dying = TRUE;
5399 There is a race condition between a thread finishing executing and been removed
5400 from the GC thread set.
5401 This happens on posix systems when TLS data is been cleaned-up, libpthread will
5402 set the thread_info slot to NULL before calling the cleanup function. This
5403 opens a window in which the thread is registered but has a NULL TLS.
5405 The suspend signal handler needs TLS data to know where to store thread state
5406 data or otherwise it will simply ignore the thread.
5408 This solution works because the thread doing STW will wait until all threads been
5409 suspended handshake back, so there is no race between the doing_hankshake test
5410 and the suspend_thread call.
5412 This is not required on systems that do synchronous STW as those can deal with
5413 the above race at suspend time.
5415 FIXME: I believe we could avoid this by using mono_thread_info_lookup when
5416 mono_thread_info_current returns NULL. Or fix mono_thread_info_lookup to do so.
5418 #if (defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED) || !defined(HAVE_PTHREAD_KILL)
5421 while (!TRYLOCK_GC) {
5422 if (!mono_sgen_park_current_thread_if_doing_handshake (p))
5427 binary_protocol_thread_unregister ((gpointer)mono_thread_info_get_tid (p));
5428 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)mono_thread_info_get_tid (p)));
5430 #if defined(__MACH__)
5431 mach_port_deallocate (current_task (), p->mach_port);
5434 if (gc_callbacks.thread_detach_func) {
5435 gc_callbacks.thread_detach_func (p->runtime_data);
5436 p->runtime_data = NULL;
5440 if (freed_thread_remsets) {
5441 for (rset = p->remset; rset->next; rset = rset->next)
5443 rset->next = freed_thread_remsets;
5444 freed_thread_remsets = p->remset;
5446 freed_thread_remsets = p->remset;
5449 if (*p->store_remset_buffer_index_addr)
5450 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5451 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5453 * This is currently not strictly required, but we do it
5454 * anyway in case we change thread unregistering:
5456 * If the thread is removed from the thread list after
5457 * unregistering (this is currently not the case), and a
5458 * collection occurs, clear_remsets() would want to memset
5459 * this buffer, which would either clobber memory or crash.
5461 *p->store_remset_buffer_addr = NULL;
5463 mono_threads_unregister_current_thread (p);
5469 sgen_thread_attach (SgenThreadInfo *info)
5472 /*this is odd, can we get attached before the gc is inited?*/
5476 if (gc_callbacks.thread_attach_func && !info->runtime_data)
5477 info->runtime_data = gc_callbacks.thread_attach_func ();
5480 mono_gc_register_thread (void *baseptr)
5482 return mono_thread_info_attach (baseptr) != NULL;
5486 * mono_gc_set_stack_end:
5488 * Set the end of the current threads stack to STACK_END. The stack space between
5489 * STACK_END and the real end of the threads stack will not be scanned during collections.
5492 mono_gc_set_stack_end (void *stack_end)
5494 SgenThreadInfo *info;
5497 info = mono_thread_info_current ();
5499 g_assert (stack_end < info->stack_end);
5500 info->stack_end = stack_end;
5505 #if USE_PTHREAD_INTERCEPT
5509 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5511 return pthread_create (new_thread, attr, start_routine, arg);
5515 mono_gc_pthread_join (pthread_t thread, void **retval)
5517 return pthread_join (thread, retval);
5521 mono_gc_pthread_detach (pthread_t thread)
5523 return pthread_detach (thread);
5527 mono_gc_pthread_exit (void *retval)
5529 pthread_exit (retval);
5532 #endif /* USE_PTHREAD_INTERCEPT */
5535 * ######################################################################
5536 * ######## Write barriers
5537 * ######################################################################
5541 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
5544 dummy_use (gpointer v) {
5545 __asm__ volatile ("" : "=r"(v) : "r"(v));
5549 static RememberedSet*
5550 alloc_remset (int size, gpointer id, gboolean global)
5552 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
5553 res->store_next = res->data;
5554 res->end_set = res->data + size;
5556 DEBUG (4, fprintf (gc_debug_file, "Allocated%s remset size %d at %p for %p\n", global ? " global" : "", size, res->data, id));
5561 * Note: the write barriers first do the needed GC work and then do the actual store:
5562 * this way the value is visible to the conservative GC scan after the write barrier
5563 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
5564 * the conservative scan, otherwise by the remembered set scan.
5567 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
5569 HEAVY_STAT (++stat_wbarrier_set_field);
5570 if (ptr_in_nursery (field_ptr)) {
5571 *(void**)field_ptr = value;
5574 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
5576 binary_protocol_wbarrier (field_ptr, value, value->vtable);
5577 if (use_cardtable) {
5578 *(void**)field_ptr = value;
5579 if (ptr_in_nursery (value))
5580 sgen_card_table_mark_address ((mword)field_ptr);
5587 rs = REMEMBERED_SET;
5588 if (rs->store_next < rs->end_set) {
5589 *(rs->store_next++) = (mword)field_ptr;
5590 *(void**)field_ptr = value;
5594 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5595 rs->next = REMEMBERED_SET;
5596 REMEMBERED_SET = rs;
5597 #ifdef HAVE_KW_THREAD
5598 mono_thread_info_current ()->remset = rs;
5600 *(rs->store_next++) = (mword)field_ptr;
5601 *(void**)field_ptr = value;
5607 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
5609 HEAVY_STAT (++stat_wbarrier_set_arrayref);
5610 if (ptr_in_nursery (slot_ptr)) {
5611 *(void**)slot_ptr = value;
5614 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
5616 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
5617 if (use_cardtable) {
5618 *(void**)slot_ptr = value;
5619 if (ptr_in_nursery (value))
5620 sgen_card_table_mark_address ((mword)slot_ptr);
5627 rs = REMEMBERED_SET;
5628 if (rs->store_next < rs->end_set) {
5629 *(rs->store_next++) = (mword)slot_ptr;
5630 *(void**)slot_ptr = value;
5634 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5635 rs->next = REMEMBERED_SET;
5636 REMEMBERED_SET = rs;
5637 #ifdef HAVE_KW_THREAD
5638 mono_thread_info_current ()->remset = rs;
5640 *(rs->store_next++) = (mword)slot_ptr;
5641 *(void**)slot_ptr = value;
5647 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
5649 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
5650 /*This check can be done without taking a lock since dest_ptr array is pinned*/
5651 if (ptr_in_nursery (dest_ptr) || count <= 0) {
5652 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5656 #ifdef SGEN_BINARY_PROTOCOL
5659 for (i = 0; i < count; ++i) {
5660 gpointer dest = (gpointer*)dest_ptr + i;
5661 gpointer obj = *((gpointer*)src_ptr + i);
5663 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
5668 if (use_cardtable) {
5669 gpointer *dest = dest_ptr;
5670 gpointer *src = src_ptr;
5672 /*overlapping that required backward copying*/
5673 if (src < dest && (src + count) > dest) {
5674 gpointer *start = dest;
5678 for (; dest >= start; --src, --dest) {
5679 gpointer value = *src;
5681 if (ptr_in_nursery (value))
5682 sgen_card_table_mark_address ((mword)dest);
5686 gpointer *end = dest + count;
5687 for (; dest < end; ++src, ++dest) {
5688 gpointer value = *src;
5690 if (ptr_in_nursery (value))
5691 sgen_card_table_mark_address ((mword)dest);
5699 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5701 rs = REMEMBERED_SET;
5702 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
5703 if (rs->store_next + 1 < rs->end_set) {
5704 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5705 *(rs->store_next++) = count;
5709 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5710 rs->next = REMEMBERED_SET;
5711 REMEMBERED_SET = rs;
5712 #ifdef HAVE_KW_THREAD
5713 mono_thread_info_current ()->remset = rs;
5715 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5716 *(rs->store_next++) = count;
5722 static char *found_obj;
5725 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
5727 char *ptr = user_data;
5729 if (ptr >= obj && ptr < obj + size) {
5730 g_assert (!found_obj);
5735 /* for use in the debugger */
5736 char* find_object_for_ptr (char *ptr);
5738 find_object_for_ptr (char *ptr)
5740 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
5742 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
5743 find_object_for_ptr_callback, ptr, TRUE);
5749 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
5754 * Very inefficient, but this is debugging code, supposed to
5755 * be called from gdb, so we don't care.
5758 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
5763 evacuate_remset_buffer (void)
5768 buffer = STORE_REMSET_BUFFER;
5770 add_generic_store_remset_from_buffer (buffer);
5771 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5773 STORE_REMSET_BUFFER_INDEX = 0;
5777 mono_gc_wbarrier_generic_nostore (gpointer ptr)
5783 HEAVY_STAT (++stat_wbarrier_generic_store);
5785 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
5786 /* FIXME: ptr_in_heap must be called with the GC lock held */
5787 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
5788 char *start = find_object_for_ptr (ptr);
5789 MonoObject *value = *(MonoObject**)ptr;
5793 MonoObject *obj = (MonoObject*)start;
5794 if (obj->vtable->domain != value->vtable->domain)
5795 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
5801 if (*(gpointer*)ptr)
5802 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
5804 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
5805 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
5809 if (use_cardtable) {
5810 if (ptr_in_nursery(*(gpointer*)ptr))
5811 sgen_card_table_mark_address ((mword)ptr);
5817 buffer = STORE_REMSET_BUFFER;
5818 index = STORE_REMSET_BUFFER_INDEX;
5819 /* This simple optimization eliminates a sizable portion of
5820 entries. Comparing it to the last but one entry as well
5821 doesn't eliminate significantly more entries. */
5822 if (buffer [index] == ptr) {
5827 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
5828 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
5831 if (index >= STORE_REMSET_BUFFER_SIZE) {
5832 evacuate_remset_buffer ();
5833 index = STORE_REMSET_BUFFER_INDEX;
5834 g_assert (index == 0);
5837 buffer [index] = ptr;
5838 STORE_REMSET_BUFFER_INDEX = index;
5844 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
5846 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
5847 *(void**)ptr = value;
5848 if (ptr_in_nursery (value))
5849 mono_gc_wbarrier_generic_nostore (ptr);
5853 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
5855 mword *dest = _dest;
5860 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
5865 size -= SIZEOF_VOID_P;
5870 #ifdef SGEN_BINARY_PROTOCOL
5872 #define HANDLE_PTR(ptr,obj) do { \
5873 gpointer o = *(gpointer*)(ptr); \
5875 gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
5876 binary_protocol_wbarrier (d, o, (gpointer) LOAD_VTABLE (o)); \
5881 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
5883 #define SCAN_OBJECT_NOVTABLE
5884 #include "sgen-scan-object.h"
5889 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
5892 size_t element_size = mono_class_value_size (klass, NULL);
5893 size_t size = count * element_size;
5895 HEAVY_STAT (++stat_wbarrier_value_copy);
5896 g_assert (klass->valuetype);
5897 #ifdef SGEN_BINARY_PROTOCOL
5900 for (i = 0; i < count; ++i) {
5901 scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
5902 (char*)src + i * element_size - sizeof (MonoObject),
5903 (mword) klass->gc_descr);
5907 if (use_cardtable) {
5908 #ifdef DISABLE_CRITICAL_REGION
5911 ENTER_CRITICAL_REGION;
5913 mono_gc_memmove (dest, src, size);
5914 sgen_card_table_mark_range ((mword)dest, size);
5915 #ifdef DISABLE_CRITICAL_REGION
5918 EXIT_CRITICAL_REGION;
5922 mono_gc_memmove (dest, src, size);
5923 rs = REMEMBERED_SET;
5924 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
5928 g_assert (klass->gc_descr_inited);
5929 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));
5931 if (rs->store_next + 4 < rs->end_set) {
5932 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5933 *(rs->store_next++) = (mword)klass->gc_descr;
5934 *(rs->store_next++) = (mword)count;
5935 *(rs->store_next++) = (mword)element_size;
5939 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5940 rs->next = REMEMBERED_SET;
5941 REMEMBERED_SET = rs;
5942 #ifdef HAVE_KW_THREAD
5943 mono_thread_info_current ()->remset = rs;
5945 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5946 *(rs->store_next++) = (mword)klass->gc_descr;
5947 *(rs->store_next++) = (mword)count;
5948 *(rs->store_next++) = (mword)element_size;
5954 * mono_gc_wbarrier_object_copy:
5956 * Write barrier to call when obj is the result of a clone or copy of an object.
5959 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
5965 HEAVY_STAT (++stat_wbarrier_object_copy);
5966 rs = REMEMBERED_SET;
5967 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
5968 size = mono_object_class (obj)->instance_size;
5970 #ifdef SGEN_BINARY_PROTOCOL
5971 scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
5973 /* do not copy the sync state */
5974 mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
5975 size - sizeof (MonoObject));
5976 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
5980 if (rs->store_next < rs->end_set) {
5981 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5985 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5986 rs->next = REMEMBERED_SET;
5987 REMEMBERED_SET = rs;
5989 #ifdef HAVE_KW_THREAD
5990 mono_thread_info_current ()->remset = rs;
5992 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5997 * ######################################################################
5998 * ######## Collector debugging
5999 * ######################################################################
6002 const char*descriptor_types [] = {
6014 describe_ptr (char *ptr)
6021 if (ptr_in_nursery (ptr)) {
6022 printf ("Pointer inside nursery.\n");
6024 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
6026 printf ("Pointer is the start of object %p in LOS space.\n", start);
6028 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
6030 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
6031 printf ("Pointer inside oldspace.\n");
6032 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
6033 printf ("Pointer is inside a pinned chunk.\n");
6035 printf ("Pointer unknown.\n");
6040 if (object_is_pinned (ptr))
6041 printf ("Object is pinned.\n");
6043 if (object_is_forwarded (ptr))
6044 printf ("Object is forwared.\n");
6046 // FIXME: Handle pointers to the inside of objects
6047 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6049 printf ("VTable: %p\n", vtable);
6050 if (vtable == NULL) {
6051 printf ("VTable is invalid (empty).\n");
6054 if (ptr_in_nursery (vtable)) {
6055 printf ("VTable is invalid (points inside nursery).\n");
6058 printf ("Class: %s\n", vtable->klass->name);
6060 desc = ((GCVTable*)vtable)->desc;
6061 printf ("Descriptor: %lx\n", (long)desc);
6064 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6068 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6074 switch ((*p) & REMSET_TYPE_MASK) {
6075 case REMSET_LOCATION:
6076 if (*p == (mword)addr)
6080 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6082 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6086 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6087 count = safe_object_get_size ((MonoObject*)ptr);
6088 count = ALIGN_UP (count);
6089 count /= sizeof (mword);
6090 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6094 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6099 /* The descriptor includes the size of MonoObject */
6100 skip_size -= sizeof (MonoObject);
6102 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6107 g_assert_not_reached ();
6113 * Return whenever ADDR occurs in the remembered sets
6116 find_in_remsets (char *addr)
6119 SgenThreadInfo *info;
6120 RememberedSet *remset;
6121 GenericStoreRememberedSet *store_remset;
6123 gboolean found = FALSE;
6125 /* the global one */
6126 for (remset = global_remset; remset; remset = remset->next) {
6127 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
6128 for (p = remset->data; p < remset->store_next;) {
6129 p = find_in_remset_loc (p, addr, &found);
6135 /* the generic store ones */
6136 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6137 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6138 if (store_remset->data [i] == addr)
6143 /* the per-thread ones */
6144 FOREACH_THREAD (info) {
6146 for (remset = info->remset; remset; remset = remset->next) {
6147 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
6148 for (p = remset->data; p < remset->store_next;) {
6149 p = find_in_remset_loc (p, addr, &found);
6154 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6155 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6158 } END_FOREACH_THREAD
6160 /* the freed thread ones */
6161 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6162 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
6163 for (p = remset->data; p < remset->store_next;) {
6164 p = find_in_remset_loc (p, addr, &found);
6173 static gboolean missing_remsets;
6176 * We let a missing remset slide if the target object is pinned,
6177 * because the store might have happened but the remset not yet added,
6178 * but in that case the target must be pinned. We might theoretically
6179 * miss some missing remsets this way, but it's very unlikely.
6182 #define HANDLE_PTR(ptr,obj) do { \
6183 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_end) { \
6184 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6185 fprintf (gc_debug_file, "Oldspace->newspace reference %p at offset %td 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); \
6186 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6187 if (!object_is_pinned (*(ptr))) \
6188 missing_remsets = TRUE; \
6194 * Check that each object reference which points into the nursery can
6195 * be found in the remembered sets.
6198 check_consistency_callback (char *start, size_t size, void *dummy)
6200 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6201 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6203 #define SCAN_OBJECT_ACTION
6204 #include "sgen-scan-object.h"
6208 * Perform consistency check of the heap.
6210 * Assumes the world is stopped.
6213 check_consistency (void)
6215 // Need to add more checks
6217 missing_remsets = FALSE;
6219 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6221 // Check that oldspace->newspace pointers are registered with the collector
6222 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6224 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6226 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6228 if (!binary_protocol_is_enabled ())
6229 g_assert (!missing_remsets);
6234 #define HANDLE_PTR(ptr,obj) do { \
6235 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6236 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6240 check_major_refs_callback (char *start, size_t size, void *dummy)
6242 #define SCAN_OBJECT_ACTION
6243 #include "sgen-scan-object.h"
6247 check_major_refs (void)
6249 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6250 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6253 /* Check that the reference is valid */
6255 #define HANDLE_PTR(ptr,obj) do { \
6257 g_assert (safe_name (*(ptr)) != NULL); \
6264 * Perform consistency check on an object. Currently we only check that the
6265 * reference fields are valid.
6268 check_object (char *start)
6273 #include "sgen-scan-object.h"
6277 * ######################################################################
6278 * ######## Other mono public interface functions.
6279 * ######################################################################
6282 #define REFS_SIZE 128
6285 MonoGCReferences callback;
6289 MonoObject *refs [REFS_SIZE];
6290 uintptr_t offsets [REFS_SIZE];
6294 #define HANDLE_PTR(ptr,obj) do { \
6296 if (hwi->count == REFS_SIZE) { \
6297 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
6301 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
6302 hwi->refs [hwi->count++] = *(ptr); \
6307 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6309 #include "sgen-scan-object.h"
6313 walk_references (char *start, size_t size, void *data)
6315 HeapWalkInfo *hwi = data;
6318 collect_references (hwi, start, size);
6319 if (hwi->count || !hwi->called)
6320 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6324 * mono_gc_walk_heap:
6325 * @flags: flags for future use
6326 * @callback: a function pointer called for each object in the heap
6327 * @data: a user data pointer that is passed to callback
6329 * This function can be used to iterate over all the live objects in the heap:
6330 * for each object, @callback is invoked, providing info about the object's
6331 * location in memory, its class, its size and the objects it references.
6332 * For each referenced object it's offset from the object address is
6333 * reported in the offsets array.
6334 * The object references may be buffered, so the callback may be invoked
6335 * multiple times for the same object: in all but the first call, the size
6336 * argument will be zero.
6337 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6338 * profiler event handler.
6340 * Returns: a non-zero value if the GC doesn't support heap walking
6343 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6348 hwi.callback = callback;
6351 mono_sgen_clear_nursery_fragments ();
6352 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
6354 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
6355 mono_sgen_los_iterate_objects (walk_references, &hwi);
6361 mono_gc_collect (int generation)
6366 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6367 stop_world (generation);
6368 if (generation == 0) {
6369 collect_nursery (0);
6371 major_collection ("user request");
6373 restart_world (generation);
6374 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6379 mono_gc_max_generation (void)
6385 mono_gc_collection_count (int generation)
6387 if (generation == 0)
6388 return num_minor_gcs;
6389 return num_major_gcs;
6393 mono_gc_get_used_size (void)
6397 tot = los_memory_usage;
6398 tot += nursery_section->next_data - nursery_section->data;
6399 tot += major_collector.get_used_size ();
6400 /* FIXME: account for pinned objects */
6406 mono_gc_get_heap_size (void)
6412 mono_gc_disable (void)
6420 mono_gc_enable (void)
6428 mono_gc_get_los_limit (void)
6430 return MAX_SMALL_OBJ_SIZE;
6434 mono_object_is_alive (MonoObject* o)
6440 mono_gc_get_generation (MonoObject *obj)
6442 if (ptr_in_nursery (obj))
6448 mono_gc_enable_events (void)
6453 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6455 mono_gc_register_disappearing_link (obj, link_addr, track, FALSE);
6459 mono_gc_weak_link_remove (void **link_addr)
6461 mono_gc_register_disappearing_link (NULL, link_addr, FALSE, FALSE);
6465 mono_gc_weak_link_get (void **link_addr)
6469 return (MonoObject*) REVEAL_POINTER (*link_addr);
6473 mono_gc_ephemeron_array_add (MonoObject *obj)
6475 EphemeronLinkNode *node;
6479 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
6484 node->array = (char*)obj;
6485 node->next = ephemeron_list;
6486 ephemeron_list = node;
6488 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6495 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6498 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
6499 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6500 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6502 mword complex = alloc_complex_descriptor (bitmap, numbits);
6503 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6507 static void *all_ref_root_descrs [32];
6510 mono_gc_make_root_descr_all_refs (int numbits)
6514 int num_bytes = numbits / 8;
6516 if (numbits < 32 && all_ref_root_descrs [numbits])
6517 return all_ref_root_descrs [numbits];
6519 gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
6520 memset (gc_bitmap, 0xff, num_bytes);
6521 if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT))
6522 gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
6523 else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
6524 gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
6526 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
6527 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
6531 all_ref_root_descrs [numbits] = descr;
6537 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6541 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6542 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6543 user_descriptors [user_descriptors_next ++] = marker;
6549 mono_gc_alloc_fixed (size_t size, void *descr)
6551 /* FIXME: do a single allocation */
6552 void *res = calloc (1, size);
6555 if (!mono_gc_register_root (res, size, descr)) {
6563 mono_gc_free_fixed (void* addr)
6565 mono_gc_deregister_root (addr);
6570 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6574 result = func (data);
6575 UNLOCK_INTERRUPTION;
6580 mono_gc_is_gc_thread (void)
6584 result = mono_thread_info_current () != NULL;
6590 is_critical_method (MonoMethod *method)
6592 return mono_runtime_is_critical_method (method) || mono_gc_is_critical_method (method);
6596 mono_gc_base_init (void)
6598 MonoThreadInfoCallbacks cb;
6601 char *major_collector_opt = NULL;
6603 glong soft_limit = 0;
6609 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
6612 /* already inited */
6615 /* being inited by another thread */
6619 /* we will init it */
6622 g_assert_not_reached ();
6624 } while (result != 0);
6626 LOCK_INIT (gc_mutex);
6628 pagesize = mono_pagesize ();
6629 gc_debug_file = stderr;
6631 cb.thread_register = sgen_thread_register;
6632 cb.thread_unregister = sgen_thread_unregister;
6633 cb.thread_attach = sgen_thread_attach;
6634 cb.mono_method_is_critical = (gpointer)is_critical_method;
6636 cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
6639 mono_threads_init (&cb, sizeof (SgenThreadInfo));
6641 LOCK_INIT (interruption_mutex);
6642 LOCK_INIT (global_remset_mutex);
6643 LOCK_INIT (pin_queue_mutex);
6645 init_user_copy_or_mark_key ();
6647 if ((env = getenv ("MONO_GC_PARAMS"))) {
6648 opts = g_strsplit (env, ",", -1);
6649 for (ptr = opts; *ptr; ++ptr) {
6651 if (g_str_has_prefix (opt, "major=")) {
6652 opt = strchr (opt, '=') + 1;
6653 major_collector_opt = g_strdup (opt);
6661 mono_sgen_init_internal_allocator ();
6662 mono_sgen_init_nursery_allocator ();
6664 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
6665 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
6666 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
6667 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6668 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
6669 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
6671 mono_native_tls_alloc (&remembered_set_key, NULL);
6673 #ifndef HAVE_KW_THREAD
6674 mono_native_tls_alloc (&thread_info_key, NULL);
6678 * This needs to happen before any internal allocations because
6679 * it inits the small id which is required for hazard pointer
6682 mono_sgen_os_init ();
6684 mono_thread_info_attach (&dummy);
6686 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
6687 mono_sgen_marksweep_init (&major_collector);
6688 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
6689 mono_sgen_marksweep_fixed_init (&major_collector);
6690 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
6691 mono_sgen_marksweep_par_init (&major_collector);
6692 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
6693 mono_sgen_marksweep_fixed_par_init (&major_collector);
6694 } else if (!strcmp (major_collector_opt, "copying")) {
6695 mono_sgen_copying_init (&major_collector);
6697 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
6701 #ifdef SGEN_HAVE_CARDTABLE
6702 use_cardtable = major_collector.supports_cardtable;
6704 use_cardtable = FALSE;
6707 num_workers = mono_cpu_count ();
6708 g_assert (num_workers > 0);
6709 if (num_workers > 16)
6712 ///* Keep this the default for now */
6713 //conservative_stack_mark = TRUE;
6716 for (ptr = opts; *ptr; ++ptr) {
6718 if (g_str_has_prefix (opt, "major="))
6720 if (g_str_has_prefix (opt, "wbarrier=")) {
6721 opt = strchr (opt, '=') + 1;
6722 if (strcmp (opt, "remset") == 0) {
6723 use_cardtable = FALSE;
6724 } else if (strcmp (opt, "cardtable") == 0) {
6725 if (!use_cardtable) {
6726 if (major_collector.supports_cardtable)
6727 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
6729 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
6735 if (g_str_has_prefix (opt, "max-heap-size=")) {
6736 opt = strchr (opt, '=') + 1;
6737 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
6738 if ((max_heap & (mono_pagesize () - 1))) {
6739 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
6743 fprintf (stderr, "max-heap-size must be an integer.\n");
6748 if (g_str_has_prefix (opt, "soft-heap-limit=")) {
6749 opt = strchr (opt, '=') + 1;
6750 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
6751 if (soft_limit <= 0) {
6752 fprintf (stderr, "soft-heap-limit must be positive.\n");
6756 fprintf (stderr, "soft-heap-limit must be an integer.\n");
6761 if (g_str_has_prefix (opt, "workers=")) {
6764 if (!major_collector.is_parallel) {
6765 fprintf (stderr, "The workers= option can only be used for parallel collectors.");
6768 opt = strchr (opt, '=') + 1;
6769 val = strtol (opt, &endptr, 10);
6770 if (!*opt || *endptr) {
6771 fprintf (stderr, "Cannot parse the workers= option value.");
6774 if (val <= 0 || val > 16) {
6775 fprintf (stderr, "The number of workers must be in the range 1 to 16.");
6778 num_workers = (int)val;
6781 if (g_str_has_prefix (opt, "stack-mark=")) {
6782 opt = strchr (opt, '=') + 1;
6783 if (!strcmp (opt, "precise")) {
6784 conservative_stack_mark = FALSE;
6785 } else if (!strcmp (opt, "conservative")) {
6786 conservative_stack_mark = TRUE;
6788 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
6794 if (g_str_has_prefix (opt, "nursery-size=")) {
6796 opt = strchr (opt, '=') + 1;
6797 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
6798 default_nursery_size = val;
6799 #ifdef SGEN_ALIGN_NURSERY
6800 if ((val & (val - 1))) {
6801 fprintf (stderr, "The nursery size must be a power of two.\n");
6805 if (val < SGEN_MAX_NURSERY_WASTE) {
6806 fprintf (stderr, "The nursery size must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE);
6810 default_nursery_bits = 0;
6811 while (1 << (++ default_nursery_bits) != default_nursery_size)
6815 fprintf (stderr, "nursery-size must be an integer.\n");
6821 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
6822 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
6823 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6824 fprintf (stderr, " soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
6825 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6826 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
6827 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
6828 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
6829 if (major_collector.print_gc_param_usage)
6830 major_collector.print_gc_param_usage ();
6837 if (major_collector.is_parallel)
6838 workers_init (num_workers);
6840 if (major_collector_opt)
6841 g_free (major_collector_opt);
6843 nursery_size = DEFAULT_NURSERY_SIZE;
6844 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
6845 init_heap_size_limits (max_heap, soft_limit);
6849 if ((env = getenv ("MONO_GC_DEBUG"))) {
6850 opts = g_strsplit (env, ",", -1);
6851 for (ptr = opts; ptr && *ptr; ptr ++) {
6853 if (opt [0] >= '0' && opt [0] <= '9') {
6854 gc_debug_level = atoi (opt);
6859 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
6860 gc_debug_file = fopen (rf, "wb");
6862 gc_debug_file = stderr;
6865 } else if (!strcmp (opt, "print-allowance")) {
6866 debug_print_allowance = TRUE;
6867 } else if (!strcmp (opt, "print-pinning")) {
6868 do_pin_stats = TRUE;
6869 } else if (!strcmp (opt, "collect-before-allocs")) {
6870 collect_before_allocs = 1;
6871 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
6872 char *arg = strchr (opt, '=') + 1;
6873 collect_before_allocs = atoi (arg);
6874 } else if (!strcmp (opt, "check-at-minor-collections")) {
6875 consistency_check_at_minor_collection = TRUE;
6876 nursery_clear_policy = CLEAR_AT_GC;
6877 } else if (!strcmp (opt, "xdomain-checks")) {
6878 xdomain_checks = TRUE;
6879 } else if (!strcmp (opt, "clear-at-gc")) {
6880 nursery_clear_policy = CLEAR_AT_GC;
6881 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
6882 nursery_clear_policy = CLEAR_AT_GC;
6883 } else if (!strcmp (opt, "check-scan-starts")) {
6884 do_scan_starts_check = TRUE;
6885 } else if (!strcmp (opt, "disable-minor")) {
6886 disable_minor_collections = TRUE;
6887 } else if (!strcmp (opt, "disable-major")) {
6888 disable_major_collections = TRUE;
6889 } else if (g_str_has_prefix (opt, "heap-dump=")) {
6890 char *filename = strchr (opt, '=') + 1;
6891 nursery_clear_policy = CLEAR_AT_GC;
6892 heap_dump_file = fopen (filename, "w");
6893 if (heap_dump_file) {
6894 fprintf (heap_dump_file, "<sgen-dump>\n");
6895 do_pin_stats = TRUE;
6897 #ifdef SGEN_BINARY_PROTOCOL
6898 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
6899 char *filename = strchr (opt, '=') + 1;
6900 binary_protocol_init (filename);
6902 fprintf (stderr, "Warning: Cardtable write barriers will not be binary-protocolled.\n");
6905 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
6906 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
6907 fprintf (stderr, "Valid options are:\n");
6908 fprintf (stderr, " collect-before-allocs[=<n>]\n");
6909 fprintf (stderr, " check-at-minor-collections\n");
6910 fprintf (stderr, " disable-minor\n");
6911 fprintf (stderr, " disable-major\n");
6912 fprintf (stderr, " xdomain-checks\n");
6913 fprintf (stderr, " clear-at-gc\n");
6914 fprintf (stderr, " print-allowance\n");
6915 fprintf (stderr, " print-pinning\n");
6922 if (major_collector.is_parallel) {
6923 if (heap_dump_file) {
6924 fprintf (stderr, "Error: Cannot do heap dump with the parallel collector.\n");
6928 fprintf (stderr, "Error: Cannot gather pinning statistics with the parallel collector.\n");
6933 if (major_collector.post_param_init)
6934 major_collector.post_param_init ();
6936 global_remset = alloc_remset (1024, NULL, FALSE);
6937 global_remset->next = NULL;
6952 #ifdef HAVE_KW_THREAD
6953 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
6954 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6955 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6956 mono_mb_emit_i4 ((mb), (offset)); \
6961 * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
6962 * where the two are the same.
6964 #if defined(__APPLE__) || defined (HOST_WIN32)
6965 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
6966 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6967 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6968 mono_mb_emit_i4 ((mb), thread_info_key); \
6969 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
6970 mono_mb_emit_byte ((mb), CEE_ADD); \
6971 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
6974 #define EMIT_TLS_ACCESS(mb,member,dummy) do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
6979 #ifdef MANAGED_ALLOCATION
6980 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
6981 * for each class. This is currently not easy to do, as it is hard to generate basic
6982 * blocks + branches, but it is easy with the linear IL codebase.
6984 * For this to work we'd need to solve the TLAB race, first. Now we
6985 * require the allocator to be in a few known methods to make sure
6986 * that they are executed atomically via the restart mechanism.
6989 create_allocator (int atype)
6991 int p_var, size_var;
6992 guint32 slowpath_branch, max_size_branch;
6993 MonoMethodBuilder *mb;
6995 MonoMethodSignature *csig;
6996 static gboolean registered = FALSE;
6997 int tlab_next_addr_var, new_next_var;
6999 const char *name = NULL;
7000 AllocatorWrapperInfo *info;
7002 #ifdef HAVE_KW_THREAD
7003 int tlab_next_addr_offset = -1;
7004 int tlab_temp_end_offset = -1;
7006 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7007 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7009 g_assert (tlab_next_addr_offset != -1);
7010 g_assert (tlab_temp_end_offset != -1);
7014 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7015 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7019 if (atype == ATYPE_SMALL) {
7021 name = "AllocSmall";
7022 } else if (atype == ATYPE_NORMAL) {
7025 } else if (atype == ATYPE_VECTOR) {
7027 name = "AllocVector";
7029 g_assert_not_reached ();
7032 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7033 csig->ret = &mono_defaults.object_class->byval_arg;
7034 for (i = 0; i < num_params; ++i)
7035 csig->params [i] = &mono_defaults.int_class->byval_arg;
7037 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7038 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7039 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7040 /* size = vtable->klass->instance_size; */
7041 mono_mb_emit_ldarg (mb, 0);
7042 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7043 mono_mb_emit_byte (mb, CEE_ADD);
7044 mono_mb_emit_byte (mb, CEE_LDIND_I);
7045 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7046 mono_mb_emit_byte (mb, CEE_ADD);
7047 /* FIXME: assert instance_size stays a 4 byte integer */
7048 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7049 mono_mb_emit_stloc (mb, size_var);
7050 } else if (atype == ATYPE_VECTOR) {
7051 MonoExceptionClause *clause;
7053 MonoClass *oom_exc_class;
7056 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7057 mono_mb_emit_ldarg (mb, 1);
7058 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7059 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7060 mono_mb_emit_exception (mb, "OverflowException", NULL);
7061 mono_mb_patch_short_branch (mb, pos);
7063 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7064 clause->try_offset = mono_mb_get_label (mb);
7066 /* vtable->klass->sizes.element_size */
7067 mono_mb_emit_ldarg (mb, 0);
7068 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7069 mono_mb_emit_byte (mb, CEE_ADD);
7070 mono_mb_emit_byte (mb, CEE_LDIND_I);
7071 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7072 mono_mb_emit_byte (mb, CEE_ADD);
7073 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7076 mono_mb_emit_ldarg (mb, 1);
7077 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7078 /* + sizeof (MonoArray) */
7079 mono_mb_emit_icon (mb, sizeof (MonoArray));
7080 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7081 mono_mb_emit_stloc (mb, size_var);
7083 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7086 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7087 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7088 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7089 "System", "OverflowException");
7090 g_assert (clause->data.catch_class);
7091 clause->handler_offset = mono_mb_get_label (mb);
7093 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7094 "System", "OutOfMemoryException");
7095 g_assert (oom_exc_class);
7096 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7099 mono_mb_emit_byte (mb, CEE_POP);
7100 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7101 mono_mb_emit_byte (mb, CEE_THROW);
7103 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7104 mono_mb_set_clauses (mb, 1, clause);
7105 mono_mb_patch_branch (mb, pos_leave);
7108 g_assert_not_reached ();
7111 /* size += ALLOC_ALIGN - 1; */
7112 mono_mb_emit_ldloc (mb, size_var);
7113 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7114 mono_mb_emit_byte (mb, CEE_ADD);
7115 /* size &= ~(ALLOC_ALIGN - 1); */
7116 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7117 mono_mb_emit_byte (mb, CEE_AND);
7118 mono_mb_emit_stloc (mb, size_var);
7120 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7121 if (atype != ATYPE_SMALL) {
7122 mono_mb_emit_ldloc (mb, size_var);
7123 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7124 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7128 * We need to modify tlab_next, but the JIT only supports reading, so we read
7129 * another tls var holding its address instead.
7132 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7133 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7134 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7135 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7137 /* p = (void**)tlab_next; */
7138 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7139 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7140 mono_mb_emit_byte (mb, CEE_LDIND_I);
7141 mono_mb_emit_stloc (mb, p_var);
7143 /* new_next = (char*)p + size; */
7144 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7145 mono_mb_emit_ldloc (mb, p_var);
7146 mono_mb_emit_ldloc (mb, size_var);
7147 mono_mb_emit_byte (mb, CEE_CONV_I);
7148 mono_mb_emit_byte (mb, CEE_ADD);
7149 mono_mb_emit_stloc (mb, new_next_var);
7151 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7152 mono_mb_emit_ldloc (mb, new_next_var);
7153 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7154 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7157 if (atype != ATYPE_SMALL)
7158 mono_mb_patch_short_branch (mb, max_size_branch);
7160 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7161 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7163 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7164 mono_mb_emit_ldarg (mb, 0);
7165 mono_mb_emit_ldloc (mb, size_var);
7166 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7167 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7168 } else if (atype == ATYPE_VECTOR) {
7169 mono_mb_emit_ldarg (mb, 1);
7170 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7172 g_assert_not_reached ();
7174 mono_mb_emit_byte (mb, CEE_RET);
7177 mono_mb_patch_short_branch (mb, slowpath_branch);
7179 /* FIXME: Memory barrier */
7181 /* tlab_next = new_next */
7182 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7183 mono_mb_emit_ldloc (mb, new_next_var);
7184 mono_mb_emit_byte (mb, CEE_STIND_I);
7186 /*The tlab store must be visible before the the vtable store. This could be replaced with a DDS but doing it with IL would be tricky. */
7187 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);
7188 mono_mb_emit_op (mb, CEE_MONO_MEMORY_BARRIER, StoreStoreBarrier);
7191 mono_mb_emit_ldloc (mb, p_var);
7192 mono_mb_emit_ldarg (mb, 0);
7193 mono_mb_emit_byte (mb, CEE_STIND_I);
7195 if (atype == ATYPE_VECTOR) {
7196 /* arr->max_length = max_length; */
7197 mono_mb_emit_ldloc (mb, p_var);
7198 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7199 mono_mb_emit_ldarg (mb, 1);
7200 mono_mb_emit_byte (mb, CEE_STIND_I);
7204 We must make sure both vtable and max_length are globaly visible before returning to managed land.
7206 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);
7207 mono_mb_emit_op (mb, CEE_MONO_MEMORY_BARRIER, StoreStoreBarrier);
7210 mono_mb_emit_ldloc (mb, p_var);
7211 mono_mb_emit_byte (mb, CEE_RET);
7213 res = mono_mb_create_method (mb, csig, 8);
7215 mono_method_get_header (res)->init_locals = FALSE;
7217 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7218 info->gc_name = "sgen";
7219 info->alloc_type = atype;
7220 mono_marshal_set_wrapper_info (res, info);
7227 mono_gc_get_gc_name (void)
7232 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7233 static MonoMethod *write_barrier_method;
7236 mono_gc_is_critical_method (MonoMethod *method)
7239 if (method == write_barrier_method)
7242 for (i = 0; i < ATYPE_NUM; ++i)
7243 if (method == alloc_method_cache [i])
7250 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7254 if (!mono_thread_internal_current ())
7255 /* Happens during thread attach */
7260 ji = mono_jit_info_table_find (domain, ip);
7264 return mono_gc_is_critical_method (ji->method);
7268 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7269 * The signature of the called method is:
7270 * object allocate (MonoVTable *vtable)
7273 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7275 #ifdef MANAGED_ALLOCATION
7276 MonoClass *klass = vtable->klass;
7278 #ifdef HAVE_KW_THREAD
7279 int tlab_next_offset = -1;
7280 int tlab_temp_end_offset = -1;
7281 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7282 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7284 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7288 if (!mono_runtime_has_tls_get ())
7290 if (klass->instance_size > tlab_size)
7292 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7296 if (klass->byval_arg.type == MONO_TYPE_STRING)
7298 if (collect_before_allocs)
7301 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7302 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7304 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7311 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7313 #ifdef MANAGED_ALLOCATION
7314 MonoClass *klass = vtable->klass;
7316 #ifdef HAVE_KW_THREAD
7317 int tlab_next_offset = -1;
7318 int tlab_temp_end_offset = -1;
7319 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7320 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7322 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7328 if (!mono_runtime_has_tls_get ())
7330 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7332 if (collect_before_allocs)
7334 g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
7336 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7343 mono_gc_get_managed_allocator_by_type (int atype)
7345 #ifdef MANAGED_ALLOCATION
7348 if (!mono_runtime_has_tls_get ())
7351 mono_loader_lock ();
7352 res = alloc_method_cache [atype];
7354 res = alloc_method_cache [atype] = create_allocator (atype);
7355 mono_loader_unlock ();
7363 mono_gc_get_managed_allocator_types (void)
7369 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
7371 memset (nursery_check_return_labels, 0, sizeof (int) * 3);
7372 #ifdef SGEN_ALIGN_NURSERY
7373 // if (ptr_in_nursery (ptr)) return;
7375 * Masking out the bits might be faster, but we would have to use 64 bit
7376 * immediates, which might be slower.
7378 mono_mb_emit_ldarg (mb, 0);
7379 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7380 mono_mb_emit_byte (mb, CEE_SHR_UN);
7381 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7382 nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
7384 // if (!ptr_in_nursery (*ptr)) return;
7385 mono_mb_emit_ldarg (mb, 0);
7386 mono_mb_emit_byte (mb, CEE_LDIND_I);
7387 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7388 mono_mb_emit_byte (mb, CEE_SHR_UN);
7389 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7390 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
7392 int label_continue1, label_continue2;
7393 int dereferenced_var;
7395 // if (ptr < (nursery_start)) goto continue;
7396 mono_mb_emit_ldarg (mb, 0);
7397 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7398 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7400 // if (ptr >= nursery_end)) goto continue;
7401 mono_mb_emit_ldarg (mb, 0);
7402 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7403 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7406 nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BR);
7409 mono_mb_patch_branch (mb, label_continue_1);
7410 mono_mb_patch_branch (mb, label_continue_2);
7412 // Dereference and store in local var
7413 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7414 mono_mb_emit_ldarg (mb, 0);
7415 mono_mb_emit_byte (mb, CEE_LDIND_I);
7416 mono_mb_emit_stloc (mb, dereferenced_var);
7418 // if (*ptr < nursery_start) return;
7419 mono_mb_emit_ldloc (mb, dereferenced_var);
7420 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7421 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BLT);
7423 // if (*ptr >= nursery_end) return;
7424 mono_mb_emit_ldloc (mb, dereferenced_var);
7425 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7426 nursery_check_return_labels [2] = mono_mb_emit_branch (mb, CEE_BGE);
7431 mono_gc_get_write_barrier (void)
7434 MonoMethodBuilder *mb;
7435 MonoMethodSignature *sig;
7436 #ifdef MANAGED_WBARRIER
7437 int i, nursery_check_labels [3];
7438 int label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7439 int buffer_var, buffer_index_var, dummy_var;
7441 #ifdef HAVE_KW_THREAD
7442 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7443 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7445 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7446 g_assert (stack_end_offset != -1);
7447 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7448 g_assert (store_remset_buffer_offset != -1);
7449 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7450 g_assert (store_remset_buffer_index_offset != -1);
7451 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7452 g_assert (store_remset_buffer_index_addr_offset != -1);
7456 // FIXME: Maybe create a separate version for ctors (the branch would be
7457 // correctly predicted more times)
7458 if (write_barrier_method)
7459 return write_barrier_method;
7461 /* Create the IL version of mono_gc_barrier_generic_store () */
7462 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7463 sig->ret = &mono_defaults.void_class->byval_arg;
7464 sig->params [0] = &mono_defaults.int_class->byval_arg;
7466 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7468 #ifdef MANAGED_WBARRIER
7469 if (use_cardtable) {
7470 emit_nursery_check (mb, nursery_check_labels);
7472 addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
7476 LDC_PTR sgen_cardtable
7478 address >> CARD_BITS
7482 if (SGEN_HAVE_OVERLAPPING_CARDS) {
7483 LDC_PTR card_table_mask
7490 mono_mb_emit_ptr (mb, sgen_cardtable);
7491 mono_mb_emit_ldarg (mb, 0);
7492 mono_mb_emit_icon (mb, CARD_BITS);
7493 mono_mb_emit_byte (mb, CEE_SHR_UN);
7494 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
7495 mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
7496 mono_mb_emit_byte (mb, CEE_AND);
7498 mono_mb_emit_byte (mb, CEE_ADD);
7499 mono_mb_emit_icon (mb, 1);
7500 mono_mb_emit_byte (mb, CEE_STIND_I1);
7503 for (i = 0; i < 3; ++i) {
7504 if (nursery_check_labels [i])
7505 mono_mb_patch_branch (mb, nursery_check_labels [i]);
7507 mono_mb_emit_byte (mb, CEE_RET);
7508 } else if (mono_runtime_has_tls_get ()) {
7509 emit_nursery_check (mb, nursery_check_labels);
7511 // if (ptr >= stack_end) goto need_wb;
7512 mono_mb_emit_ldarg (mb, 0);
7513 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7514 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7516 // if (ptr >= stack_start) return;
7517 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7518 mono_mb_emit_ldarg (mb, 0);
7519 mono_mb_emit_ldloc_addr (mb, dummy_var);
7520 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7523 mono_mb_patch_branch (mb, label_need_wb);
7525 // buffer = STORE_REMSET_BUFFER;
7526 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7527 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7528 mono_mb_emit_stloc (mb, buffer_var);
7530 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7531 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7532 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7533 mono_mb_emit_stloc (mb, buffer_index_var);
7535 // if (buffer [buffer_index] == ptr) return;
7536 mono_mb_emit_ldloc (mb, buffer_var);
7537 mono_mb_emit_ldloc (mb, buffer_index_var);
7538 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7539 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7540 mono_mb_emit_byte (mb, CEE_SHL);
7541 mono_mb_emit_byte (mb, CEE_ADD);
7542 mono_mb_emit_byte (mb, CEE_LDIND_I);
7543 mono_mb_emit_ldarg (mb, 0);
7544 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7547 mono_mb_emit_ldloc (mb, buffer_index_var);
7548 mono_mb_emit_icon (mb, 1);
7549 mono_mb_emit_byte (mb, CEE_ADD);
7550 mono_mb_emit_stloc (mb, buffer_index_var);
7552 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7553 mono_mb_emit_ldloc (mb, buffer_index_var);
7554 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7555 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7557 // buffer [buffer_index] = ptr;
7558 mono_mb_emit_ldloc (mb, buffer_var);
7559 mono_mb_emit_ldloc (mb, buffer_index_var);
7560 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7561 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7562 mono_mb_emit_byte (mb, CEE_SHL);
7563 mono_mb_emit_byte (mb, CEE_ADD);
7564 mono_mb_emit_ldarg (mb, 0);
7565 mono_mb_emit_byte (mb, CEE_STIND_I);
7567 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7568 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7569 mono_mb_emit_ldloc (mb, buffer_index_var);
7570 mono_mb_emit_byte (mb, CEE_STIND_I);
7573 for (i = 0; i < 3; ++i) {
7574 if (nursery_check_labels [i])
7575 mono_mb_patch_branch (mb, nursery_check_labels [i]);
7577 mono_mb_patch_branch (mb, label_no_wb_3);
7578 mono_mb_patch_branch (mb, label_no_wb_4);
7579 mono_mb_emit_byte (mb, CEE_RET);
7582 mono_mb_patch_branch (mb, label_slow_path);
7584 mono_mb_emit_ldarg (mb, 0);
7585 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7586 mono_mb_emit_byte (mb, CEE_RET);
7590 mono_mb_emit_ldarg (mb, 0);
7591 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7592 mono_mb_emit_byte (mb, CEE_RET);
7595 res = mono_mb_create_method (mb, sig, 16);
7598 mono_loader_lock ();
7599 if (write_barrier_method) {
7600 /* Already created */
7601 mono_free_method (res);
7603 /* double-checked locking */
7604 mono_memory_barrier ();
7605 write_barrier_method = res;
7607 mono_loader_unlock ();
7609 return write_barrier_method;
7613 mono_gc_get_description (void)
7615 return g_strdup ("sgen");
7619 mono_gc_set_desktop_mode (void)
7624 mono_gc_is_moving (void)
7630 mono_gc_is_disabled (void)
7636 mono_sgen_debug_printf (int level, const char *format, ...)
7640 if (level > gc_debug_level)
7643 va_start (ap, format);
7644 vfprintf (gc_debug_file, format, ap);
7649 mono_sgen_get_logfile (void)
7651 return gc_debug_file;
7655 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
7662 mono_sgen_get_nursery_clear_policy (void)
7664 return nursery_clear_policy;
7668 mono_sgen_get_array_fill_vtable (void)
7670 if (!array_fill_vtable) {
7671 static MonoClass klass;
7672 static MonoVTable vtable;
7674 MonoDomain *domain = mono_get_root_domain ();
7677 klass.element_class = mono_defaults.byte_class;
7679 klass.instance_size = sizeof (MonoArray);
7680 klass.sizes.element_size = 1;
7682 vtable.klass = &klass;
7683 vtable.gc_descr = NULL;
7686 array_fill_vtable = &vtable;
7688 return array_fill_vtable;
7692 mono_sgen_gc_lock (void)
7698 mono_sgen_gc_unlock (void)
7703 #endif /* HAVE_SGEN_GC */