2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
15 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
18 * Permission is hereby granted to use or copy this program
19 * for any purpose, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
25 * Copyright 2001-2003 Ximian, Inc
26 * Copyright 2003-2010 Novell, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining
29 * a copy of this software and associated documentation files (the
30 * "Software"), to deal in the Software without restriction, including
31 * without limitation the rights to use, copy, modify, merge, publish,
32 * distribute, sublicense, and/or sell copies of the Software, and to
33 * permit persons to whom the Software is furnished to do so, subject to
34 * the following conditions:
36 * The above copyright notice and this permission notice shall be
37 * included in all copies or substantial portions of the Software.
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 * Important: allocation provides always zeroed memory, having to do
49 * a memset after allocation is deadly for performance.
50 * Memory usage at startup is currently as follows:
52 * 64 KB internal space
54 * We should provide a small memory config with half the sizes
56 * We currently try to make as few mono assumptions as possible:
57 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
59 * 2) gc descriptor is the second word in the vtable (first word in the class)
60 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
61 * 4) there is a function to get an object's size and the number of
62 * elements in an array.
63 * 5) we know the special way bounds are allocated for complex arrays
64 * 6) we know about proxies and how to treat them when domains are unloaded
66 * Always try to keep stack usage to a minimum: no recursive behaviour
67 * and no large stack allocs.
69 * General description.
70 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
71 * When the nursery is full we start a nursery collection: this is performed with a
73 * When the old generation is full we start a copying GC of the old generation as well:
74 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
75 * in the future. Maybe we'll even do both during the same collection like IMMIX.
77 * The things that complicate this description are:
78 * *) pinned objects: we can't move them so we need to keep track of them
79 * *) no precise info of the thread stacks and registers: we need to be able to
80 * quickly find the objects that may be referenced conservatively and pin them
81 * (this makes the first issues more important)
82 * *) large objects are too expensive to be dealt with using copying GC: we handle them
83 * with mark/sweep during major collections
84 * *) some objects need to not move even if they are small (interned strings, Type handles):
85 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
86 * PinnedChunks regions
92 *) we could have a function pointer in MonoClass to implement
93 customized write barriers for value types
95 *) investigate the stuff needed to advance a thread to a GC-safe
96 point (single-stepping, read from unmapped memory etc) and implement it.
97 This would enable us to inline allocations and write barriers, for example,
98 or at least parts of them, like the write barrier checks.
99 We may need this also for handling precise info on stacks, even simple things
100 as having uninitialized data on the stack and having to wait for the prolog
101 to zero it. Not an issue for the last frame that we scan conservatively.
102 We could always not trust the value in the slots anyway.
104 *) modify the jit to save info about references in stack locations:
105 this can be done just for locals as a start, so that at least
106 part of the stack is handled precisely.
108 *) test/fix endianess issues
110 *) Implement a card table as the write barrier instead of remembered
111 sets? Card tables are not easy to implement with our current
112 memory layout. We have several different kinds of major heap
113 objects: Small objects in regular blocks, small objects in pinned
114 chunks and LOS objects. If we just have a pointer we have no way
115 to tell which kind of object it points into, therefore we cannot
116 know where its card table is. The least we have to do to make
117 this happen is to get rid of write barriers for indirect stores.
120 *) Get rid of write barriers for indirect stores. We can do this by
121 telling the GC to wbarrier-register an object once we do an ldloca
122 or ldelema on it, and to unregister it once it's not used anymore
123 (it can only travel downwards on the stack). The problem with
124 unregistering is that it needs to happen eventually no matter
125 what, even if exceptions are thrown, the thread aborts, etc.
126 Rodrigo suggested that we could do only the registering part and
127 let the collector find out (pessimistically) when it's safe to
128 unregister, namely when the stack pointer of the thread that
129 registered the object is higher than it was when the registering
130 happened. This might make for a good first implementation to get
131 some data on performance.
133 *) Some sort of blacklist support? Blacklists is a concept from the
134 Boehm GC: if during a conservative scan we find pointers to an
135 area which we might use as heap, we mark that area as unusable, so
136 pointer retention by random pinning pointers is reduced.
138 *) experiment with max small object size (very small right now - 2kb,
139 because it's tied to the max freelist size)
141 *) add an option to mmap the whole heap in one chunk: it makes for many
142 simplifications in the checks (put the nursery at the top and just use a single
143 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
144 not flexible (too much of the address space may be used by default or we can't
145 increase the heap as needed) and we'd need a race-free mechanism to return memory
146 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
147 was written to, munmap is needed, but the following mmap may not find the same segment
150 *) memzero the major fragments after restarting the world and optionally a smaller
153 *) investigate having fragment zeroing threads
155 *) separate locks for finalization and other minor stuff to reduce
158 *) try a different copying order to improve memory locality
160 *) a thread abort after a store but before the write barrier will
161 prevent the write barrier from executing
163 *) specialized dynamically generated markers/copiers
165 *) Dynamically adjust TLAB size to the number of threads. If we have
166 too many threads that do allocation, we might need smaller TLABs,
167 and we might get better performance with larger TLABs if we only
168 have a handful of threads. We could sum up the space left in all
169 assigned TLABs and if that's more than some percentage of the
170 nursery size, reduce the TLAB size.
172 *) Explore placing unreachable objects on unused nursery memory.
173 Instead of memset'ng a region to zero, place an int[] covering it.
174 A good place to start is add_nursery_frag. The tricky thing here is
175 placing those objects atomically outside of a collection.
185 #include <semaphore.h>
194 #define _XOPEN_SOURCE
197 #include "metadata/sgen-gc.h"
198 #include "metadata/metadata-internals.h"
199 #include "metadata/class-internals.h"
200 #include "metadata/gc-internal.h"
201 #include "metadata/object-internals.h"
202 #include "metadata/threads.h"
203 #include "metadata/sgen-cardtable.h"
204 #include "metadata/sgen-protocol.h"
205 #include "metadata/sgen-archdep.h"
206 #include "metadata/sgen-bridge.h"
207 #include "metadata/mono-gc.h"
208 #include "metadata/method-builder.h"
209 #include "metadata/profiler-private.h"
210 #include "metadata/monitor.h"
211 #include "metadata/threadpool-internals.h"
212 #include "metadata/mempool-internals.h"
213 #include "metadata/marshal.h"
214 #include "metadata/runtime.h"
215 #include "utils/mono-mmap.h"
216 #include "utils/mono-time.h"
217 #include "utils/mono-semaphore.h"
218 #include "utils/mono-counters.h"
219 #include "utils/mono-proclib.h"
221 #include <mono/utils/memcheck.h>
223 #if defined(__MACH__)
224 #include "utils/mach-support.h"
227 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
231 #include "mono/cil/opcode.def"
237 #undef pthread_create
239 #undef pthread_detach
242 * ######################################################################
243 * ######## Types and constants used by the GC.
244 * ######################################################################
247 /* 0 means not initialized, 1 is initialized, -1 means in progress */
248 static gint32 gc_initialized = 0;
249 /* If set, do a minor collection before every X allocation */
250 static guint32 collect_before_allocs = 0;
251 /* If set, do a heap consistency check before each minor collection */
252 static gboolean consistency_check_at_minor_collection = FALSE;
253 /* If set, check that there are no references to the domain left at domain unload */
254 static gboolean xdomain_checks = FALSE;
255 /* If not null, dump the heap after each collection into this file */
256 static FILE *heap_dump_file = NULL;
257 /* If set, mark stacks conservatively, even if precise marking is possible */
258 static gboolean conservative_stack_mark = FALSE;
259 /* If set, do a plausibility check on the scan_starts before and after
261 static gboolean do_scan_starts_check = FALSE;
262 static gboolean disable_minor_collections = FALSE;
263 static gboolean disable_major_collections = FALSE;
265 #ifdef HEAVY_STATISTICS
266 static long long stat_objects_alloced = 0;
267 static long long stat_bytes_alloced = 0;
268 long long stat_objects_alloced_degraded = 0;
269 long long stat_bytes_alloced_degraded = 0;
270 static long long stat_bytes_alloced_los = 0;
272 long long stat_copy_object_called_nursery = 0;
273 long long stat_objects_copied_nursery = 0;
274 long long stat_copy_object_called_major = 0;
275 long long stat_objects_copied_major = 0;
277 long long stat_scan_object_called_nursery = 0;
278 long long stat_scan_object_called_major = 0;
280 long long stat_nursery_copy_object_failed_from_space = 0;
281 long long stat_nursery_copy_object_failed_forwarded = 0;
282 long long stat_nursery_copy_object_failed_pinned = 0;
284 static long long stat_store_remsets = 0;
285 static long long stat_store_remsets_unique = 0;
286 static long long stat_saved_remsets_1 = 0;
287 static long long stat_saved_remsets_2 = 0;
288 static long long stat_local_remsets_processed = 0;
289 static long long stat_global_remsets_added = 0;
290 static long long stat_global_remsets_readded = 0;
291 static long long stat_global_remsets_processed = 0;
292 static long long stat_global_remsets_discarded = 0;
294 static int stat_wbarrier_set_field = 0;
295 static int stat_wbarrier_set_arrayref = 0;
296 static int stat_wbarrier_arrayref_copy = 0;
297 static int stat_wbarrier_generic_store = 0;
298 static int stat_wbarrier_generic_store_remset = 0;
299 static int stat_wbarrier_set_root = 0;
300 static int stat_wbarrier_value_copy = 0;
301 static int stat_wbarrier_object_copy = 0;
304 static long long stat_pinned_objects = 0;
306 static long long time_minor_pre_collection_fragment_clear = 0;
307 static long long time_minor_pinning = 0;
308 static long long time_minor_scan_remsets = 0;
309 static long long time_minor_scan_card_table = 0;
310 static long long time_minor_scan_pinned = 0;
311 static long long time_minor_scan_registered_roots = 0;
312 static long long time_minor_scan_thread_data = 0;
313 static long long time_minor_finish_gray_stack = 0;
314 static long long time_minor_fragment_creation = 0;
316 static long long time_major_pre_collection_fragment_clear = 0;
317 static long long time_major_pinning = 0;
318 static long long time_major_scan_pinned = 0;
319 static long long time_major_scan_registered_roots = 0;
320 static long long time_major_scan_thread_data = 0;
321 static long long time_major_scan_alloc_pinned = 0;
322 static long long time_major_scan_finalized = 0;
323 static long long time_major_scan_big_objects = 0;
324 static long long time_major_finish_gray_stack = 0;
325 static long long time_major_free_bigobjs = 0;
326 static long long time_major_los_sweep = 0;
327 static long long time_major_sweep = 0;
328 static long long time_major_fragment_creation = 0;
330 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
332 int gc_debug_level = 0;
337 mono_gc_flush_info (void)
339 fflush (gc_debug_file);
344 * Define this to allow the user to change the nursery size by
345 * specifying its value in the MONO_GC_PARAMS environmental
346 * variable. See mono_gc_base_init for details.
348 #define USER_CONFIG 1
350 #define TV_DECLARE SGEN_TV_DECLARE
351 #define TV_GETTIME SGEN_TV_GETTIME
352 #define TV_ELAPSED SGEN_TV_ELAPSED
353 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
355 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
357 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
359 /* the runtime can register areas of memory as roots: we keep two lists of roots,
360 * a pinned root set for conservatively scanned roots and a normal one for
361 * precisely scanned roots (currently implemented as a single list).
363 typedef struct _RootRecord RootRecord;
372 * We're never actually using the first element. It's always set to
373 * NULL to simplify the elimination of consecutive duplicate
376 #define STORE_REMSET_BUFFER_SIZE 1023
378 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
379 struct _GenericStoreRememberedSet {
380 GenericStoreRememberedSet *next;
381 /* We need one entry less because the first entry of store
382 remset buffers is always a dummy and we don't copy it. */
383 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
386 /* we have 4 possible values in the low 2 bits */
388 REMSET_LOCATION, /* just a pointer to the exact location */
389 REMSET_RANGE, /* range of pointer fields */
390 REMSET_OBJECT, /* mark all the object for scanning */
391 REMSET_VTYPE, /* a valuetype array described by a gc descriptor, a count and a size */
392 REMSET_TYPE_MASK = 0x3
395 #ifdef HAVE_KW_THREAD
396 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
398 static pthread_key_t remembered_set_key;
399 static RememberedSet *global_remset;
400 static RememberedSet *freed_thread_remsets;
401 static GenericStoreRememberedSet *generic_store_remsets = NULL;
403 /*A two slots cache for recently inserted remsets */
404 static gpointer global_remset_cache [2];
406 /* FIXME: later choose a size that takes into account the RememberedSet struct
407 * and doesn't waste any alloc paddin space.
409 #define DEFAULT_REMSET_SIZE 1024
410 static RememberedSet* alloc_remset (int size, gpointer id, gboolean global);
412 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
413 #define object_is_pinned SGEN_OBJECT_IS_PINNED
414 #define pin_object SGEN_PIN_OBJECT
415 #define unpin_object SGEN_UNPIN_OBJECT
417 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_end))
419 #define LOAD_VTABLE SGEN_LOAD_VTABLE
422 safe_name (void* obj)
424 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
425 return vt->klass->name;
428 #define safe_object_get_size mono_sgen_safe_object_get_size
431 mono_sgen_safe_name (void* obj)
433 return safe_name (obj);
437 * ######################################################################
438 * ######## Global data.
439 * ######################################################################
441 static LOCK_DECLARE (gc_mutex);
442 static int gc_disabled = 0;
443 static int num_minor_gcs = 0;
444 static int num_major_gcs = 0;
446 static gboolean use_cardtable;
450 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
451 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
452 static int default_nursery_size = (1 << 22);
453 #ifdef SGEN_ALIGN_NURSERY
454 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
455 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
456 static int default_nursery_bits = 22;
461 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
462 #ifdef SGEN_ALIGN_NURSERY
463 #define DEFAULT_NURSERY_BITS 22
468 #ifndef SGEN_ALIGN_NURSERY
469 #define DEFAULT_NURSERY_BITS -1
472 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
474 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
476 static mword pagesize = 4096;
477 static mword nursery_size;
478 static int degraded_mode = 0;
480 static mword total_alloc = 0;
481 /* use this to tune when to do a major/minor collection */
482 static mword memory_pressure = 0;
483 static mword minor_collection_allowance;
484 static int minor_collection_sections_alloced = 0;
486 static GCMemSection *nursery_section = NULL;
487 static mword lowest_heap_address = ~(mword)0;
488 static mword highest_heap_address = 0;
490 static LOCK_DECLARE (interruption_mutex);
491 static LOCK_DECLARE (global_remset_mutex);
492 static LOCK_DECLARE (pin_queue_mutex);
494 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
495 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
497 #define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
498 #define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
500 typedef struct _FinalizeEntry FinalizeEntry;
501 struct _FinalizeEntry {
506 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
507 struct _FinalizeEntryHashTable {
508 FinalizeEntry **table;
513 typedef struct _DisappearingLink DisappearingLink;
514 struct _DisappearingLink {
515 DisappearingLink *next;
519 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
520 struct _DisappearingLinkHashTable {
521 DisappearingLink **table;
526 typedef struct _EphemeronLinkNode EphemeronLinkNode;
528 struct _EphemeronLinkNode {
529 EphemeronLinkNode *next;
538 int current_collection_generation = -1;
541 * The link pointer is hidden by negating each bit. We use the lowest
542 * bit of the link (before negation) to store whether it needs
543 * resurrection tracking.
545 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
546 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
548 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
549 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
551 /* objects that are ready to be finalized */
552 static FinalizeEntry *fin_ready_list = NULL;
553 static FinalizeEntry *critical_fin_list = NULL;
555 static EphemeronLinkNode *ephemeron_list;
557 static int num_ready_finalizers = 0;
558 static int no_finalize = 0;
561 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
562 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
563 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
567 /* registered roots: the key to the hash is the root start address */
569 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
571 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
572 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
573 static mword roots_size = 0; /* amount of memory in the root set */
574 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
576 #define GC_ROOT_NUM 32
579 void *objects [GC_ROOT_NUM];
580 int root_types [GC_ROOT_NUM];
581 uintptr_t extra_info [GC_ROOT_NUM];
585 notify_gc_roots (GCRootReport *report)
589 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
594 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
596 if (report->count == GC_ROOT_NUM)
597 notify_gc_roots (report);
598 report->objects [report->count] = object;
599 report->root_types [report->count] = rtype;
600 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
604 * The current allocation cursors
605 * We allocate objects in the nursery.
606 * The nursery is the area between nursery_start and nursery_end.
607 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
608 * from nursery fragments.
609 * tlab_next is the pointer to the space inside the TLAB where the next object will
611 * tlab_temp_end is the pointer to the end of the temporary space reserved for
612 * the allocation: it allows us to set the scan starts at reasonable intervals.
613 * tlab_real_end points to the end of the TLAB.
614 * nursery_frag_real_end points to the end of the currently used nursery fragment.
615 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
616 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
617 * At the next allocation, the area of the nursery where objects can be present is
618 * between MIN(nursery_first_pinned_start, first_fragment_start) and
619 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
621 static char *nursery_start = NULL;
622 static char *nursery_end = NULL;
623 static char *nursery_alloc_bound = NULL;
625 #ifdef HAVE_KW_THREAD
626 #define TLAB_ACCESS_INIT
627 #define TLAB_START tlab_start
628 #define TLAB_NEXT tlab_next
629 #define TLAB_TEMP_END tlab_temp_end
630 #define TLAB_REAL_END tlab_real_end
631 #define REMEMBERED_SET remembered_set
632 #define STORE_REMSET_BUFFER store_remset_buffer
633 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
634 #define IN_CRITICAL_REGION thread_info->in_critical_region
636 static pthread_key_t thread_info_key;
637 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
638 #define TLAB_START (__thread_info__->tlab_start)
639 #define TLAB_NEXT (__thread_info__->tlab_next)
640 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
641 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
642 #define REMEMBERED_SET (__thread_info__->remset)
643 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
644 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
645 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
648 #ifndef DISABLE_CRITICAL_REGION
649 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
650 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
651 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
655 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
656 * variables for next+temp_end ?
658 #ifdef HAVE_KW_THREAD
659 static __thread SgenThreadInfo *thread_info;
660 static __thread char *tlab_start;
661 static __thread char *tlab_next;
662 static __thread char *tlab_temp_end;
663 static __thread char *tlab_real_end;
664 static __thread gpointer *store_remset_buffer;
665 static __thread long store_remset_buffer_index;
666 /* Used by the managed allocator/wbarrier */
667 static __thread char **tlab_next_addr;
668 static __thread char *stack_end;
669 static __thread long *store_remset_buffer_index_addr;
672 /* The size of a TLAB */
673 /* The bigger the value, the less often we have to go to the slow path to allocate a new
674 * one, but the more space is wasted by threads not allocating much memory.
676 * FIXME: Make this self-tuning for each thread.
678 static guint32 tlab_size = (1024 * 4);
680 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
682 /* Functions supplied by the runtime to be called by the GC */
683 static MonoGCCallbacks gc_callbacks;
685 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
686 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
688 #define ALIGN_UP SGEN_ALIGN_UP
690 #define MOVED_OBJECTS_NUM 64
691 static void *moved_objects [MOVED_OBJECTS_NUM];
692 static int moved_objects_idx = 0;
694 /* Vtable of the objects used to fill out nursery fragments before a collection */
695 static MonoVTable *array_fill_vtable;
697 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
698 pthread_t main_gc_thread = NULL;
702 * ######################################################################
703 * ######## Heap size accounting
704 * ######################################################################
707 static mword max_heap_size = ((mword)0)- ((mword)1);
708 static mword allocated_heap;
710 /*Object was pinned during the current collection*/
711 static mword objects_pinned;
714 mono_sgen_release_space (mword size, int space)
716 allocated_heap -= size;
720 available_free_space (void)
722 return max_heap_size - MIN (allocated_heap, max_heap_size);
726 mono_sgen_try_alloc_space (mword size, int space)
728 if (available_free_space () < size)
731 allocated_heap += size;
736 init_heap_size_limits (glong max_heap)
741 if (max_heap < nursery_size * 4) {
742 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
745 max_heap_size = max_heap - nursery_size;
749 * ######################################################################
750 * ######## Macros and function declarations.
751 * ######################################################################
755 align_pointer (void *ptr)
757 mword p = (mword)ptr;
758 p += sizeof (gpointer) - 1;
759 p &= ~ (sizeof (gpointer) - 1);
763 typedef SgenGrayQueue GrayQueue;
765 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
766 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
768 /* forward declarations */
769 static int stop_world (int generation);
770 static int restart_world (int generation);
771 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
772 static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
773 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
774 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
775 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
776 static void report_finalizer_roots (void);
777 static void report_registered_roots (void);
778 static void find_pinning_ref_from_thread (char *obj, size_t size);
779 static void update_current_thread_stack (void *start);
780 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
781 static void process_fin_stage_entries (void);
782 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
783 static void null_links_for_domain (MonoDomain *domain, int generation);
784 static void process_dislink_stage_entries (void);
786 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
787 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
788 static void optimize_pin_queue (int start_slot);
789 static void clear_remsets (void);
790 static void clear_tlabs (void);
791 static void sort_addresses (void **array, int size);
792 static gboolean drain_gray_stack (GrayQueue *queue, int max_objs);
793 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
794 static gboolean need_major_collection (mword space_needed);
795 static void major_collection (const char *reason);
797 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc);
798 static gboolean mono_gc_is_critical_method (MonoMethod *method);
800 void describe_ptr (char *ptr);
801 void check_object (char *start);
803 static void check_consistency (void);
804 static void check_major_refs (void);
805 static void check_scan_starts (void);
806 static void check_for_xdomain_refs (void);
807 static void dump_heap (const char *type, int num, const char *reason);
809 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
811 static void init_stats (void);
813 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
814 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
815 static void null_ephemerons_for_domain (MonoDomain *domain);
817 SgenMajorCollector major_collector;
819 #include "sgen-pinning.c"
820 #include "sgen-pinning-stats.c"
821 #include "sgen-gray.c"
822 #include "sgen-workers.c"
823 #include "sgen-cardtable.c"
825 /* Root bitmap descriptors are simpler: the lower three bits describe the type
826 * and we either have 30/62 bitmap bits or nibble-based run-length,
827 * or a complex descriptor, or a user defined marker function.
830 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
835 ROOT_DESC_TYPE_MASK = 0x7,
836 ROOT_DESC_TYPE_SHIFT = 3,
839 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
841 #define MAX_USER_DESCRIPTORS 16
843 static gsize* complex_descriptors = NULL;
844 static int complex_descriptors_size = 0;
845 static int complex_descriptors_next = 0;
846 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
847 static int user_descriptors_next = 0;
850 alloc_complex_descriptor (gsize *bitmap, int numbits)
854 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
855 nwords = numbits / GC_BITS_PER_WORD + 1;
858 res = complex_descriptors_next;
859 /* linear search, so we don't have duplicates with domain load/unload
860 * this should not be performance critical or we'd have bigger issues
861 * (the number and size of complex descriptors should be small).
863 for (i = 0; i < complex_descriptors_next; ) {
864 if (complex_descriptors [i] == nwords) {
866 for (j = 0; j < nwords - 1; ++j) {
867 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
877 i += complex_descriptors [i];
879 if (complex_descriptors_next + nwords > complex_descriptors_size) {
880 int new_size = complex_descriptors_size * 2 + nwords;
881 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
882 complex_descriptors_size = new_size;
884 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
885 complex_descriptors_next += nwords;
886 complex_descriptors [res] = nwords;
887 for (i = 0; i < nwords - 1; ++i) {
888 complex_descriptors [res + 1 + i] = bitmap [i];
889 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
896 mono_sgen_get_complex_descriptor (mword desc)
898 return complex_descriptors + (desc >> LOW_TYPE_BITS);
902 * Descriptor builders.
905 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
907 return (void*) DESC_TYPE_RUN_LENGTH;
911 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
913 int first_set = -1, num_set = 0, last_set = -1, i;
915 size_t stored_size = obj_size;
916 for (i = 0; i < numbits; ++i) {
917 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
925 * We don't encode the size of types that don't contain
926 * references because they might not be aligned, i.e. the
927 * bottom two bits might be set, which would clash with the
928 * bits we need to encode the descriptor type. Since we don't
929 * use the encoded size to skip objects, other than for
930 * processing remsets, in which case only the positions of
931 * references are relevant, this is not a problem.
934 return (void*)DESC_TYPE_RUN_LENGTH;
935 g_assert (!(stored_size & 0x3));
936 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
937 /* check run-length encoding first: one byte offset, one byte number of pointers
938 * on 64 bit archs, we can have 3 runs, just one on 32.
939 * It may be better to use nibbles.
942 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
943 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
945 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
946 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
947 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));
951 /* we know the 2-word header is ptr-free */
952 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
953 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
954 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
957 /* it's a complex object ... */
958 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
962 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
964 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
966 int first_set = -1, num_set = 0, last_set = -1, i;
967 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
968 for (i = 0; i < numbits; ++i) {
969 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
976 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
978 return (void*)DESC_TYPE_RUN_LENGTH;
979 if (elem_size <= MAX_ELEMENT_SIZE) {
980 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
982 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
984 /* Note: we also handle structs with just ref fields */
985 if (num_set * sizeof (gpointer) == elem_size) {
986 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
988 /* FIXME: try run-len first */
989 /* Note: we can't skip the object header here, because it's not present */
990 if (last_set <= SMALL_BITMAP_SIZE) {
991 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
994 /* it's am array of complex structs ... */
995 desc = DESC_TYPE_COMPLEX_ARR;
996 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1000 /* Return the bitmap encoded by a descriptor */
1002 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1004 mword d = (mword)descr;
1008 case DESC_TYPE_RUN_LENGTH: {
1009 int first_set = (d >> 16) & 0xff;
1010 int num_set = (d >> 24) & 0xff;
1013 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1015 for (i = first_set; i < first_set + num_set; ++i)
1016 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1018 *numbits = first_set + num_set;
1023 g_assert_not_reached ();
1028 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1030 MonoObject *o = (MonoObject*)(obj);
1031 MonoObject *ref = (MonoObject*)*(ptr);
1032 int offset = (char*)(ptr) - (char*)o;
1034 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1036 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1038 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1039 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1041 /* Thread.cached_culture_info */
1042 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1043 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1044 !strcmp(o->vtable->klass->name_space, "System") &&
1045 !strcmp(o->vtable->klass->name, "Object[]"))
1048 * 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
1049 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1050 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1051 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1052 * 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
1053 * 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
1054 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1055 * 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
1056 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1058 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1059 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1060 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1061 !strcmp (o->vtable->klass->name, "MemoryStream"))
1063 /* append_job() in threadpool.c */
1064 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1065 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1066 !strcmp (o->vtable->klass->name_space, "System") &&
1067 !strcmp (o->vtable->klass->name, "Object[]") &&
1068 mono_thread_pool_is_queue_array ((MonoArray*) o))
1074 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1076 MonoObject *o = (MonoObject*)(obj);
1077 MonoObject *ref = (MonoObject*)*(ptr);
1078 int offset = (char*)(ptr) - (char*)o;
1080 MonoClassField *field;
1083 if (!ref || ref->vtable->domain == domain)
1085 if (is_xdomain_ref_allowed (ptr, obj, domain))
1089 for (class = o->vtable->klass; class; class = class->parent) {
1092 for (i = 0; i < class->field.count; ++i) {
1093 if (class->fields[i].offset == offset) {
1094 field = &class->fields[i];
1102 if (ref->vtable->klass == mono_defaults.string_class)
1103 str = mono_string_to_utf8 ((MonoString*)ref);
1106 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1107 o, o->vtable->klass->name_space, o->vtable->klass->name,
1108 offset, field ? field->name : "",
1109 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1110 mono_gc_scan_for_specific_ref (o, TRUE);
1116 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1119 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1121 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1123 #include "sgen-scan-object.h"
1126 static gboolean scan_object_for_specific_ref_precise = TRUE;
1129 #define HANDLE_PTR(ptr,obj) do { \
1130 if ((MonoObject*)*(ptr) == key) { \
1131 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1132 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1137 scan_object_for_specific_ref (char *start, MonoObject *key)
1141 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1144 if (scan_object_for_specific_ref_precise) {
1145 #include "sgen-scan-object.h"
1147 mword *words = (mword*)start;
1148 size_t size = safe_object_get_size ((MonoObject*)start);
1150 for (i = 0; i < size / sizeof (mword); ++i) {
1151 if (words [i] == (mword)key) {
1152 g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
1153 key, start, safe_name (start), i * sizeof (mword));
1160 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1162 while (start < end) {
1166 if (!*(void**)start) {
1167 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1172 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1178 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1180 callback (obj, size, data);
1187 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1189 scan_object_for_specific_ref (obj, key);
1193 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1197 g_print ("found ref to %p in root record %p\n", key, root);
1200 static MonoObject *check_key = NULL;
1201 static RootRecord *check_root = NULL;
1204 check_root_obj_specific_ref_from_marker (void **obj)
1206 check_root_obj_specific_ref (check_root, check_key, *obj);
1210 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1215 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1216 for (root = roots_hash [root_type][i]; root; root = root->next) {
1217 void **start_root = (void**)root->start_root;
1218 mword desc = root->root_desc;
1222 switch (desc & ROOT_DESC_TYPE_MASK) {
1223 case ROOT_DESC_BITMAP:
1224 desc >>= ROOT_DESC_TYPE_SHIFT;
1227 check_root_obj_specific_ref (root, key, *start_root);
1232 case ROOT_DESC_COMPLEX: {
1233 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1234 int bwords = (*bitmap_data) - 1;
1235 void **start_run = start_root;
1237 while (bwords-- > 0) {
1238 gsize bmap = *bitmap_data++;
1239 void **objptr = start_run;
1242 check_root_obj_specific_ref (root, key, *objptr);
1246 start_run += GC_BITS_PER_WORD;
1250 case ROOT_DESC_USER: {
1251 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1252 marker (start_root, check_root_obj_specific_ref_from_marker);
1255 case ROOT_DESC_RUN_LEN:
1256 g_assert_not_reached ();
1258 g_assert_not_reached ();
1267 mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
1272 scan_object_for_specific_ref_precise = precise;
1274 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1275 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1277 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1279 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1281 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1282 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1284 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1285 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1286 void **ptr = (void**)root->start_root;
1288 while (ptr < (void**)root->end_root) {
1289 check_root_obj_specific_ref (root, *ptr, key);
1297 need_remove_object_for_domain (char *start, MonoDomain *domain)
1299 if (mono_object_domain (start) == domain) {
1300 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1301 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1308 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1310 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1311 if (vt->klass == mono_defaults.internal_thread_class)
1312 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1313 /* The object could be a proxy for an object in the domain
1315 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1316 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1318 /* The server could already have been zeroed out, so
1319 we need to check for that, too. */
1320 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1321 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1323 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1328 static MonoDomain *check_domain = NULL;
1331 check_obj_not_in_domain (void **o)
1333 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1337 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1341 check_domain = domain;
1342 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1343 for (root = roots_hash [root_type][i]; root; root = root->next) {
1344 void **start_root = (void**)root->start_root;
1345 mword desc = root->root_desc;
1347 /* The MonoDomain struct is allowed to hold
1348 references to objects in its own domain. */
1349 if (start_root == (void**)domain)
1352 switch (desc & ROOT_DESC_TYPE_MASK) {
1353 case ROOT_DESC_BITMAP:
1354 desc >>= ROOT_DESC_TYPE_SHIFT;
1356 if ((desc & 1) && *start_root)
1357 check_obj_not_in_domain (*start_root);
1362 case ROOT_DESC_COMPLEX: {
1363 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1364 int bwords = (*bitmap_data) - 1;
1365 void **start_run = start_root;
1367 while (bwords-- > 0) {
1368 gsize bmap = *bitmap_data++;
1369 void **objptr = start_run;
1371 if ((bmap & 1) && *objptr)
1372 check_obj_not_in_domain (*objptr);
1376 start_run += GC_BITS_PER_WORD;
1380 case ROOT_DESC_USER: {
1381 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1382 marker (start_root, check_obj_not_in_domain);
1385 case ROOT_DESC_RUN_LEN:
1386 g_assert_not_reached ();
1388 g_assert_not_reached ();
1392 check_domain = NULL;
1396 check_for_xdomain_refs (void)
1400 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1401 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1403 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1405 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1406 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1410 clear_domain_process_object (char *obj, MonoDomain *domain)
1414 process_object_for_domain_clearing (obj, domain);
1415 remove = need_remove_object_for_domain (obj, domain);
1417 if (remove && ((MonoObject*)obj)->synchronisation) {
1418 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1420 mono_gc_register_disappearing_link (NULL, dislink, FALSE, TRUE);
1427 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1429 if (clear_domain_process_object (obj, domain))
1430 memset (obj, 0, size);
1434 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1436 clear_domain_process_object (obj, domain);
1440 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1442 if (need_remove_object_for_domain (obj, domain))
1443 major_collector.free_non_pinned_object (obj, size);
1447 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1449 if (need_remove_object_for_domain (obj, domain))
1450 major_collector.free_pinned_object (obj, size);
1454 * When appdomains are unloaded we can easily remove objects that have finalizers,
1455 * but all the others could still be present in random places on the heap.
1456 * We need a sweep to get rid of them even though it's going to be costly
1458 * The reason we need to remove them is because we access the vtable and class
1459 * structures to know the object size and the reference bitmap: once the domain is
1460 * unloaded the point to random memory.
1463 mono_gc_clear_domain (MonoDomain * domain)
1465 LOSObject *bigobj, *prev;
1470 process_fin_stage_entries ();
1471 process_dislink_stage_entries ();
1473 mono_sgen_clear_nursery_fragments ();
1475 if (xdomain_checks && domain != mono_get_root_domain ()) {
1476 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1477 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1478 check_for_xdomain_refs ();
1481 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1482 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1484 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1485 to memory returned to the OS.*/
1486 null_ephemerons_for_domain (domain);
1488 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1489 null_links_for_domain (domain, i);
1491 /* We need two passes over major and large objects because
1492 freeing such objects might give their memory back to the OS
1493 (in the case of large objects) or obliterate its vtable
1494 (pinned objects with major-copying or pinned and non-pinned
1495 objects with major-mark&sweep), but we might need to
1496 dereference a pointer from an object to another object if
1497 the first object is a proxy. */
1498 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1499 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1500 clear_domain_process_object (bigobj->data, domain);
1503 for (bigobj = los_object_list; bigobj;) {
1504 if (need_remove_object_for_domain (bigobj->data, domain)) {
1505 LOSObject *to_free = bigobj;
1507 prev->next = bigobj->next;
1509 los_object_list = bigobj->next;
1510 bigobj = bigobj->next;
1511 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1513 mono_sgen_los_free_object (to_free);
1517 bigobj = bigobj->next;
1519 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1520 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1526 global_remset_cache_clear (void)
1528 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1532 * Tries to check if a given remset location was already added to the global remset.
1535 * A 2 entry, LRU cache of recently saw location remsets.
1537 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1539 * Returns TRUE is the element was added..
1542 global_remset_location_was_not_added (gpointer ptr)
1545 gpointer first = global_remset_cache [0], second;
1547 HEAVY_STAT (++stat_global_remsets_discarded);
1551 second = global_remset_cache [1];
1553 if (second == ptr) {
1554 /*Move the second to the front*/
1555 global_remset_cache [0] = second;
1556 global_remset_cache [1] = first;
1558 HEAVY_STAT (++stat_global_remsets_discarded);
1562 global_remset_cache [0] = second;
1563 global_remset_cache [1] = ptr;
1568 * mono_sgen_add_to_global_remset:
1570 * The global remset contains locations which point into newspace after
1571 * a minor collection. This can happen if the objects they point to are pinned.
1573 * LOCKING: If called from a parallel collector, the global remset
1574 * lock must be held. For serial collectors that is not necessary.
1577 mono_sgen_add_to_global_remset (gpointer ptr)
1580 gboolean lock = major_collector.is_parallel;
1582 if (use_cardtable) {
1583 sgen_card_table_mark_address ((mword)ptr);
1587 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1592 if (!global_remset_location_was_not_added (ptr))
1595 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1596 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1598 HEAVY_STAT (++stat_global_remsets_added);
1601 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1602 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1604 if (global_remset->store_next + 3 < global_remset->end_set) {
1605 *(global_remset->store_next++) = (mword)ptr;
1608 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL, TRUE);
1609 rs->next = global_remset;
1611 *(global_remset->store_next++) = (mword)ptr;
1614 int global_rs_size = 0;
1616 for (rs = global_remset; rs; rs = rs->next) {
1617 global_rs_size += rs->store_next - rs->data;
1619 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1624 UNLOCK_GLOBAL_REMSET;
1630 * Scan objects in the gray stack until the stack is empty. This should be called
1631 * frequently after each object is copied, to achieve better locality and cache
1635 drain_gray_stack (GrayQueue *queue, int max_objs)
1639 if (current_collection_generation == GENERATION_NURSERY) {
1641 GRAY_OBJECT_DEQUEUE (queue, obj);
1644 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1645 major_collector.minor_scan_object (obj, queue);
1650 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1654 for (i = 0; i != max_objs; ++i) {
1655 GRAY_OBJECT_DEQUEUE (queue, obj);
1658 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1659 major_collector.major_scan_object (obj, queue);
1661 } while (max_objs < 0);
1667 * Addresses from start to end are already sorted. This function finds
1668 * the object header for each address and pins the object. The
1669 * addresses must be inside the passed section. The (start of the)
1670 * address array is overwritten with the addresses of the actually
1671 * pinned objects. Return the number of pinned objects.
1674 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1679 void *last_obj = NULL;
1680 size_t last_obj_size = 0;
1683 void **definitely_pinned = start;
1685 mono_sgen_nursery_allocator_prepare_for_pinning ();
1687 while (start < end) {
1689 /* the range check should be reduntant */
1690 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1691 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1692 /* multiple pointers to the same object */
1693 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1697 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1698 g_assert (idx < section->num_scan_start);
1699 search_start = (void*)section->scan_starts [idx];
1700 if (!search_start || search_start > addr) {
1703 search_start = section->scan_starts [idx];
1704 if (search_start && search_start <= addr)
1707 if (!search_start || search_start > addr)
1708 search_start = start_nursery;
1710 if (search_start < last_obj)
1711 search_start = (char*)last_obj + last_obj_size;
1712 /* now addr should be in an object a short distance from search_start
1713 * Note that search_start must point to zeroed mem or point to an object.
1717 if (!*(void**)search_start) {
1718 /* Consistency check */
1720 for (frag = nursery_fragments; frag; frag = frag->next) {
1721 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1722 g_assert_not_reached ();
1726 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1729 last_obj = search_start;
1730 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1732 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1733 /* Marks the beginning of a nursery fragment, skip */
1735 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1736 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1737 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));
1738 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1739 pin_object (search_start);
1740 GRAY_OBJECT_ENQUEUE (queue, search_start);
1742 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1743 definitely_pinned [count] = search_start;
1748 /* skip to the next object */
1749 search_start = (void*)((char*)search_start + last_obj_size);
1750 } while (search_start <= addr);
1751 /* we either pinned the correct object or we ignored the addr because
1752 * it points to unused zeroed memory.
1758 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1759 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1760 GCRootReport report;
1762 for (idx = 0; idx < count; ++idx)
1763 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
1764 notify_gc_roots (&report);
1766 stat_pinned_objects += count;
1771 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1773 int num_entries = section->pin_queue_num_entries;
1775 void **start = section->pin_queue_start;
1777 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1778 section->data, section->next_data, queue);
1779 section->pin_queue_num_entries = reduced_to;
1781 section->pin_queue_start = NULL;
1787 mono_sgen_pin_object (void *object, GrayQueue *queue)
1789 if (major_collector.is_parallel) {
1791 /*object arrives pinned*/
1792 pin_stage_ptr (object);
1796 SGEN_PIN_OBJECT (object);
1797 pin_stage_ptr (object);
1800 GRAY_OBJECT_ENQUEUE (queue, object);
1803 /* Sort the addresses in array in increasing order.
1804 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1807 sort_addresses (void **array, int size)
1812 for (i = 1; i < size; ++i) {
1815 int parent = (child - 1) / 2;
1817 if (array [parent] >= array [child])
1820 tmp = array [parent];
1821 array [parent] = array [child];
1822 array [child] = tmp;
1828 for (i = size - 1; i > 0; --i) {
1831 array [i] = array [0];
1837 while (root * 2 + 1 <= end) {
1838 int child = root * 2 + 1;
1840 if (child < end && array [child] < array [child + 1])
1842 if (array [root] >= array [child])
1846 array [root] = array [child];
1847 array [child] = tmp;
1854 static G_GNUC_UNUSED void
1855 print_nursery_gaps (void* start_nursery, void *end_nursery)
1858 gpointer first = start_nursery;
1860 for (i = 0; i < next_pin_slot; ++i) {
1861 next = pin_queue [i];
1862 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1866 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1869 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1871 optimize_pin_queue (int start_slot)
1873 void **start, **cur, **end;
1874 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1875 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1876 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1877 if ((next_pin_slot - start_slot) > 1)
1878 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1879 start = cur = pin_queue + start_slot;
1880 end = pin_queue + next_pin_slot;
1883 while (*start == *cur && cur < end)
1887 next_pin_slot = start - pin_queue;
1888 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1889 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1894 * Scan the memory between start and end and queue values which could be pointers
1895 * to the area between start_nursery and end_nursery for later consideration.
1896 * Typically used for thread stacks.
1899 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1902 while (start < end) {
1903 if (*start >= start_nursery && *start < end_nursery) {
1905 * *start can point to the middle of an object
1906 * note: should we handle pointing at the end of an object?
1907 * pinning in C# code disallows pointing at the end of an object
1908 * but there is some small chance that an optimizing C compiler
1909 * may keep the only reference to an object by pointing
1910 * at the end of it. We ignore this small chance for now.
1911 * Pointers to the end of an object are indistinguishable
1912 * from pointers to the start of the next object in memory
1913 * so if we allow that we'd need to pin two objects...
1914 * We queue the pointer in an array, the
1915 * array will then be sorted and uniqued. This way
1916 * we can coalesce several pinning pointers and it should
1917 * be faster since we'd do a memory scan with increasing
1918 * addresses. Note: we can align the address to the allocation
1919 * alignment, so the unique process is more effective.
1921 mword addr = (mword)*start;
1922 addr &= ~(ALLOC_ALIGN - 1);
1923 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
1924 pin_stage_ptr ((void*)addr);
1926 pin_stats_register_address ((char*)addr, pin_type);
1927 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
1932 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1936 * Debugging function: find in the conservative roots where @obj is being pinned.
1938 static G_GNUC_UNUSED void
1939 find_pinning_reference (char *obj, size_t size)
1943 char *endobj = obj + size;
1944 for (i = 0; i < roots_hash_size [0]; ++i) {
1945 for (root = roots_hash [0][i]; root; root = root->next) {
1946 /* if desc is non-null it has precise info */
1947 if (!root->root_desc) {
1948 char ** start = (char**)root->start_root;
1949 while (start < (char**)root->end_root) {
1950 if (*start >= obj && *start < endobj) {
1951 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in pinned roots %p-%p (at %p in record %p)\n", obj, root->start_root, root->end_root, start, root));
1958 find_pinning_ref_from_thread (obj, size);
1962 * The first thing we do in a collection is to identify pinned objects.
1963 * This function considers all the areas of memory that need to be
1964 * conservatively scanned.
1967 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
1971 DEBUG (2, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d/%d entries)\n", (int)roots_size, num_roots_entries [ROOT_TYPE_NORMAL], num_roots_entries [ROOT_TYPE_PINNED]));
1972 /* objects pinned from the API are inside these roots */
1973 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1974 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1975 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
1976 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
1979 /* now deal with the thread stacks
1980 * in the future we should be able to conservatively scan only:
1981 * *) the cpu registers
1982 * *) the unmanaged stack frames
1983 * *) the _last_ managed stack frame
1984 * *) pointers slots in managed frames
1986 scan_thread_data (start_nursery, end_nursery, FALSE, queue);
1988 evacuate_pin_staging_area ();
1992 CopyOrMarkObjectFunc func;
1994 } UserCopyOrMarkData;
1996 static pthread_key_t user_copy_or_mark_key;
1999 init_user_copy_or_mark_key (void)
2001 pthread_key_create (&user_copy_or_mark_key, NULL);
2005 set_user_copy_or_mark_data (UserCopyOrMarkData *data)
2007 static pthread_once_t init_control = PTHREAD_ONCE_INIT;
2008 pthread_once (&init_control, init_user_copy_or_mark_key);
2010 pthread_setspecific (user_copy_or_mark_key, data);
2014 single_arg_user_copy_or_mark (void **obj)
2016 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
2018 data->func (obj, data->queue);
2022 * The memory area from start_root to end_root contains pointers to objects.
2023 * Their position is precisely described by @desc (this means that the pointer
2024 * can be either NULL or the pointer to the start of an object).
2025 * This functions copies them to to_space updates them.
2027 * This function is not thread-safe!
2030 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2032 switch (desc & ROOT_DESC_TYPE_MASK) {
2033 case ROOT_DESC_BITMAP:
2034 desc >>= ROOT_DESC_TYPE_SHIFT;
2036 if ((desc & 1) && *start_root) {
2037 copy_func (start_root, queue);
2038 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2039 drain_gray_stack (queue, -1);
2045 case ROOT_DESC_COMPLEX: {
2046 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2047 int bwords = (*bitmap_data) - 1;
2048 void **start_run = start_root;
2050 while (bwords-- > 0) {
2051 gsize bmap = *bitmap_data++;
2052 void **objptr = start_run;
2054 if ((bmap & 1) && *objptr) {
2055 copy_func (objptr, queue);
2056 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2057 drain_gray_stack (queue, -1);
2062 start_run += GC_BITS_PER_WORD;
2066 case ROOT_DESC_USER: {
2067 UserCopyOrMarkData data = { copy_func, queue };
2068 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2069 set_user_copy_or_mark_data (&data);
2070 marker (start_root, single_arg_user_copy_or_mark);
2071 set_user_copy_or_mark_data (NULL);
2074 case ROOT_DESC_RUN_LEN:
2075 g_assert_not_reached ();
2077 g_assert_not_reached ();
2082 reset_heap_boundaries (void)
2084 lowest_heap_address = ~(mword)0;
2085 highest_heap_address = 0;
2089 mono_sgen_update_heap_boundaries (mword low, mword high)
2094 old = lowest_heap_address;
2097 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2100 old = highest_heap_address;
2103 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2106 /* size must be a power of 2 */
2108 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2110 /* Allocate twice the memory to be able to put the block on an aligned address */
2111 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2116 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2117 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2120 mono_sgen_free_os_memory (mem, aligned - mem);
2121 if (aligned + size < mem + size + alignment)
2122 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2128 * Allocate and setup the data structures needed to be able to allocate objects
2129 * in the nursery. The nursery is stored in nursery_section.
2132 alloc_nursery (void)
2134 GCMemSection *section;
2139 if (nursery_section)
2141 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2142 /* later we will alloc a larger area for the nursery but only activate
2143 * what we need. The rest will be used as expansion if we have too many pinned
2144 * objects in the existing nursery.
2146 /* FIXME: handle OOM */
2147 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2149 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2150 alloc_size = nursery_size;
2151 #ifdef SGEN_ALIGN_NURSERY
2152 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2154 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2156 nursery_start = data;
2157 nursery_end = nursery_start + nursery_size;
2158 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
2159 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));
2160 section->data = section->next_data = data;
2161 section->size = alloc_size;
2162 section->end_data = nursery_end;
2163 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2164 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2165 section->num_scan_start = scan_starts;
2166 section->block.role = MEMORY_ROLE_GEN0;
2167 section->block.next = NULL;
2169 nursery_section = section;
2171 mono_sgen_nursery_allocator_set_nursery_bounds (nursery_start, nursery_end);
2175 mono_gc_get_nursery (int *shift_bits, size_t *size)
2177 *size = nursery_size;
2178 #ifdef SGEN_ALIGN_NURSERY
2179 *shift_bits = DEFAULT_NURSERY_BITS;
2183 return nursery_start;
2187 mono_gc_precise_stack_mark_enabled (void)
2189 return !conservative_stack_mark;
2193 mono_gc_get_logfile (void)
2195 return mono_sgen_get_logfile ();
2199 report_finalizer_roots_list (FinalizeEntry *list)
2201 GCRootReport report;
2205 for (fin = list; fin; fin = fin->next) {
2208 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2210 notify_gc_roots (&report);
2214 report_finalizer_roots (void)
2216 report_finalizer_roots_list (fin_ready_list);
2217 report_finalizer_roots_list (critical_fin_list);
2220 static GCRootReport *root_report;
2223 single_arg_report_root (void **obj)
2226 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2230 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2232 switch (desc & ROOT_DESC_TYPE_MASK) {
2233 case ROOT_DESC_BITMAP:
2234 desc >>= ROOT_DESC_TYPE_SHIFT;
2236 if ((desc & 1) && *start_root) {
2237 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2243 case ROOT_DESC_COMPLEX: {
2244 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2245 int bwords = (*bitmap_data) - 1;
2246 void **start_run = start_root;
2248 while (bwords-- > 0) {
2249 gsize bmap = *bitmap_data++;
2250 void **objptr = start_run;
2252 if ((bmap & 1) && *objptr) {
2253 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2258 start_run += GC_BITS_PER_WORD;
2262 case ROOT_DESC_USER: {
2263 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2264 root_report = report;
2265 marker (start_root, single_arg_report_root);
2268 case ROOT_DESC_RUN_LEN:
2269 g_assert_not_reached ();
2271 g_assert_not_reached ();
2276 report_registered_roots_by_type (int root_type)
2278 GCRootReport report;
2282 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2283 for (root = roots_hash [root_type][i]; root; root = root->next) {
2284 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2285 precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
2288 notify_gc_roots (&report);
2292 report_registered_roots (void)
2294 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2295 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2299 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2303 for (fin = list; fin; fin = fin->next) {
2306 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2307 copy_func (&fin->object, queue);
2312 generation_name (int generation)
2314 switch (generation) {
2315 case GENERATION_NURSERY: return "nursery";
2316 case GENERATION_OLD: return "old";
2317 default: g_assert_not_reached ();
2321 static MonoObject **finalized_array = NULL;
2322 static int finalized_array_capacity = 0;
2323 static int finalized_array_entries = 0;
2326 bridge_register_finalized_object (MonoObject *object)
2328 if (!finalized_array)
2331 if (finalized_array_entries >= finalized_array_capacity) {
2332 MonoObject **new_array;
2333 g_assert (finalized_array_entries == finalized_array_capacity);
2334 finalized_array_capacity *= 2;
2335 new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2336 memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
2337 mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
2338 finalized_array = new_array;
2340 finalized_array [finalized_array_entries++] = object;
2344 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2349 int ephemeron_rounds = 0;
2351 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2354 * We copied all the reachable objects. Now it's the time to copy
2355 * the objects that were not referenced by the roots, but by the copied objects.
2356 * we built a stack of objects pointed to by gray_start: they are
2357 * additional roots and we may add more items as we go.
2358 * We loop until gray_start == gray_objects which means no more objects have
2359 * been added. Note this is iterative: no recursion is involved.
2360 * We need to walk the LO list as well in search of marked big objects
2361 * (use a flag since this is needed only on major collections). We need to loop
2362 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2363 * To achieve better cache locality and cache usage, we drain the gray stack
2364 * frequently, after each object is copied, and just finish the work here.
2366 drain_gray_stack (queue, -1);
2368 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2371 We must clear weak links that don't track resurrection before processing object ready for
2372 finalization so they can be cleared before that.
2374 null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
2375 if (generation == GENERATION_OLD)
2376 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
2378 if (finalized_array == NULL && mono_sgen_need_bridge_processing ()) {
2379 finalized_array_capacity = 32;
2380 finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2382 finalized_array_entries = 0;
2384 /* walk the finalization queue and move also the objects that need to be
2385 * finalized: use the finalized objects as new roots so the objects they depend
2386 * on are also not reclaimed. As with the roots above, only objects in the nursery
2387 * are marked/copied.
2388 * We need a loop here, since objects ready for finalizers may reference other objects
2389 * that are fin-ready. Speedup with a flag?
2394 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2395 * before processing finalizable objects to avoid finalizing reachable values.
2397 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2398 * while they are been finalized.
2400 int done_with_ephemerons = 0;
2402 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2403 drain_gray_stack (queue, -1);
2405 } while (!done_with_ephemerons);
2407 fin_ready = num_ready_finalizers;
2408 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2409 if (generation == GENERATION_OLD)
2410 finalize_in_range (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
2412 if (fin_ready != num_ready_finalizers) {
2414 if (finalized_array != NULL)
2415 mono_sgen_bridge_processing (finalized_array_entries, finalized_array);
2418 /* drain the new stack that might have been created */
2419 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2420 drain_gray_stack (queue, -1);
2421 } while (fin_ready != num_ready_finalizers);
2423 if (mono_sgen_need_bridge_processing ())
2424 g_assert (num_loops <= 1);
2427 * Clear ephemeron pairs with unreachable keys.
2428 * We pass the copy func so we can figure out if an array was promoted or not.
2430 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2433 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));
2436 * handle disappearing links
2437 * Note we do this after checking the finalization queue because if an object
2438 * survives (at least long enough to be finalized) we don't clear the link.
2439 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2440 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2443 g_assert (gray_object_queue_is_empty (queue));
2445 null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
2446 if (generation == GENERATION_OLD)
2447 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
2448 if (gray_object_queue_is_empty (queue))
2450 drain_gray_stack (queue, -1);
2453 g_assert (gray_object_queue_is_empty (queue));
2457 mono_sgen_check_section_scan_starts (GCMemSection *section)
2460 for (i = 0; i < section->num_scan_start; ++i) {
2461 if (section->scan_starts [i]) {
2462 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2463 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2469 check_scan_starts (void)
2471 if (!do_scan_starts_check)
2473 mono_sgen_check_section_scan_starts (nursery_section);
2474 major_collector.check_scan_starts ();
2477 static int last_num_pinned = 0;
2480 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2484 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2485 for (root = roots_hash [root_type][i]; root; root = root->next) {
2486 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2487 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2493 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2495 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2499 mono_sgen_dump_section (GCMemSection *section, const char *type)
2501 char *start = section->data;
2502 char *end = section->data + section->size;
2503 char *occ_start = NULL;
2505 char *old_start = NULL; /* just for debugging */
2507 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2509 while (start < end) {
2513 if (!*(void**)start) {
2515 mono_sgen_dump_occupied (occ_start, start, section->data);
2518 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2521 g_assert (start < section->next_data);
2526 vt = (GCVTable*)LOAD_VTABLE (start);
2529 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2532 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2533 start - section->data,
2534 vt->klass->name_space, vt->klass->name,
2542 mono_sgen_dump_occupied (occ_start, start, section->data);
2544 fprintf (heap_dump_file, "</section>\n");
2548 dump_object (MonoObject *obj, gboolean dump_location)
2550 static char class_name [1024];
2552 MonoClass *class = mono_object_class (obj);
2556 * Python's XML parser is too stupid to parse angle brackets
2557 * in strings, so we just ignore them;
2560 while (class->name [i] && j < sizeof (class_name) - 1) {
2561 if (!strchr ("<>\"", class->name [i]))
2562 class_name [j++] = class->name [i];
2565 g_assert (j < sizeof (class_name));
2568 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2569 class->name_space, class_name,
2570 safe_object_get_size (obj));
2571 if (dump_location) {
2572 const char *location;
2573 if (ptr_in_nursery (obj))
2574 location = "nursery";
2575 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2579 fprintf (heap_dump_file, " location=\"%s\"", location);
2581 fprintf (heap_dump_file, "/>\n");
2585 dump_heap (const char *type, int num, const char *reason)
2590 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2592 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2593 fprintf (heap_dump_file, ">\n");
2594 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2595 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2596 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2597 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2598 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2600 fprintf (heap_dump_file, "<pinned-objects>\n");
2601 for (list = pinned_objects; list; list = list->next)
2602 dump_object (list->obj, TRUE);
2603 fprintf (heap_dump_file, "</pinned-objects>\n");
2605 mono_sgen_dump_section (nursery_section, "nursery");
2607 major_collector.dump_heap (heap_dump_file);
2609 fprintf (heap_dump_file, "<los>\n");
2610 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2611 dump_object ((MonoObject*)bigobj->data, FALSE);
2612 fprintf (heap_dump_file, "</los>\n");
2614 fprintf (heap_dump_file, "</collection>\n");
2618 mono_sgen_register_moved_object (void *obj, void *destination)
2620 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2622 /* FIXME: handle this for parallel collector */
2623 g_assert (!major_collector.is_parallel);
2625 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2626 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2627 moved_objects_idx = 0;
2629 moved_objects [moved_objects_idx++] = obj;
2630 moved_objects [moved_objects_idx++] = destination;
2636 static gboolean inited = FALSE;
2641 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2642 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2643 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2644 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2645 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2646 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2647 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2648 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2649 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2651 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2652 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2653 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2654 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2655 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2656 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2657 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2658 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2659 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2660 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2661 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2662 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2663 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2665 mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2667 #ifdef HEAVY_STATISTICS
2668 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2669 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2670 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2671 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2672 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2673 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2674 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2675 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2677 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2678 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2679 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2680 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2681 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2683 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2684 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2685 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2686 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2688 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2689 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2691 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2692 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2693 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2695 mono_sgen_nursery_allocator_init_heavy_stats ();
2697 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2698 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2699 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2700 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2701 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2702 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2703 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2704 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2705 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2711 static gboolean need_calculate_minor_collection_allowance;
2713 static int last_collection_old_num_major_sections;
2714 static mword last_collection_los_memory_usage = 0;
2715 static mword last_collection_old_los_memory_usage;
2716 static mword last_collection_los_memory_alloced;
2719 reset_minor_collection_allowance (void)
2721 need_calculate_minor_collection_allowance = TRUE;
2725 try_calculate_minor_collection_allowance (gboolean overwrite)
2727 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2728 mword los_memory_saved;
2731 g_assert (need_calculate_minor_collection_allowance);
2733 if (!need_calculate_minor_collection_allowance)
2736 if (!*major_collector.have_swept) {
2738 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2742 num_major_sections = major_collector.get_num_major_sections ();
2744 num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
2745 los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
2747 save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2750 * We aim to allow the allocation of as many sections as is
2751 * necessary to reclaim save_target sections in the next
2752 * collection. We assume the collection pattern won't change.
2753 * In the last cycle, we had num_major_sections_saved for
2754 * minor_collection_sections_alloced. Assuming things won't
2755 * change, this must be the same ratio as save_target for
2756 * allowance_target, i.e.
2758 * num_major_sections_saved save_target
2759 * --------------------------------- == ----------------
2760 * minor_collection_sections_alloced allowance_target
2764 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));
2766 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2768 if (major_collector.have_computed_minor_collection_allowance)
2769 major_collector.have_computed_minor_collection_allowance ();
2771 need_calculate_minor_collection_allowance = FALSE;
2775 need_major_collection (mword space_needed)
2777 mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
2778 return (space_needed > available_free_space ()) ||
2779 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
2783 mono_sgen_need_major_collection (mword space_needed)
2785 return need_major_collection (space_needed);
2789 job_gray_queue (WorkerData *worker_data)
2791 return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
2798 } ScanFromRemsetsJobData;
2801 job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
2803 ScanFromRemsetsJobData *job_data = job_data_untyped;
2805 scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
2810 CopyOrMarkObjectFunc func;
2814 } ScanFromRegisteredRootsJobData;
2817 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
2819 ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
2821 scan_from_registered_roots (job_data->func,
2822 job_data->heap_start, job_data->heap_end,
2823 job_data->root_type,
2824 job_gray_queue (worker_data));
2831 } ScanThreadDataJobData;
2834 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
2836 ScanThreadDataJobData *job_data = job_data_untyped;
2838 scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
2839 job_gray_queue (worker_data));
2843 * Collect objects in the nursery. Returns whether to trigger a major
2847 collect_nursery (size_t requested_size)
2849 gboolean needs_major;
2850 size_t max_garbage_amount;
2852 ScanFromRemsetsJobData sfrjd;
2853 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
2854 ScanThreadDataJobData stdjd;
2855 mword fragment_total;
2856 TV_DECLARE (all_atv);
2857 TV_DECLARE (all_btv);
2861 if (disable_minor_collections)
2864 mono_perfcounters->gc_collections0++;
2866 current_collection_generation = GENERATION_NURSERY;
2868 binary_protocol_collection (GENERATION_NURSERY);
2869 check_scan_starts ();
2873 nursery_next = mono_sgen_nursery_alloc_get_upper_alloc_bound ();
2874 /* FIXME: optimize later to use the higher address where an object can be present */
2875 nursery_next = MAX (nursery_next, nursery_end);
2877 nursery_alloc_bound = nursery_next;
2879 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)));
2880 max_garbage_amount = nursery_next - nursery_start;
2881 g_assert (nursery_section->size >= max_garbage_amount);
2883 /* world must be stopped already */
2884 TV_GETTIME (all_atv);
2887 /* Pinning no longer depends on clearing all nursery fragments */
2888 mono_sgen_clear_current_nursery_fragment ();
2891 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2894 check_for_xdomain_refs ();
2896 nursery_section->next_data = nursery_next;
2898 major_collector.start_nursery_collection ();
2900 try_calculate_minor_collection_allowance (FALSE);
2902 gray_object_queue_init (&gray_queue);
2903 workers_init_distribute_gray_queue ();
2906 mono_stats.minor_gc_count ++;
2908 global_remset_cache_clear ();
2910 process_fin_stage_entries ();
2911 process_dislink_stage_entries ();
2913 /* pin from pinned handles */
2915 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2916 pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2917 /* identify pinned objects */
2918 optimize_pin_queue (0);
2919 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2920 nursery_section->pin_queue_start = pin_queue;
2921 nursery_section->pin_queue_num_entries = next_pin_slot;
2923 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
2924 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
2925 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2927 if (consistency_check_at_minor_collection)
2928 check_consistency ();
2930 workers_start_all_workers ();
2933 * Walk all the roots and copy the young objects to the old
2934 * generation, starting from to_space.
2936 * The global remsets must be processed before the workers start
2937 * marking because they might add global remsets.
2939 scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2941 workers_start_marking ();
2943 sfrjd.heap_start = nursery_start;
2944 sfrjd.heap_end = nursery_next;
2945 workers_enqueue_job (job_scan_from_remsets, &sfrjd);
2947 /* we don't have complete write barrier yet, so we scan all the old generation sections */
2949 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
2950 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2952 if (use_cardtable) {
2954 card_tables_collect_stats (TRUE);
2955 scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2957 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
2960 if (!major_collector.is_parallel)
2961 drain_gray_stack (&gray_queue, -1);
2963 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2964 report_registered_roots ();
2965 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2966 report_finalizer_roots ();
2968 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
2970 /* registered roots, this includes static fields */
2971 scrrjd_normal.func = major_collector.copy_object;
2972 scrrjd_normal.heap_start = nursery_start;
2973 scrrjd_normal.heap_end = nursery_next;
2974 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
2975 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
2977 scrrjd_wbarrier.func = major_collector.copy_object;
2978 scrrjd_wbarrier.heap_start = nursery_start;
2979 scrrjd_wbarrier.heap_end = nursery_next;
2980 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
2981 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
2984 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2987 stdjd.heap_start = nursery_start;
2988 stdjd.heap_end = nursery_next;
2989 workers_enqueue_job (job_scan_thread_data, &stdjd);
2992 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2995 if (major_collector.is_parallel) {
2996 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
2997 workers_distribute_gray_queue_sections ();
3003 if (major_collector.is_parallel)
3004 g_assert (gray_object_queue_is_empty (&gray_queue));
3006 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3008 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3009 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3012 * The (single-threaded) finalization code might have done
3013 * some copying/marking so we can only reset the GC thread's
3014 * worker data here instead of earlier when we joined the
3017 if (major_collector.reset_worker_data)
3018 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3020 if (objects_pinned) {
3021 evacuate_pin_staging_area ();
3022 optimize_pin_queue (0);
3023 nursery_section->pin_queue_start = pin_queue;
3024 nursery_section->pin_queue_num_entries = next_pin_slot;
3027 /* walk the pin_queue, build up the fragment list of free memory, unmark
3028 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3031 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3032 fragment_total = mono_sgen_build_nursery_fragments (nursery_section, pin_queue, next_pin_slot);
3033 if (!fragment_total)
3036 /* Clear TLABs for all threads */
3039 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3041 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3042 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3044 if (consistency_check_at_minor_collection)
3045 check_major_refs ();
3047 major_collector.finish_nursery_collection ();
3049 TV_GETTIME (all_btv);
3050 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3053 dump_heap ("minor", num_minor_gcs - 1, NULL);
3055 /* prepare the pin queue for the next collection */
3056 last_num_pinned = next_pin_slot;
3058 if (fin_ready_list || critical_fin_list) {
3059 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3060 mono_gc_finalize_notify ();
3064 g_assert (gray_object_queue_is_empty (&gray_queue));
3067 card_tables_collect_stats (FALSE);
3069 check_scan_starts ();
3071 binary_protocol_flush_buffers (FALSE);
3073 /*objects are late pinned because of lack of memory, so a major is a good call*/
3074 needs_major = need_major_collection (0) || objects_pinned;
3075 current_collection_generation = -1;
3083 FinalizeEntry *list;
3084 } ScanFinalizerEntriesJobData;
3087 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
3089 ScanFinalizerEntriesJobData *job_data = job_data_untyped;
3091 scan_finalizer_entries (major_collector.copy_or_mark_object,
3093 job_gray_queue (worker_data));
3097 major_do_collection (const char *reason)
3099 LOSObject *bigobj, *prevbo;
3100 TV_DECLARE (all_atv);
3101 TV_DECLARE (all_btv);
3104 /* FIXME: only use these values for the precise scan
3105 * note that to_space pointers should be excluded anyway...
3107 char *heap_start = NULL;
3108 char *heap_end = (char*)-1;
3109 int old_next_pin_slot;
3110 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3111 ScanThreadDataJobData stdjd;
3112 ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
3114 mono_perfcounters->gc_collections1++;
3116 last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3119 * A domain could have been freed, resulting in
3120 * los_memory_usage being less than last_collection_los_memory_usage.
3122 last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3123 last_collection_old_los_memory_usage = los_memory_usage;
3126 //count_ref_nonref_objs ();
3127 //consistency_check ();
3129 binary_protocol_collection (GENERATION_OLD);
3130 check_scan_starts ();
3131 gray_object_queue_init (&gray_queue);
3132 workers_init_distribute_gray_queue ();
3135 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3137 mono_stats.major_gc_count ++;
3139 /* world must be stopped already */
3140 TV_GETTIME (all_atv);
3143 /* Pinning depends on this */
3144 mono_sgen_clear_nursery_fragments ();
3147 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3149 nursery_section->next_data = nursery_end;
3150 /* we should also coalesce scanning from sections close to each other
3151 * and deal with pointers outside of the sections later.
3154 if (major_collector.start_major_collection)
3155 major_collector.start_major_collection ();
3157 *major_collector.have_swept = FALSE;
3158 reset_minor_collection_allowance ();
3161 check_for_xdomain_refs ();
3163 /* The remsets are not useful for a major collection */
3165 global_remset_cache_clear ();
3167 card_table_clear ();
3169 process_fin_stage_entries ();
3170 process_dislink_stage_entries ();
3174 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3175 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3176 optimize_pin_queue (0);
3179 * pin_queue now contains all candidate pointers, sorted and
3180 * uniqued. We must do two passes now to figure out which
3181 * objects are pinned.
3183 * The first is to find within the pin_queue the area for each
3184 * section. This requires that the pin_queue be sorted. We
3185 * also process the LOS objects and pinned chunks here.
3187 * The second, destructive, pass is to reduce the section
3188 * areas to pointers to the actually pinned objects.
3190 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3191 /* first pass for the sections */
3192 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3193 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3194 /* identify possible pointers to the insize of large objects */
3195 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3196 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3198 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3199 pin_object (bigobj->data);
3200 /* FIXME: only enqueue if object has references */
3201 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3203 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3204 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));
3207 /* second pass for the sections */
3208 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3209 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3210 old_next_pin_slot = next_pin_slot;
3213 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3214 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3215 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3217 major_collector.init_to_space ();
3219 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3220 main_gc_thread = pthread_self ();
3223 workers_start_all_workers ();
3224 workers_start_marking ();
3226 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3227 report_registered_roots ();
3229 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3231 /* registered roots, this includes static fields */
3232 scrrjd_normal.func = major_collector.copy_or_mark_object;
3233 scrrjd_normal.heap_start = heap_start;
3234 scrrjd_normal.heap_end = heap_end;
3235 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3236 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
3238 scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
3239 scrrjd_wbarrier.heap_start = heap_start;
3240 scrrjd_wbarrier.heap_end = heap_end;
3241 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3242 workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
3245 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3248 stdjd.heap_start = heap_start;
3249 stdjd.heap_end = heap_end;
3250 workers_enqueue_job (job_scan_thread_data, &stdjd);
3253 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3256 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3258 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3259 report_finalizer_roots ();
3261 /* scan the list of objects ready for finalization */
3262 sfejd_fin_ready.list = fin_ready_list;
3263 workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
3265 sfejd_critical_fin.list = critical_fin_list;
3266 workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
3269 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3270 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3273 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3275 if (major_collector.is_parallel) {
3276 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3277 workers_distribute_gray_queue_sections ();
3283 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3284 main_gc_thread = NULL;
3287 if (major_collector.is_parallel)
3288 g_assert (gray_object_queue_is_empty (&gray_queue));
3290 /* all the objects in the heap */
3291 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3293 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3296 * The (single-threaded) finalization code might have done
3297 * some copying/marking so we can only reset the GC thread's
3298 * worker data here instead of earlier when we joined the
3301 if (major_collector.reset_worker_data)
3302 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3304 if (objects_pinned) {
3305 /*This is slow, but we just OOM'd*/
3306 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3307 evacuate_pin_staging_area ();
3308 optimize_pin_queue (0);
3309 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3313 reset_heap_boundaries ();
3314 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
3316 /* sweep the big objects list */
3318 for (bigobj = los_object_list; bigobj;) {
3319 if (object_is_pinned (bigobj->data)) {
3320 unpin_object (bigobj->data);
3321 mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3324 /* not referenced anywhere, so we can free it */
3326 prevbo->next = bigobj->next;
3328 los_object_list = bigobj->next;
3330 bigobj = bigobj->next;
3331 mono_sgen_los_free_object (to_free);
3335 bigobj = bigobj->next;
3339 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3341 mono_sgen_los_sweep ();
3344 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3346 major_collector.sweep ();
3349 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3351 /* walk the pin_queue, build up the fragment list of free memory, unmark
3352 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3355 if (!mono_sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries))
3358 /* Clear TLABs for all threads */
3362 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3364 TV_GETTIME (all_btv);
3365 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3368 dump_heap ("major", num_major_gcs - 1, reason);
3370 /* prepare the pin queue for the next collection */
3372 if (fin_ready_list || critical_fin_list) {
3373 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3374 mono_gc_finalize_notify ();
3378 g_assert (gray_object_queue_is_empty (&gray_queue));
3380 try_calculate_minor_collection_allowance (TRUE);
3382 minor_collection_sections_alloced = 0;
3383 last_collection_los_memory_usage = los_memory_usage;
3385 major_collector.finish_major_collection ();
3387 check_scan_starts ();
3389 binary_protocol_flush_buffers (FALSE);
3391 //consistency_check ();
3395 major_collection (const char *reason)
3397 if (disable_major_collections) {
3398 collect_nursery (0);
3402 current_collection_generation = GENERATION_OLD;
3403 major_do_collection (reason);
3404 current_collection_generation = -1;
3408 sgen_collect_major_no_lock (const char *reason)
3410 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3412 major_collection (reason);
3414 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3418 * When deciding if it's better to collect or to expand, keep track
3419 * of how much garbage was reclaimed with the last collection: if it's too
3421 * This is called when we could not allocate a small object.
3423 static void __attribute__((noinline))
3424 minor_collect_or_expand_inner (size_t size)
3426 int do_minor_collection = 1;
3428 g_assert (nursery_section);
3429 if (do_minor_collection) {
3430 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3432 if (collect_nursery (size)) {
3433 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3434 major_collection ("minor overflow");
3435 /* keep events symmetric */
3436 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3438 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3440 /* this also sets the proper pointers for the next allocation */
3441 if (!mono_sgen_can_alloc_size (size)) {
3443 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3444 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3445 for (i = 0; i < last_num_pinned; ++i) {
3446 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])));
3450 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3452 //report_internal_mem_usage ();
3456 * ######################################################################
3457 * ######## Memory allocation from the OS
3458 * ######################################################################
3459 * This section of code deals with getting memory from the OS and
3460 * allocating memory for GC-internal data structures.
3461 * Internal memory can be handled with a freelist for small objects.
3467 G_GNUC_UNUSED static void
3468 report_internal_mem_usage (void)
3470 printf ("Internal memory usage:\n");
3471 mono_sgen_report_internal_mem_usage ();
3472 printf ("Pinned memory usage:\n");
3473 major_collector.report_pinned_memory_usage ();
3477 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3478 * This must not require any lock.
3481 mono_sgen_alloc_os_memory (size_t size, int activate)
3484 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3486 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3487 size += pagesize - 1;
3488 size &= ~(pagesize - 1);
3489 ptr = mono_valloc (0, size, prot_flags);
3491 total_alloc += size;
3496 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3499 mono_sgen_free_os_memory (void *addr, size_t size)
3501 mono_vfree (addr, size);
3503 size += pagesize - 1;
3504 size &= ~(pagesize - 1);
3506 total_alloc -= size;
3510 * ######################################################################
3511 * ######## Object allocation
3512 * ######################################################################
3513 * This section of code deals with allocating memory for objects.
3514 * There are several ways:
3515 * *) allocate large objects
3516 * *) allocate normal objects
3517 * *) fast lock-free allocation
3518 * *) allocation of pinned objects
3522 alloc_degraded (MonoVTable *vtable, size_t size)
3524 if (need_major_collection (0)) {
3525 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3527 major_collection ("degraded overflow");
3529 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3532 return major_collector.alloc_degraded (vtable, size);
3536 * Provide a variant that takes just the vtable for small fixed-size objects.
3537 * The aligned size is already computed and stored in vt->gc_descr.
3538 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3539 * processing. We can keep track of where objects start, for example,
3540 * so when we scan the thread stacks for pinned objects, we can start
3541 * a search for the pinned object in SCAN_START_SIZE chunks.
3544 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3546 /* FIXME: handle OOM */
3551 HEAVY_STAT (++stat_objects_alloced);
3552 if (size <= MAX_SMALL_OBJ_SIZE)
3553 HEAVY_STAT (stat_bytes_alloced += size);
3555 HEAVY_STAT (stat_bytes_alloced_los += size);
3557 size = ALIGN_UP (size);
3559 g_assert (vtable->gc_descr);
3561 if (G_UNLIKELY (collect_before_allocs)) {
3562 static int alloc_count;
3564 InterlockedIncrement (&alloc_count);
3565 if (((alloc_count % collect_before_allocs) == 0) && nursery_section) {
3566 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3568 collect_nursery (0);
3570 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3571 if (!degraded_mode && !mono_sgen_can_alloc_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
3573 g_assert_not_reached ();
3579 * We must already have the lock here instead of after the
3580 * fast path because we might be interrupted in the fast path
3581 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3582 * and we'll end up allocating an object in a fragment which
3583 * no longer belongs to us.
3585 * The managed allocator does not do this, but it's treated
3586 * specially by the world-stopping code.
3589 if (size > MAX_SMALL_OBJ_SIZE) {
3590 p = mono_sgen_los_alloc_large_inner (vtable, size);
3592 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3594 p = (void**)TLAB_NEXT;
3595 /* FIXME: handle overflow */
3596 new_next = (char*)p + size;
3597 TLAB_NEXT = new_next;
3599 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3603 * FIXME: We might need a memory barrier here so the change to tlab_next is
3604 * visible before the vtable store.
3607 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3608 binary_protocol_alloc (p , vtable, size);
3609 g_assert (*p == NULL);
3612 g_assert (TLAB_NEXT == new_next);
3619 /* there are two cases: the object is too big or we run out of space in the TLAB */
3620 /* we also reach here when the thread does its first allocation after a minor
3621 * collection, since the tlab_ variables are initialized to NULL.
3622 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3623 * objects that need finalizers can have the high bit set in their size
3624 * so the above check fails and we can readily add the object to the queue.
3625 * This avoids taking again the GC lock when registering, but this is moot when
3626 * doing thread-local allocation, so it may not be a good idea.
3628 g_assert (TLAB_NEXT == new_next);
3629 if (TLAB_NEXT >= TLAB_REAL_END) {
3631 * Run out of space in the TLAB. When this happens, some amount of space
3632 * remains in the TLAB, but not enough to satisfy the current allocation
3633 * request. Currently, we retire the TLAB in all cases, later we could
3634 * keep it if the remaining space is above a treshold, and satisfy the
3635 * allocation directly from the nursery.
3638 /* when running in degraded mode, we continue allocing that way
3639 * for a while, to decrease the number of useless nursery collections.
3641 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3642 p = alloc_degraded (vtable, size);
3643 binary_protocol_alloc_degraded (p, vtable, size);
3647 if (size > tlab_size) {
3648 /* Allocate directly from the nursery */
3650 p = mono_sgen_nursery_alloc (size);
3652 minor_collect_or_expand_inner (size);
3653 if (degraded_mode) {
3654 p = alloc_degraded (vtable, size);
3655 binary_protocol_alloc_degraded (p, vtable, size);
3658 p = mono_sgen_nursery_alloc (size);
3667 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3668 memset (p, 0, size);
3673 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3676 p = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3678 minor_collect_or_expand_inner (tlab_size);
3679 if (degraded_mode) {
3680 p = alloc_degraded (vtable, size);
3681 binary_protocol_alloc_degraded (p, vtable, size);
3684 p = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3694 /* Allocate a new TLAB from the current nursery fragment */
3695 TLAB_START = (char*)p;
3696 TLAB_NEXT = TLAB_START;
3697 TLAB_REAL_END = TLAB_START + alloc_size;
3698 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3700 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3701 memset (TLAB_START, 0, alloc_size);
3704 /* Allocate from the TLAB */
3705 p = (void*)TLAB_NEXT;
3707 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3709 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3712 /* Reached tlab_temp_end */
3714 /* record the scan start so we can find pinned objects more easily */
3715 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3716 /* we just bump tlab_temp_end as well */
3717 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3718 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3723 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3724 binary_protocol_alloc (p, vtable, size);
3732 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3738 size = ALIGN_UP (size);
3740 g_assert (vtable->gc_descr);
3741 if (size > MAX_SMALL_OBJ_SIZE)
3744 if (G_UNLIKELY (size > tlab_size)) {
3745 /* Allocate directly from the nursery */
3746 p = mono_sgen_nursery_alloc (size);
3750 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3751 memset (p, 0, size);
3752 g_assert (*p == NULL);
3754 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3756 p = (void**)TLAB_NEXT;
3757 /* FIXME: handle overflow */
3758 new_next = (char*)p + size;
3759 TLAB_NEXT = new_next;
3761 /*First case, we overflowed the tlab, get a new one*/
3762 if (G_UNLIKELY (new_next >= TLAB_REAL_END)) {
3765 new_next = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3766 p = (void**)new_next;
3770 g_assert (alloc_size >= size);
3771 TLAB_START = (char*)new_next;
3772 TLAB_NEXT = new_next + size;
3773 TLAB_REAL_END = new_next + alloc_size;
3774 TLAB_TEMP_END = new_next + MIN (SCAN_START_SIZE, alloc_size);
3776 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3777 memset (new_next, 0, alloc_size);
3779 g_assert (*p == NULL);
3783 /* Second case, we overflowed temp end */
3784 if (G_UNLIKELY (new_next >= TLAB_TEMP_END)) {
3785 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3786 /* we just bump tlab_temp_end as well */
3787 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3788 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3791 g_assert (TLAB_NEXT == new_next);
3795 * FIXME: We might need a memory barrier here so the change to tlab_next is
3796 * visible before the vtable store.
3799 HEAVY_STAT (++stat_objects_alloced);
3800 HEAVY_STAT (stat_bytes_alloced += size);
3802 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3803 binary_protocol_alloc (p, vtable, size);
3804 g_assert (*p == NULL);
3811 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
3814 #ifndef DISABLE_CRITICAL_REGION
3816 ENTER_CRITICAL_REGION;
3817 res = mono_gc_try_alloc_obj_nolock (vtable, size);
3819 EXIT_CRITICAL_REGION;
3822 EXIT_CRITICAL_REGION;
3825 res = mono_gc_alloc_obj_nolock (vtable, size);
3827 if (G_UNLIKELY (!res))
3828 return mono_gc_out_of_memory (size);
3833 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
3836 #ifndef DISABLE_CRITICAL_REGION
3838 ENTER_CRITICAL_REGION;
3839 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
3841 arr->max_length = max_length;
3842 EXIT_CRITICAL_REGION;
3845 EXIT_CRITICAL_REGION;
3850 arr = mono_gc_alloc_obj_nolock (vtable, size);
3851 if (G_UNLIKELY (!arr)) {
3853 return mono_gc_out_of_memory (size);
3856 arr->max_length = max_length;
3864 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
3867 MonoArrayBounds *bounds;
3871 arr = mono_gc_alloc_obj_nolock (vtable, size);
3872 if (G_UNLIKELY (!arr)) {
3874 return mono_gc_out_of_memory (size);
3877 arr->max_length = max_length;
3879 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
3880 arr->bounds = bounds;
3888 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
3891 #ifndef DISABLE_CRITICAL_REGION
3893 ENTER_CRITICAL_REGION;
3894 str = mono_gc_try_alloc_obj_nolock (vtable, size);
3897 EXIT_CRITICAL_REGION;
3900 EXIT_CRITICAL_REGION;
3905 str = mono_gc_alloc_obj_nolock (vtable, size);
3906 if (G_UNLIKELY (!str)) {
3908 return mono_gc_out_of_memory (size);
3919 * To be used for interned strings and possibly MonoThread, reflection handles.
3920 * We may want to explicitly free these objects.
3923 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
3926 size = ALIGN_UP (size);
3929 if (size > MAX_SMALL_OBJ_SIZE) {
3930 /* large objects are always pinned anyway */
3931 p = mono_sgen_los_alloc_large_inner (vtable, size);
3933 DEBUG (9, g_assert (vtable->klass->inited));
3934 p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
3937 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3938 binary_protocol_alloc_pinned (p, vtable, size);
3946 mono_gc_alloc_mature (MonoVTable *vtable)
3949 size_t size = ALIGN_UP (vtable->klass->instance_size);
3951 res = alloc_degraded (vtable, size);
3954 if (G_UNLIKELY (vtable->klass->has_finalize))
3955 mono_object_register_finalizer ((MonoObject*)res);
3961 * ######################################################################
3962 * ######## Finalization support
3963 * ######################################################################
3967 * this is valid for the nursery: if the object has been forwarded it means it's
3968 * still refrenced from a root. If it is pinned it's still alive as well.
3969 * Return TRUE if @obj is ready to be finalized.
3971 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3974 is_critical_finalizer (FinalizeEntry *entry)
3979 if (!mono_defaults.critical_finalizer_object)
3982 obj = entry->object;
3983 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3985 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
3989 queue_finalization_entry (FinalizeEntry *entry) {
3990 if (is_critical_finalizer (entry)) {
3991 entry->next = critical_fin_list;
3992 critical_fin_list = entry;
3994 entry->next = fin_ready_list;
3995 fin_ready_list = entry;
4000 object_is_reachable (char *object, char *start, char *end)
4002 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4003 if (object < start || object >= end)
4005 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4008 #include "sgen-fin-weak-hash.c"
4011 mono_sgen_object_is_live (void *obj)
4013 if (ptr_in_nursery (obj))
4014 return object_is_pinned (obj);
4015 if (current_collection_generation == GENERATION_NURSERY)
4017 return major_collector.is_object_live (obj);
4020 /* LOCKING: requires that the GC lock is held */
4022 null_ephemerons_for_domain (MonoDomain *domain)
4024 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4027 MonoObject *object = (MonoObject*)current->array;
4029 if (object && !object->vtable) {
4030 EphemeronLinkNode *tmp = current;
4033 prev->next = current->next;
4035 ephemeron_list = current->next;
4037 current = current->next;
4038 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4041 current = current->next;
4046 /* LOCKING: requires that the GC lock is held */
4048 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4050 int was_in_nursery, was_promoted;
4051 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4053 Ephemeron *cur, *array_end;
4057 char *object = current->array;
4059 if (!object_is_reachable (object, start, end)) {
4060 EphemeronLinkNode *tmp = current;
4062 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4065 prev->next = current->next;
4067 ephemeron_list = current->next;
4069 current = current->next;
4070 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4075 was_in_nursery = ptr_in_nursery (object);
4076 copy_func ((void**)&object, queue);
4077 current->array = object;
4079 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4080 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4082 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4084 array = (MonoArray*)object;
4085 cur = mono_array_addr (array, Ephemeron, 0);
4086 array_end = cur + mono_array_length_fast (array);
4087 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4089 for (; cur < array_end; ++cur) {
4090 char *key = (char*)cur->key;
4092 if (!key || key == tombstone)
4095 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4096 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4097 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4099 if (!object_is_reachable (key, start, end)) {
4100 cur->key = tombstone;
4106 if (ptr_in_nursery (key)) {/*key was not promoted*/
4107 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4108 mono_sgen_add_to_global_remset (&cur->key);
4110 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4111 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4112 mono_sgen_add_to_global_remset (&cur->value);
4117 current = current->next;
4121 /* LOCKING: requires that the GC lock is held */
4123 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4125 int nothing_marked = 1;
4126 EphemeronLinkNode *current = ephemeron_list;
4128 Ephemeron *cur, *array_end;
4131 for (current = ephemeron_list; current; current = current->next) {
4132 char *object = current->array;
4133 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4135 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4136 if (object < start || object >= end)
4139 /*It has to be alive*/
4140 if (!object_is_reachable (object, start, end)) {
4141 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4145 copy_func ((void**)&object, queue);
4147 array = (MonoArray*)object;
4148 cur = mono_array_addr (array, Ephemeron, 0);
4149 array_end = cur + mono_array_length_fast (array);
4150 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4152 for (; cur < array_end; ++cur) {
4153 char *key = cur->key;
4155 if (!key || key == tombstone)
4158 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4159 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4160 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4162 if (object_is_reachable (key, start, end)) {
4163 char *value = cur->value;
4165 copy_func ((void**)&cur->key, queue);
4167 if (!object_is_reachable (value, start, end))
4169 copy_func ((void**)&cur->value, queue);
4175 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4176 return nothing_marked;
4180 mono_gc_invoke_finalizers (void)
4182 FinalizeEntry *entry = NULL;
4183 gboolean entry_is_critical = FALSE;
4186 /* FIXME: batch to reduce lock contention */
4187 while (fin_ready_list || critical_fin_list) {
4191 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4193 /* We have finalized entry in the last
4194 interation, now we need to remove it from
4197 *list = entry->next;
4199 FinalizeEntry *e = *list;
4200 while (e->next != entry)
4202 e->next = entry->next;
4204 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4208 /* Now look for the first non-null entry. */
4209 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4212 entry_is_critical = FALSE;
4214 entry_is_critical = TRUE;
4215 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4220 g_assert (entry->object);
4221 num_ready_finalizers--;
4222 obj = entry->object;
4223 entry->object = NULL;
4224 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4232 g_assert (entry->object == NULL);
4234 /* the object is on the stack so it is pinned */
4235 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4236 mono_gc_run_finalize (obj, NULL);
4243 mono_gc_pending_finalizers (void)
4245 return fin_ready_list || critical_fin_list;
4248 /* Negative value to remove */
4250 mono_gc_add_memory_pressure (gint64 value)
4252 /* FIXME: Use interlocked functions */
4254 memory_pressure += value;
4259 mono_sgen_register_major_sections_alloced (int num_sections)
4261 minor_collection_sections_alloced += num_sections;
4265 mono_sgen_get_minor_collection_allowance (void)
4267 return minor_collection_allowance;
4271 * ######################################################################
4272 * ######## registered roots support
4273 * ######################################################################
4277 rehash_roots (gboolean pinned)
4281 RootRecord **new_hash;
4282 RootRecord *entry, *next;
4285 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4286 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4287 for (i = 0; i < roots_hash_size [pinned]; ++i) {
4288 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4289 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4291 entry->next = new_hash [hash];
4292 new_hash [hash] = entry;
4295 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4296 roots_hash [pinned] = new_hash;
4297 roots_hash_size [pinned] = new_size;
4301 find_root (int root_type, char *start, guint32 addr_hash)
4303 RootRecord *new_root;
4305 guint32 hash = addr_hash % roots_hash_size [root_type];
4306 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4307 /* we allow changing the size and the descriptor (for thread statics etc) */
4308 if (new_root->start_root == start) {
4317 * We do not coalesce roots.
4320 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4322 RootRecord *new_root;
4323 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
4326 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4327 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
4330 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4331 new_root = find_root (i, start, addr_hash);
4332 /* we allow changing the size and the descriptor (for thread statics etc) */
4334 size_t old_size = new_root->end_root - new_root->start_root;
4335 new_root->end_root = new_root->start_root + size;
4336 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
4337 ((new_root->root_desc == 0) && (descr == NULL)));
4338 new_root->root_desc = (mword)descr;
4340 roots_size -= old_size;
4345 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
4347 new_root->start_root = start;
4348 new_root->end_root = new_root->start_root + size;
4349 new_root->root_desc = (mword)descr;
4351 hash = addr_hash % roots_hash_size [root_type];
4352 num_roots_entries [root_type]++;
4353 new_root->next = roots_hash [root_type] [hash];
4354 roots_hash [root_type][hash] = new_root;
4355 DEBUG (3, fprintf (gc_debug_file, "Added root %p for range: %p-%p, descr: %p (%d/%d bytes)\n", new_root, new_root->start_root, new_root->end_root, descr, (int)size, (int)roots_size));
4365 mono_gc_register_root (char *start, size_t size, void *descr)
4367 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4371 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4373 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4377 mono_gc_deregister_root (char* addr)
4379 RootRecord *tmp, *prev;
4380 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
4384 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4385 hash = addr_hash % roots_hash_size [root_type];
4386 tmp = roots_hash [root_type][hash];
4389 if (tmp->start_root == (char*)addr) {
4391 prev->next = tmp->next;
4393 roots_hash [root_type][hash] = tmp->next;
4394 roots_size -= (tmp->end_root - tmp->start_root);
4395 num_roots_entries [root_type]--;
4396 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
4397 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
4408 * ######################################################################
4409 * ######## Thread handling (stop/start code)
4410 * ######################################################################
4413 #if USE_SIGNAL_BASED_START_STOP_WORLD
4415 static MonoSemType suspend_ack_semaphore;
4416 static MonoSemType *suspend_ack_semaphore_ptr;
4417 static unsigned int global_stop_count = 0;
4419 static sigset_t suspend_signal_mask;
4422 static MonoContext cur_thread_ctx = {0};
4424 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4428 update_current_thread_stack (void *start)
4430 int stack_guard = 0;
4431 #ifndef USE_MONO_CTX
4432 void *ptr = cur_thread_regs;
4434 SgenThreadInfo *info = mono_thread_info_current ();
4436 info->stack_start = align_pointer (&stack_guard);
4437 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
4439 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
4440 info->monoctx = &cur_thread_ctx;
4442 ARCH_STORE_REGS (ptr);
4443 info->stopped_regs = ptr;
4445 if (gc_callbacks.thread_suspend_func)
4446 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
4450 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
4451 * have cross-domain checks in the write barrier.
4453 //#define XDOMAIN_CHECKS_IN_WBARRIER
4455 #ifndef SGEN_BINARY_PROTOCOL
4456 #ifndef HEAVY_STATISTICS
4457 #define MANAGED_ALLOCATION
4458 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
4459 #define MANAGED_WBARRIER
4465 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
4468 mono_sgen_wait_for_suspend_ack (int count)
4470 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4471 /* mach thread_resume is synchronous so we dont need to wait for them */
4475 for (i = 0; i < count; ++i) {
4476 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
4477 if (errno != EINTR) {
4478 g_error ("sem_wait ()");
4486 restart_threads_until_none_in_managed_allocator (void)
4488 SgenThreadInfo *info;
4489 int num_threads_died = 0;
4490 int sleep_duration = -1;
4493 int restart_count = 0, restarted_count = 0;
4494 /* restart all threads that stopped in the
4496 FOREACH_THREAD_SAFE (info) {
4500 if (!info->thread_is_dying && (!info->stack_start || info->in_critical_region ||
4501 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
4502 binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
4503 result = mono_sgen_resume_thread (info);
4510 /* we set the stopped_ip to
4511 NULL for threads which
4512 we're not restarting so
4513 that we can easily identify
4515 info->stopped_ip = NULL;
4516 info->stopped_domain = NULL;
4518 } END_FOREACH_THREAD_SAFE
4519 /* if no threads were restarted, we're done */
4520 if (restart_count == 0)
4523 /* wait for the threads to signal their restart */
4524 mono_sgen_wait_for_suspend_ack (restart_count);
4526 if (sleep_duration < 0) {
4530 g_usleep (sleep_duration);
4531 sleep_duration += 10;
4534 /* stop them again */
4535 FOREACH_THREAD (info) {
4537 if (info->skip || info->stopped_ip == NULL)
4539 result = mono_sgen_suspend_thread (info);
4546 } END_FOREACH_THREAD
4547 /* some threads might have died */
4548 num_threads_died += restart_count - restarted_count;
4549 /* wait for the threads to signal their suspension
4551 mono_sgen_wait_for_suspend_ack (restart_count);
4554 return num_threads_died;
4558 suspend_thread (SgenThreadInfo *info, void *context)
4562 MonoContext monoctx;
4564 gpointer regs [ARCH_NUM_REGS];
4566 gpointer stack_start;
4568 g_assert (info->doing_handshake);
4570 info->stopped_domain = mono_domain_get ();
4571 info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
4572 stop_count = global_stop_count;
4573 /* duplicate signal */
4574 if (0 && info->stop_count == stop_count)
4576 #ifdef HAVE_KW_THREAD
4577 /* update the remset info in the thread data structure */
4578 info->remset = remembered_set;
4580 stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
4581 /* If stack_start is not within the limits, then don't set it
4582 in info and we will be restarted. */
4583 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
4584 info->stack_start = stack_start;
4588 mono_sigctx_to_monoctx (context, &monoctx);
4589 info->monoctx = &monoctx;
4591 info->monoctx = NULL;
4595 ARCH_COPY_SIGCTX_REGS (regs, context);
4596 info->stopped_regs = regs;
4598 info->stopped_regs = NULL;
4602 g_assert (!info->stack_start);
4605 /* Notify the JIT */
4606 if (gc_callbacks.thread_suspend_func)
4607 gc_callbacks.thread_suspend_func (info->runtime_data, context);
4609 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
4610 /* notify the waiting thread */
4611 MONO_SEM_POST (suspend_ack_semaphore_ptr);
4612 info->stop_count = stop_count;
4614 /* wait until we receive the restart signal */
4617 sigsuspend (&suspend_signal_mask);
4618 } while (info->signal != restart_signal_num && info->doing_handshake);
4620 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
4621 /* notify the waiting thread */
4622 MONO_SEM_POST (suspend_ack_semaphore_ptr);
4625 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
4627 suspend_handler (int sig, siginfo_t *siginfo, void *context)
4629 SgenThreadInfo *info;
4630 int old_errno = errno;
4632 info = mono_thread_info_current ();
4635 suspend_thread (info, context);
4637 /* This can happen while a thread is dying */
4638 g_print ("no thread info in suspend\n");
4645 restart_handler (int sig)
4647 SgenThreadInfo *info;
4648 int old_errno = errno;
4650 info = mono_thread_info_current ();
4653 * If a thread is dying there might be no thread info. In
4654 * that case we rely on info->doing_handshake.
4657 info->signal = restart_signal_num;
4658 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
4665 acquire_gc_locks (void)
4668 mono_thread_info_suspend_lock ();
4672 release_gc_locks (void)
4674 mono_thread_info_suspend_unlock ();
4675 UNLOCK_INTERRUPTION;
4678 static TV_DECLARE (stop_world_time);
4679 static unsigned long max_pause_usec = 0;
4681 /* LOCKING: assumes the GC lock is held */
4683 stop_world (int generation)
4687 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
4688 acquire_gc_locks ();
4690 update_current_thread_stack (&count);
4692 global_stop_count++;
4693 DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ()));
4694 TV_GETTIME (stop_world_time);
4695 count = mono_sgen_thread_handshake (suspend_signal_num);
4696 count -= restart_threads_until_none_in_managed_allocator ();
4697 g_assert (count >= 0);
4698 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
4699 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
4703 /* LOCKING: assumes the GC lock is held */
4705 restart_world (int generation)
4708 SgenThreadInfo *info;
4709 TV_DECLARE (end_sw);
4712 /* notify the profiler of the leftovers */
4713 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
4714 if (moved_objects_idx) {
4715 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
4716 moved_objects_idx = 0;
4719 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
4720 FOREACH_THREAD (info) {
4721 info->stack_start = NULL;
4723 info->monoctx = NULL;
4725 info->stopped_regs = NULL;
4727 } END_FOREACH_THREAD
4729 release_gc_locks ();
4731 count = mono_sgen_thread_handshake (restart_signal_num);
4732 TV_GETTIME (end_sw);
4733 usec = TV_ELAPSED (stop_world_time, end_sw);
4734 max_pause_usec = MAX (usec, max_pause_usec);
4735 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
4736 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
4740 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
4743 mono_sgen_get_current_collection_generation (void)
4745 return current_collection_generation;
4749 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
4751 gc_callbacks = *callbacks;
4755 mono_gc_get_gc_callbacks ()
4757 return &gc_callbacks;
4760 /* Variables holding start/end nursery so it won't have to be passed at every call */
4761 static void *scan_area_arg_start, *scan_area_arg_end;
4764 mono_gc_conservatively_scan_area (void *start, void *end)
4766 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
4770 mono_gc_scan_object (void *obj)
4772 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
4774 if (current_collection_generation == GENERATION_NURSERY)
4775 major_collector.copy_object (&obj, data->queue);
4777 major_collector.copy_or_mark_object (&obj, data->queue);
4782 * Mark from thread stacks and registers.
4785 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
4787 SgenThreadInfo *info;
4789 scan_area_arg_start = start_nursery;
4790 scan_area_arg_end = end_nursery;
4792 FOREACH_THREAD (info) {
4794 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));
4797 DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
4798 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
4799 UserCopyOrMarkData data = { NULL, queue };
4800 set_user_copy_or_mark_data (&data);
4801 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
4802 set_user_copy_or_mark_data (NULL);
4803 } else if (!precise) {
4804 if (!info->thread_is_dying)
4805 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
4809 if (!info->thread_is_dying && !precise)
4810 conservatively_pin_objects_from ((void**)info->monoctx, (void**)info->monoctx + ARCH_NUM_REGS,
4811 start_nursery, end_nursery, PIN_TYPE_STACK);
4813 if (!info->thread_is_dying && !precise)
4814 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
4815 start_nursery, end_nursery, PIN_TYPE_STACK);
4817 } END_FOREACH_THREAD
4821 find_pinning_ref_from_thread (char *obj, size_t size)
4824 SgenThreadInfo *info;
4825 char *endobj = obj + size;
4827 FOREACH_THREAD (info) {
4828 char **start = (char**)info->stack_start;
4831 while (start < (char**)info->stack_end) {
4832 if (*start >= obj && *start < endobj) {
4833 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));
4838 for (j = 0; j < ARCH_NUM_REGS; ++j) {
4840 mword w = ((mword*)info->monoctx) [j];
4842 mword w = (mword)info->stopped_regs [j];
4845 if (w >= (mword)obj && w < (mword)obj + size)
4846 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)));
4847 } END_FOREACH_THREAD
4852 ptr_on_stack (void *ptr)
4854 gpointer stack_start = &stack_start;
4855 SgenThreadInfo *info = mono_thread_info_current ();
4857 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
4863 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
4870 HEAVY_STAT (++stat_global_remsets_processed);
4872 HEAVY_STAT (++stat_local_remsets_processed);
4874 /* FIXME: exclude stack locations */
4875 switch ((*p) & REMSET_TYPE_MASK) {
4876 case REMSET_LOCATION:
4878 //__builtin_prefetch (ptr);
4879 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
4880 gpointer old = *ptr;
4881 major_collector.copy_object (ptr, queue);
4882 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
4884 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
4885 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4887 * If the object is pinned, each reference to it from nonpinned objects
4888 * becomes part of the global remset, which can grow very large.
4890 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4891 mono_sgen_add_to_global_remset (ptr);
4894 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
4898 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4899 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4902 while (count-- > 0) {
4903 major_collector.copy_object (ptr, queue);
4904 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
4905 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
4906 mono_sgen_add_to_global_remset (ptr);
4911 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4912 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4914 major_collector.minor_scan_object ((char*)ptr, queue);
4916 case REMSET_VTYPE: {
4919 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4920 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4925 while (count-- > 0) {
4926 major_collector.minor_scan_vtype ((char*)ptr, desc, queue);
4927 ptr = (void**)((char*)ptr + skip_size);
4932 g_assert_not_reached ();
4937 #ifdef HEAVY_STATISTICS
4939 collect_store_remsets (RememberedSet *remset, mword *bumper)
4941 mword *p = remset->data;
4946 while (p < remset->store_next) {
4947 switch ((*p) & REMSET_TYPE_MASK) {
4948 case REMSET_LOCATION:
4951 ++stat_saved_remsets_1;
4953 if (*p == last1 || *p == last2) {
4954 ++stat_saved_remsets_2;
4971 g_assert_not_reached ();
4981 RememberedSet *remset;
4983 SgenThreadInfo *info;
4985 mword *addresses, *bumper, *p, *r;
4987 FOREACH_THREAD (info) {
4988 for (remset = info->remset; remset; remset = remset->next)
4989 size += remset->store_next - remset->data;
4990 } END_FOREACH_THREAD
4991 for (remset = freed_thread_remsets; remset; remset = remset->next)
4992 size += remset->store_next - remset->data;
4993 for (remset = global_remset; remset; remset = remset->next)
4994 size += remset->store_next - remset->data;
4996 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
4998 FOREACH_THREAD (info) {
4999 for (remset = info->remset; remset; remset = remset->next)
5000 bumper = collect_store_remsets (remset, bumper);
5001 } END_FOREACH_THREAD
5002 for (remset = global_remset; remset; remset = remset->next)
5003 bumper = collect_store_remsets (remset, bumper);
5004 for (remset = freed_thread_remsets; remset; remset = remset->next)
5005 bumper = collect_store_remsets (remset, bumper);
5007 g_assert (bumper <= addresses + size);
5009 stat_store_remsets += bumper - addresses;
5011 sort_addresses ((void**)addresses, bumper - addresses);
5014 while (r < bumper) {
5020 stat_store_remsets_unique += p - addresses;
5022 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5027 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5029 *info->store_remset_buffer_index_addr = 0;
5030 /* See the comment at the end of sgen_thread_unregister() */
5031 if (*info->store_remset_buffer_addr)
5032 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5036 remset_byte_size (RememberedSet *remset)
5038 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5042 scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5044 RememberedSet *remset;
5045 mword *p, *next_p, *store_pos;
5047 /* the global one */
5048 for (remset = global_remset; remset; remset = remset->next) {
5049 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));
5050 store_pos = remset->data;
5051 for (p = remset->data; p < remset->store_next; p = next_p) {
5052 void **ptr = (void**)p [0];
5054 /*Ignore previously processed remset.*/
5055 if (!global_remset_location_was_not_added (ptr)) {
5060 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5063 * Clear global remsets of locations which no longer point to the
5064 * nursery. Otherwise, they could grow indefinitely between major
5067 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5069 if (ptr_in_nursery (*ptr)) {
5070 *store_pos ++ = p [0];
5071 HEAVY_STAT (++stat_global_remsets_readded);
5075 /* Truncate the remset */
5076 remset->store_next = store_pos;
5081 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5084 SgenThreadInfo *info;
5085 RememberedSet *remset;
5086 GenericStoreRememberedSet *store_remset;
5089 #ifdef HEAVY_STATISTICS
5093 /* the generic store ones */
5094 store_remset = generic_store_remsets;
5095 while (store_remset) {
5096 GenericStoreRememberedSet *next = store_remset->next;
5098 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5099 gpointer addr = store_remset->data [i];
5101 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5104 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5106 store_remset = next;
5108 generic_store_remsets = NULL;
5110 /* the per-thread ones */
5111 FOREACH_THREAD (info) {
5112 RememberedSet *next;
5114 for (remset = info->remset; remset; remset = next) {
5115 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));
5116 for (p = remset->data; p < remset->store_next;)
5117 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5118 remset->store_next = remset->data;
5119 next = remset->next;
5120 remset->next = NULL;
5121 if (remset != info->remset) {
5122 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5123 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5126 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5127 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5128 clear_thread_store_remset_buffer (info);
5129 } END_FOREACH_THREAD
5131 /* the freed thread ones */
5132 while (freed_thread_remsets) {
5133 RememberedSet *next;
5134 remset = freed_thread_remsets;
5135 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));
5136 for (p = remset->data; p < remset->store_next;)
5137 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5138 next = remset->next;
5139 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5140 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5141 freed_thread_remsets = next;
5146 * Clear the info in the remembered sets: we're doing a major collection, so
5147 * the per-thread ones are not needed and the global ones will be reconstructed
5151 clear_remsets (void)
5153 SgenThreadInfo *info;
5154 RememberedSet *remset, *next;
5156 /* the global list */
5157 for (remset = global_remset; remset; remset = next) {
5158 remset->store_next = remset->data;
5159 next = remset->next;
5160 remset->next = NULL;
5161 if (remset != global_remset) {
5162 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5163 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5166 /* the generic store ones */
5167 while (generic_store_remsets) {
5168 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5169 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5170 generic_store_remsets = gs_next;
5172 /* the per-thread ones */
5173 FOREACH_THREAD (info) {
5174 for (remset = info->remset; remset; remset = next) {
5175 remset->store_next = remset->data;
5176 next = remset->next;
5177 remset->next = NULL;
5178 if (remset != info->remset) {
5179 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5180 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5183 clear_thread_store_remset_buffer (info);
5184 } END_FOREACH_THREAD
5186 /* the freed thread ones */
5187 while (freed_thread_remsets) {
5188 next = freed_thread_remsets->next;
5189 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5190 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5191 freed_thread_remsets = next;
5196 * Clear the thread local TLAB variables for all threads.
5201 SgenThreadInfo *info;
5203 FOREACH_THREAD (info) {
5204 /* A new TLAB will be allocated when the thread does its first allocation */
5205 *info->tlab_start_addr = NULL;
5206 *info->tlab_next_addr = NULL;
5207 *info->tlab_temp_end_addr = NULL;
5208 *info->tlab_real_end_addr = NULL;
5209 } END_FOREACH_THREAD
5213 sgen_thread_register (SgenThreadInfo* info, void *addr)
5215 #ifndef HAVE_KW_THREAD
5216 SgenThreadInfo *__thread_info__ = info;
5220 #ifndef HAVE_KW_THREAD
5221 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5223 g_assert (!pthread_getspecific (thread_info_key));
5224 pthread_setspecific (thread_info_key, info);
5229 info->stop_count = -1;
5232 info->doing_handshake = FALSE;
5233 info->thread_is_dying = FALSE;
5234 info->stack_start = NULL;
5235 info->tlab_start_addr = &TLAB_START;
5236 info->tlab_next_addr = &TLAB_NEXT;
5237 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5238 info->tlab_real_end_addr = &TLAB_REAL_END;
5239 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5240 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5241 info->stopped_ip = NULL;
5242 info->stopped_domain = NULL;
5244 info->monoctx = NULL;
5246 info->stopped_regs = NULL;
5249 binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
5251 #ifdef HAVE_KW_THREAD
5252 tlab_next_addr = &tlab_next;
5253 store_remset_buffer_index_addr = &store_remset_buffer_index;
5256 #if defined(__MACH__)
5257 info->mach_port = mach_thread_self ();
5260 /* try to get it with attributes first */
5261 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5265 pthread_attr_t attr;
5266 pthread_getattr_np (pthread_self (), &attr);
5267 pthread_attr_getstack (&attr, &sstart, &size);
5268 info->stack_start_limit = sstart;
5269 info->stack_end = (char*)sstart + size;
5270 pthread_attr_destroy (&attr);
5272 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5273 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5274 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5277 /* FIXME: we assume the stack grows down */
5278 gsize stack_bottom = (gsize)addr;
5279 stack_bottom += 4095;
5280 stack_bottom &= ~4095;
5281 info->stack_end = (char*)stack_bottom;
5285 #ifdef HAVE_KW_THREAD
5286 stack_end = info->stack_end;
5289 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info, FALSE);
5290 pthread_setspecific (remembered_set_key, info->remset);
5291 #ifdef HAVE_KW_THREAD
5292 remembered_set = info->remset;
5295 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5296 STORE_REMSET_BUFFER_INDEX = 0;
5298 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p)\n", info, (gpointer)mono_thread_info_get_tid (info)));
5300 if (gc_callbacks.thread_attach_func)
5301 info->runtime_data = gc_callbacks.thread_attach_func ();
5308 add_generic_store_remset_from_buffer (gpointer *buffer)
5310 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5311 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5312 remset->next = generic_store_remsets;
5313 generic_store_remsets = remset;
5317 sgen_thread_unregister (SgenThreadInfo *p)
5319 RememberedSet *rset;
5321 /* If a delegate is passed to native code and invoked on a thread we dont
5322 * know about, the jit will register it with mono_jit_thread_attach, but
5323 * we have no way of knowing when that thread goes away. SGen has a TSD
5324 * so we assume that if the domain is still registered, we can detach
5327 if (mono_domain_get ())
5328 mono_thread_detach (mono_thread_current ());
5330 p->thread_is_dying = TRUE;
5333 There is a race condition between a thread finishing executing and been removed
5334 from the GC thread set.
5335 This happens on posix systems when TLS data is been cleaned-up, libpthread will
5336 set the thread_info slot to NULL before calling the cleanup function. This
5337 opens a window in which the thread is registered but has a NULL TLS.
5339 The suspend signal handler needs TLS data to know where to store thread state
5340 data or otherwise it will simply ignore the thread.
5342 This solution works because the thread doing STW will wait until all threads been
5343 suspended handshake back, so there is no race between the doing_hankshake test
5344 and the suspend_thread call.
5346 This is not required on systems that do synchronous STW as those can deal with
5347 the above race at suspend time.
5349 FIXME: I believe we could avoid this by using mono_thread_info_lookup when
5350 mono_thread_info_current returns NULL. Or fix mono_thread_info_lookup to do so.
5352 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5355 while (!TRYLOCK_GC) {
5356 if (p->doing_handshake)
5357 suspend_thread (p, NULL);
5363 binary_protocol_thread_unregister ((gpointer)id);
5364 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)mono_thread_info_get_tid (p)));
5366 #if defined(__MACH__)
5367 mach_port_deallocate (current_task (), p->mach_port);
5370 if (gc_callbacks.thread_detach_func) {
5371 gc_callbacks.thread_detach_func (p->runtime_data);
5372 p->runtime_data = NULL;
5376 if (freed_thread_remsets) {
5377 for (rset = p->remset; rset->next; rset = rset->next)
5379 rset->next = freed_thread_remsets;
5380 freed_thread_remsets = p->remset;
5382 freed_thread_remsets = p->remset;
5385 if (*p->store_remset_buffer_index_addr)
5386 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5387 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5389 * This is currently not strictly required, but we do it
5390 * anyway in case we change thread unregistering:
5392 * If the thread is removed from the thread list after
5393 * unregistering (this is currently not the case), and a
5394 * collection occurs, clear_remsets() would want to memset
5395 * this buffer, which would either clobber memory or crash.
5397 *p->store_remset_buffer_addr = NULL;
5399 mono_threads_unregister_current_thread (p);
5405 sgen_thread_attach (SgenThreadInfo *info)
5408 /*this is odd, can we get attached before the gc is inited?*/
5412 if (gc_callbacks.thread_attach_func && !info->runtime_data)
5413 info->runtime_data = gc_callbacks.thread_attach_func ();
5415 /* Need a better place to initialize this */
5416 if (!array_fill_vtable && mono_get_root_domain ()) {
5417 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
5422 mono_gc_register_thread (void *baseptr)
5424 return mono_thread_info_attach (baseptr) != NULL;
5428 * mono_gc_set_stack_end:
5430 * Set the end of the current threads stack to STACK_END. The stack space between
5431 * STACK_END and the real end of the threads stack will not be scanned during collections.
5434 mono_gc_set_stack_end (void *stack_end)
5436 SgenThreadInfo *info;
5439 info = mono_thread_info_current ();
5441 g_assert (stack_end < info->stack_end);
5442 info->stack_end = stack_end;
5447 #if USE_PTHREAD_INTERCEPT
5451 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5453 return mono_threads_pthread_create (new_thread, attr, start_routine, arg);
5457 mono_gc_pthread_join (pthread_t thread, void **retval)
5459 return pthread_join (thread, retval);
5463 mono_gc_pthread_detach (pthread_t thread)
5465 return pthread_detach (thread);
5468 #endif /* USE_PTHREAD_INTERCEPT */
5471 * ######################################################################
5472 * ######## Write barriers
5473 * ######################################################################
5477 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
5480 dummy_use (gpointer v) {
5481 __asm__ volatile ("" : "=r"(v) : "r"(v));
5485 static RememberedSet*
5486 alloc_remset (int size, gpointer id, gboolean global)
5488 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
5489 res->store_next = res->data;
5490 res->end_set = res->data + size;
5492 DEBUG (4, fprintf (gc_debug_file, "Allocated%s remset size %d at %p for %p\n", global ? " global" : "", size, res->data, id));
5497 * Note: the write barriers first do the needed GC work and then do the actual store:
5498 * this way the value is visible to the conservative GC scan after the write barrier
5499 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
5500 * the conservative scan, otherwise by the remembered set scan.
5503 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
5505 HEAVY_STAT (++stat_wbarrier_set_field);
5506 if (ptr_in_nursery (field_ptr)) {
5507 *(void**)field_ptr = value;
5510 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
5511 if (use_cardtable) {
5512 *(void**)field_ptr = value;
5513 if (ptr_in_nursery (value))
5514 sgen_card_table_mark_address ((mword)field_ptr);
5521 rs = REMEMBERED_SET;
5522 if (rs->store_next < rs->end_set) {
5523 *(rs->store_next++) = (mword)field_ptr;
5524 *(void**)field_ptr = value;
5528 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5529 rs->next = REMEMBERED_SET;
5530 REMEMBERED_SET = rs;
5531 #ifdef HAVE_KW_THREAD
5532 mono_thread_info_current ()->remset = rs;
5534 *(rs->store_next++) = (mword)field_ptr;
5535 *(void**)field_ptr = value;
5541 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
5543 HEAVY_STAT (++stat_wbarrier_set_arrayref);
5544 if (ptr_in_nursery (slot_ptr)) {
5545 *(void**)slot_ptr = value;
5548 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
5549 if (use_cardtable) {
5550 *(void**)slot_ptr = value;
5551 if (ptr_in_nursery (value))
5552 sgen_card_table_mark_address ((mword)slot_ptr);
5559 rs = REMEMBERED_SET;
5560 if (rs->store_next < rs->end_set) {
5561 *(rs->store_next++) = (mword)slot_ptr;
5562 *(void**)slot_ptr = value;
5566 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5567 rs->next = REMEMBERED_SET;
5568 REMEMBERED_SET = rs;
5569 #ifdef HAVE_KW_THREAD
5570 mono_thread_info_current ()->remset = rs;
5572 *(rs->store_next++) = (mword)slot_ptr;
5573 *(void**)slot_ptr = value;
5579 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
5581 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
5582 /*This check can be done without taking a lock since dest_ptr array is pinned*/
5583 if (ptr_in_nursery (dest_ptr) || count <= 0) {
5584 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5588 if (use_cardtable) {
5589 gpointer *dest = dest_ptr;
5590 gpointer *src = src_ptr;
5592 /*overlapping that required backward copying*/
5593 if (src < dest && (src + count) > dest) {
5594 gpointer *start = dest;
5598 for (; dest >= start; --src, --dest) {
5599 gpointer value = *src;
5601 if (ptr_in_nursery (value))
5602 sgen_card_table_mark_address ((mword)dest);
5606 gpointer *end = dest + count;
5607 for (; dest < end; ++src, ++dest) {
5608 gpointer value = *src;
5610 if (ptr_in_nursery (value))
5611 sgen_card_table_mark_address ((mword)dest);
5619 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5621 rs = REMEMBERED_SET;
5622 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
5623 if (rs->store_next + 1 < rs->end_set) {
5624 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5625 *(rs->store_next++) = count;
5629 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5630 rs->next = REMEMBERED_SET;
5631 REMEMBERED_SET = rs;
5632 #ifdef HAVE_KW_THREAD
5633 mono_thread_info_current ()->remset = rs;
5635 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5636 *(rs->store_next++) = count;
5642 static char *found_obj;
5645 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
5647 char *ptr = user_data;
5649 if (ptr >= obj && ptr < obj + size) {
5650 g_assert (!found_obj);
5655 /* for use in the debugger */
5656 char* find_object_for_ptr (char *ptr);
5658 find_object_for_ptr (char *ptr)
5660 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
5662 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
5663 find_object_for_ptr_callback, ptr, TRUE);
5669 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
5674 * Very inefficient, but this is debugging code, supposed to
5675 * be called from gdb, so we don't care.
5678 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
5683 evacuate_remset_buffer (void)
5688 buffer = STORE_REMSET_BUFFER;
5690 add_generic_store_remset_from_buffer (buffer);
5691 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5693 STORE_REMSET_BUFFER_INDEX = 0;
5697 mono_gc_wbarrier_generic_nostore (gpointer ptr)
5703 HEAVY_STAT (++stat_wbarrier_generic_store);
5705 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
5706 /* FIXME: ptr_in_heap must be called with the GC lock held */
5707 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
5708 char *start = find_object_for_ptr (ptr);
5709 MonoObject *value = *(MonoObject**)ptr;
5713 MonoObject *obj = (MonoObject*)start;
5714 if (obj->vtable->domain != value->vtable->domain)
5715 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
5721 if (*(gpointer*)ptr)
5722 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
5724 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
5725 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
5729 if (use_cardtable) {
5730 if (ptr_in_nursery(*(gpointer*)ptr))
5731 sgen_card_table_mark_address ((mword)ptr);
5737 buffer = STORE_REMSET_BUFFER;
5738 index = STORE_REMSET_BUFFER_INDEX;
5739 /* This simple optimization eliminates a sizable portion of
5740 entries. Comparing it to the last but one entry as well
5741 doesn't eliminate significantly more entries. */
5742 if (buffer [index] == ptr) {
5747 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
5748 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
5751 if (index >= STORE_REMSET_BUFFER_SIZE) {
5752 evacuate_remset_buffer ();
5753 index = STORE_REMSET_BUFFER_INDEX;
5754 g_assert (index == 0);
5757 buffer [index] = ptr;
5758 STORE_REMSET_BUFFER_INDEX = index;
5764 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
5766 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
5767 *(void**)ptr = value;
5768 if (ptr_in_nursery (value))
5769 mono_gc_wbarrier_generic_nostore (ptr);
5773 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
5775 mword *dest = _dest;
5780 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
5785 size -= SIZEOF_VOID_P;
5792 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
5795 size_t size = count * mono_class_value_size (klass, NULL);
5797 HEAVY_STAT (++stat_wbarrier_value_copy);
5798 g_assert (klass->valuetype);
5799 if (use_cardtable) {
5800 #ifdef DISABLE_CRITICAL_REGION
5803 ENTER_CRITICAL_REGION;
5805 mono_gc_memmove (dest, src, size);
5806 sgen_card_table_mark_range ((mword)dest, size);
5807 #ifdef DISABLE_CRITICAL_REGION
5810 EXIT_CRITICAL_REGION;
5814 mono_gc_memmove (dest, src, size);
5815 rs = REMEMBERED_SET;
5816 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
5820 g_assert (klass->gc_descr_inited);
5821 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));
5823 if (rs->store_next + 4 < rs->end_set) {
5824 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5825 *(rs->store_next++) = (mword)klass->gc_descr;
5826 *(rs->store_next++) = (mword)count;
5827 *(rs->store_next++) = (mword)size;
5831 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5832 rs->next = REMEMBERED_SET;
5833 REMEMBERED_SET = rs;
5834 #ifdef HAVE_KW_THREAD
5835 mono_thread_info_current ()->remset = rs;
5837 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5838 *(rs->store_next++) = (mword)klass->gc_descr;
5839 *(rs->store_next++) = (mword)count;
5840 *(rs->store_next++) = (mword)size;
5846 * mono_gc_wbarrier_object_copy:
5848 * Write barrier to call when obj is the result of a clone or copy of an object.
5851 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
5857 HEAVY_STAT (++stat_wbarrier_object_copy);
5858 rs = REMEMBERED_SET;
5859 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
5860 size = mono_object_class (obj)->instance_size;
5862 /* do not copy the sync state */
5863 mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
5864 size - sizeof (MonoObject));
5865 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
5869 if (rs->store_next < rs->end_set) {
5870 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5874 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5875 rs->next = REMEMBERED_SET;
5876 REMEMBERED_SET = rs;
5878 #ifdef HAVE_KW_THREAD
5879 mono_thread_info_current ()->remset = rs;
5881 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5886 * ######################################################################
5887 * ######## Collector debugging
5888 * ######################################################################
5891 const char*descriptor_types [] = {
5903 describe_ptr (char *ptr)
5910 if (ptr_in_nursery (ptr)) {
5911 printf ("Pointer inside nursery.\n");
5913 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
5915 printf ("Pointer is the start of object %p in LOS space.\n", start);
5917 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
5919 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
5920 printf ("Pointer inside oldspace.\n");
5921 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
5922 printf ("Pointer is inside a pinned chunk.\n");
5924 printf ("Pointer unknown.\n");
5929 if (object_is_pinned (ptr))
5930 printf ("Object is pinned.\n");
5932 if (object_is_forwarded (ptr))
5933 printf ("Object is forwared.\n");
5935 // FIXME: Handle pointers to the inside of objects
5936 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
5938 printf ("VTable: %p\n", vtable);
5939 if (vtable == NULL) {
5940 printf ("VTable is invalid (empty).\n");
5943 if (ptr_in_nursery (vtable)) {
5944 printf ("VTable is invalid (points inside nursery).\n");
5947 printf ("Class: %s\n", vtable->klass->name);
5949 desc = ((GCVTable*)vtable)->desc;
5950 printf ("Descriptor: %lx\n", (long)desc);
5953 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
5957 find_in_remset_loc (mword *p, char *addr, gboolean *found)
5963 switch ((*p) & REMSET_TYPE_MASK) {
5964 case REMSET_LOCATION:
5965 if (*p == (mword)addr)
5969 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5971 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5975 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5976 count = safe_object_get_size ((MonoObject*)ptr);
5977 count = ALIGN_UP (count);
5978 count /= sizeof (mword);
5979 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5983 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5988 /* The descriptor includes the size of MonoObject */
5989 skip_size -= sizeof (MonoObject);
5991 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
5996 g_assert_not_reached ();
6002 * Return whenever ADDR occurs in the remembered sets
6005 find_in_remsets (char *addr)
6008 SgenThreadInfo *info;
6009 RememberedSet *remset;
6010 GenericStoreRememberedSet *store_remset;
6012 gboolean found = FALSE;
6014 /* the global one */
6015 for (remset = global_remset; remset; remset = remset->next) {
6016 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));
6017 for (p = remset->data; p < remset->store_next;) {
6018 p = find_in_remset_loc (p, addr, &found);
6024 /* the generic store ones */
6025 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6026 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6027 if (store_remset->data [i] == addr)
6032 /* the per-thread ones */
6033 FOREACH_THREAD (info) {
6035 for (remset = info->remset; remset; remset = remset->next) {
6036 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));
6037 for (p = remset->data; p < remset->store_next;) {
6038 p = find_in_remset_loc (p, addr, &found);
6043 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6044 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6047 } END_FOREACH_THREAD
6049 /* the freed thread ones */
6050 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6051 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));
6052 for (p = remset->data; p < remset->store_next;) {
6053 p = find_in_remset_loc (p, addr, &found);
6062 static gboolean missing_remsets;
6065 * We let a missing remset slide if the target object is pinned,
6066 * because the store might have happened but the remset not yet added,
6067 * but in that case the target must be pinned. We might theoretically
6068 * miss some missing remsets this way, but it's very unlikely.
6071 #define HANDLE_PTR(ptr,obj) do { \
6072 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_end) { \
6073 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6074 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); \
6075 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6076 if (!object_is_pinned (*(ptr))) \
6077 missing_remsets = TRUE; \
6083 * Check that each object reference which points into the nursery can
6084 * be found in the remembered sets.
6087 check_consistency_callback (char *start, size_t size, void *dummy)
6089 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6090 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6092 #define SCAN_OBJECT_ACTION
6093 #include "sgen-scan-object.h"
6097 * Perform consistency check of the heap.
6099 * Assumes the world is stopped.
6102 check_consistency (void)
6104 // Need to add more checks
6106 missing_remsets = FALSE;
6108 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6110 // Check that oldspace->newspace pointers are registered with the collector
6111 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6113 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6115 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6117 if (!binary_protocol_is_enabled ())
6118 g_assert (!missing_remsets);
6123 #define HANDLE_PTR(ptr,obj) do { \
6124 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6125 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6129 check_major_refs_callback (char *start, size_t size, void *dummy)
6131 #define SCAN_OBJECT_ACTION
6132 #include "sgen-scan-object.h"
6136 check_major_refs (void)
6138 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6139 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6142 /* Check that the reference is valid */
6144 #define HANDLE_PTR(ptr,obj) do { \
6146 g_assert (safe_name (*(ptr)) != NULL); \
6153 * Perform consistency check on an object. Currently we only check that the
6154 * reference fields are valid.
6157 check_object (char *start)
6162 #include "sgen-scan-object.h"
6166 * ######################################################################
6167 * ######## Other mono public interface functions.
6168 * ######################################################################
6171 #define REFS_SIZE 128
6174 MonoGCReferences callback;
6178 MonoObject *refs [REFS_SIZE];
6179 uintptr_t offsets [REFS_SIZE];
6183 #define HANDLE_PTR(ptr,obj) do { \
6185 if (hwi->count == REFS_SIZE) { \
6186 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
6190 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
6191 hwi->refs [hwi->count++] = *(ptr); \
6196 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6198 #include "sgen-scan-object.h"
6202 walk_references (char *start, size_t size, void *data)
6204 HeapWalkInfo *hwi = data;
6207 collect_references (hwi, start, size);
6208 if (hwi->count || !hwi->called)
6209 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6213 * mono_gc_walk_heap:
6214 * @flags: flags for future use
6215 * @callback: a function pointer called for each object in the heap
6216 * @data: a user data pointer that is passed to callback
6218 * This function can be used to iterate over all the live objects in the heap:
6219 * for each object, @callback is invoked, providing info about the object's
6220 * location in memory, its class, its size and the objects it references.
6221 * For each referenced object it's offset from the object address is
6222 * reported in the offsets array.
6223 * The object references may be buffered, so the callback may be invoked
6224 * multiple times for the same object: in all but the first call, the size
6225 * argument will be zero.
6226 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6227 * profiler event handler.
6229 * Returns: a non-zero value if the GC doesn't support heap walking
6232 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6237 hwi.callback = callback;
6240 mono_sgen_clear_nursery_fragments ();
6241 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
6243 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
6244 mono_sgen_los_iterate_objects (walk_references, &hwi);
6250 mono_gc_collect (int generation)
6255 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6256 stop_world (generation);
6257 if (generation == 0) {
6258 collect_nursery (0);
6260 major_collection ("user request");
6262 restart_world (generation);
6263 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6268 mono_gc_max_generation (void)
6274 mono_gc_collection_count (int generation)
6276 if (generation == 0)
6277 return num_minor_gcs;
6278 return num_major_gcs;
6282 mono_gc_get_used_size (void)
6286 tot = los_memory_usage;
6287 tot += nursery_section->next_data - nursery_section->data;
6288 tot += major_collector.get_used_size ();
6289 /* FIXME: account for pinned objects */
6295 mono_gc_get_heap_size (void)
6301 mono_gc_disable (void)
6309 mono_gc_enable (void)
6317 mono_gc_get_los_limit (void)
6319 return MAX_SMALL_OBJ_SIZE;
6323 mono_object_is_alive (MonoObject* o)
6329 mono_gc_get_generation (MonoObject *obj)
6331 if (ptr_in_nursery (obj))
6337 mono_gc_enable_events (void)
6342 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6344 mono_gc_register_disappearing_link (obj, link_addr, track, FALSE);
6348 mono_gc_weak_link_remove (void **link_addr)
6350 mono_gc_register_disappearing_link (NULL, link_addr, FALSE, FALSE);
6354 mono_gc_weak_link_get (void **link_addr)
6358 return (MonoObject*) REVEAL_POINTER (*link_addr);
6362 mono_gc_ephemeron_array_add (MonoObject *obj)
6364 EphemeronLinkNode *node;
6368 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
6373 node->array = (char*)obj;
6374 node->next = ephemeron_list;
6375 ephemeron_list = node;
6377 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6384 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6387 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
6388 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6389 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6391 mword complex = alloc_complex_descriptor (bitmap, numbits);
6392 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6396 static void *all_ref_root_descrs [32];
6399 mono_gc_make_root_descr_all_refs (int numbits)
6403 int num_bytes = numbits / 8;
6405 if (numbits < 32 && all_ref_root_descrs [numbits])
6406 return all_ref_root_descrs [numbits];
6408 gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
6409 memset (gc_bitmap, 0xff, num_bytes);
6410 if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT))
6411 gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
6412 else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
6413 gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
6415 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
6416 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
6420 all_ref_root_descrs [numbits] = descr;
6426 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6430 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6431 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6432 user_descriptors [user_descriptors_next ++] = marker;
6438 mono_gc_alloc_fixed (size_t size, void *descr)
6440 /* FIXME: do a single allocation */
6441 void *res = calloc (1, size);
6444 if (!mono_gc_register_root (res, size, descr)) {
6452 mono_gc_free_fixed (void* addr)
6454 mono_gc_deregister_root (addr);
6459 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6463 result = func (data);
6464 UNLOCK_INTERRUPTION;
6469 mono_gc_is_gc_thread (void)
6473 result = mono_thread_info_current () != NULL;
6479 is_critical_method (MonoMethod *method)
6481 return mono_runtime_is_critical_method (method) || mono_gc_is_critical_method (method);
6485 mono_gc_base_init (void)
6487 MonoThreadInfoCallbacks cb;
6490 char *major_collector_opt = NULL;
6491 struct sigaction sinfo;
6497 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
6500 /* already inited */
6503 /* being inited by another thread */
6507 /* we will init it */
6510 g_assert_not_reached ();
6512 } while (result != 0);
6514 LOCK_INIT (gc_mutex);
6516 pagesize = mono_pagesize ();
6517 gc_debug_file = stdout;
6519 cb.thread_register = sgen_thread_register;
6520 cb.thread_unregister = sgen_thread_unregister;
6521 cb.thread_attach = sgen_thread_attach;
6522 cb.mono_method_is_critical = is_critical_method;
6524 mono_threads_init (&cb, sizeof (SgenThreadInfo));
6526 LOCK_INIT (interruption_mutex);
6527 LOCK_INIT (global_remset_mutex);
6528 LOCK_INIT (pin_queue_mutex);
6530 if ((env = getenv ("MONO_GC_PARAMS"))) {
6531 opts = g_strsplit (env, ",", -1);
6532 for (ptr = opts; *ptr; ++ptr) {
6534 if (g_str_has_prefix (opt, "major=")) {
6535 opt = strchr (opt, '=') + 1;
6536 major_collector_opt = g_strdup (opt);
6544 mono_sgen_init_internal_allocator ();
6545 mono_sgen_init_nursery_allocator ();
6547 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
6548 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
6549 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
6550 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
6551 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
6552 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6553 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
6554 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
6556 pthread_key_create (&remembered_set_key, NULL);
6558 #ifndef HAVE_KW_THREAD
6559 pthread_key_create (&thread_info_key, NULL);
6563 * This needs to happen before any internal allocations because
6564 * it inits the small id which is required for hazard pointer
6567 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
6568 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
6570 sigfillset (&sinfo.sa_mask);
6571 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
6572 sinfo.sa_sigaction = suspend_handler;
6573 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
6574 g_error ("failed sigaction");
6577 sinfo.sa_handler = restart_handler;
6578 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
6579 g_error ("failed sigaction");
6582 sigfillset (&suspend_signal_mask);
6583 sigdelset (&suspend_signal_mask, restart_signal_num);
6585 mono_thread_info_attach (&sinfo);
6587 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
6588 mono_sgen_marksweep_init (&major_collector);
6589 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
6590 mono_sgen_marksweep_fixed_init (&major_collector);
6591 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
6592 mono_sgen_marksweep_par_init (&major_collector);
6593 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
6594 mono_sgen_marksweep_fixed_par_init (&major_collector);
6595 } else if (!strcmp (major_collector_opt, "copying")) {
6596 mono_sgen_copying_init (&major_collector);
6598 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
6602 #ifdef SGEN_HAVE_CARDTABLE
6603 use_cardtable = major_collector.supports_cardtable;
6605 use_cardtable = FALSE;
6608 num_workers = mono_cpu_count ();
6609 g_assert (num_workers > 0);
6610 if (num_workers > 16)
6613 /* Keep this the default for now */
6614 conservative_stack_mark = TRUE;
6617 for (ptr = opts; *ptr; ++ptr) {
6619 if (g_str_has_prefix (opt, "major="))
6621 if (g_str_has_prefix (opt, "wbarrier=")) {
6622 opt = strchr (opt, '=') + 1;
6623 if (strcmp (opt, "remset") == 0) {
6624 use_cardtable = FALSE;
6625 } else if (strcmp (opt, "cardtable") == 0) {
6626 if (!use_cardtable) {
6627 if (major_collector.supports_cardtable)
6628 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
6630 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
6636 if (g_str_has_prefix (opt, "max-heap-size=")) {
6637 opt = strchr (opt, '=') + 1;
6638 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
6639 if ((max_heap & (mono_pagesize () - 1))) {
6640 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
6644 fprintf (stderr, "max-heap-size must be an integer.\n");
6649 if (g_str_has_prefix (opt, "workers=")) {
6652 if (!major_collector.is_parallel) {
6653 fprintf (stderr, "The workers= option can only be used for parallel collectors.");
6656 opt = strchr (opt, '=') + 1;
6657 val = strtol (opt, &endptr, 10);
6658 if (!*opt || *endptr) {
6659 fprintf (stderr, "Cannot parse the workers= option value.");
6662 if (val <= 0 || val > 16) {
6663 fprintf (stderr, "The number of workers must be in the range 1 to 16.");
6666 num_workers = (int)val;
6669 if (g_str_has_prefix (opt, "stack-mark=")) {
6670 opt = strchr (opt, '=') + 1;
6671 if (!strcmp (opt, "precise")) {
6672 conservative_stack_mark = FALSE;
6673 } else if (!strcmp (opt, "conservative")) {
6674 conservative_stack_mark = TRUE;
6676 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
6682 if (g_str_has_prefix (opt, "nursery-size=")) {
6684 opt = strchr (opt, '=') + 1;
6685 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
6686 default_nursery_size = val;
6687 #ifdef SGEN_ALIGN_NURSERY
6688 if ((val & (val - 1))) {
6689 fprintf (stderr, "The nursery size must be a power of two.\n");
6693 default_nursery_bits = 0;
6694 while (1 << (++ default_nursery_bits) != default_nursery_size)
6698 fprintf (stderr, "nursery-size must be an integer.\n");
6704 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
6705 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
6706 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6707 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6708 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
6709 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
6710 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
6711 if (major_collector.print_gc_param_usage)
6712 major_collector.print_gc_param_usage ();
6719 if (major_collector.is_parallel)
6720 workers_init (num_workers);
6722 if (major_collector_opt)
6723 g_free (major_collector_opt);
6725 nursery_size = DEFAULT_NURSERY_SIZE;
6726 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
6727 init_heap_size_limits (max_heap);
6731 if ((env = getenv ("MONO_GC_DEBUG"))) {
6732 opts = g_strsplit (env, ",", -1);
6733 for (ptr = opts; ptr && *ptr; ptr ++) {
6735 if (opt [0] >= '0' && opt [0] <= '9') {
6736 gc_debug_level = atoi (opt);
6741 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
6742 gc_debug_file = fopen (rf, "wb");
6744 gc_debug_file = stderr;
6747 } else if (!strcmp (opt, "collect-before-allocs")) {
6748 collect_before_allocs = 1;
6749 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
6750 char *arg = strchr (opt, '=') + 1;
6751 collect_before_allocs = atoi (arg);
6752 } else if (!strcmp (opt, "check-at-minor-collections")) {
6753 consistency_check_at_minor_collection = TRUE;
6754 nursery_clear_policy = CLEAR_AT_GC;
6755 } else if (!strcmp (opt, "xdomain-checks")) {
6756 xdomain_checks = TRUE;
6757 } else if (!strcmp (opt, "clear-at-gc")) {
6758 nursery_clear_policy = CLEAR_AT_GC;
6759 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
6760 nursery_clear_policy = CLEAR_AT_GC;
6761 } else if (!strcmp (opt, "check-scan-starts")) {
6762 do_scan_starts_check = TRUE;
6763 } else if (!strcmp (opt, "disable-minor")) {
6764 disable_minor_collections = TRUE;
6765 } else if (!strcmp (opt, "disable-major")) {
6766 disable_major_collections = TRUE;
6767 } else if (g_str_has_prefix (opt, "heap-dump=")) {
6768 char *filename = strchr (opt, '=') + 1;
6769 nursery_clear_policy = CLEAR_AT_GC;
6770 heap_dump_file = fopen (filename, "w");
6772 fprintf (heap_dump_file, "<sgen-dump>\n");
6773 #ifdef SGEN_BINARY_PROTOCOL
6774 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
6775 char *filename = strchr (opt, '=') + 1;
6776 binary_protocol_init (filename);
6779 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
6780 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
6781 fprintf (stderr, "Valid options are:\n");
6782 fprintf (stderr, " collect-before-allocs[=<n>]\n");
6783 fprintf (stderr, " check-at-minor-collections\n");
6784 fprintf (stderr, " disable-minor\n");
6785 fprintf (stderr, " disable-major\n");
6786 fprintf (stderr, " xdomain-checks\n");
6787 fprintf (stderr, " clear-at-gc\n");
6794 if (major_collector.post_param_init)
6795 major_collector.post_param_init ();
6797 global_remset = alloc_remset (1024, NULL, FALSE);
6798 global_remset->next = NULL;
6807 mono_gc_get_suspend_signal (void)
6809 return suspend_signal_num;
6819 #ifdef HAVE_KW_THREAD
6820 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
6821 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6822 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6823 mono_mb_emit_i4 ((mb), (offset)); \
6828 * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
6829 * where the two are the same.
6832 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
6833 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6834 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6835 mono_mb_emit_i4 ((mb), thread_info_key); \
6836 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
6837 mono_mb_emit_byte ((mb), CEE_ADD); \
6838 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
6841 #define EMIT_TLS_ACCESS(mb,member,dummy) do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
6846 #ifdef MANAGED_ALLOCATION
6847 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
6848 * for each class. This is currently not easy to do, as it is hard to generate basic
6849 * blocks + branches, but it is easy with the linear IL codebase.
6851 * For this to work we'd need to solve the TLAB race, first. Now we
6852 * require the allocator to be in a few known methods to make sure
6853 * that they are executed atomically via the restart mechanism.
6856 create_allocator (int atype)
6858 int p_var, size_var;
6859 guint32 slowpath_branch, max_size_branch;
6860 MonoMethodBuilder *mb;
6862 MonoMethodSignature *csig;
6863 static gboolean registered = FALSE;
6864 int tlab_next_addr_var, new_next_var;
6866 const char *name = NULL;
6867 AllocatorWrapperInfo *info;
6869 #ifdef HAVE_KW_THREAD
6870 int tlab_next_addr_offset = -1;
6871 int tlab_temp_end_offset = -1;
6873 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
6874 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6876 g_assert (tlab_next_addr_offset != -1);
6877 g_assert (tlab_temp_end_offset != -1);
6881 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
6882 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
6886 if (atype == ATYPE_SMALL) {
6888 name = "AllocSmall";
6889 } else if (atype == ATYPE_NORMAL) {
6892 } else if (atype == ATYPE_VECTOR) {
6894 name = "AllocVector";
6896 g_assert_not_reached ();
6899 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
6900 csig->ret = &mono_defaults.object_class->byval_arg;
6901 for (i = 0; i < num_params; ++i)
6902 csig->params [i] = &mono_defaults.int_class->byval_arg;
6904 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
6905 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
6906 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6907 /* size = vtable->klass->instance_size; */
6908 mono_mb_emit_ldarg (mb, 0);
6909 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6910 mono_mb_emit_byte (mb, CEE_ADD);
6911 mono_mb_emit_byte (mb, CEE_LDIND_I);
6912 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
6913 mono_mb_emit_byte (mb, CEE_ADD);
6914 /* FIXME: assert instance_size stays a 4 byte integer */
6915 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6916 mono_mb_emit_stloc (mb, size_var);
6917 } else if (atype == ATYPE_VECTOR) {
6918 MonoExceptionClause *clause;
6920 MonoClass *oom_exc_class;
6923 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
6924 mono_mb_emit_ldarg (mb, 1);
6925 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
6926 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
6927 mono_mb_emit_exception (mb, "OverflowException", NULL);
6928 mono_mb_patch_short_branch (mb, pos);
6930 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
6931 clause->try_offset = mono_mb_get_label (mb);
6933 /* vtable->klass->sizes.element_size */
6934 mono_mb_emit_ldarg (mb, 0);
6935 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6936 mono_mb_emit_byte (mb, CEE_ADD);
6937 mono_mb_emit_byte (mb, CEE_LDIND_I);
6938 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
6939 mono_mb_emit_byte (mb, CEE_ADD);
6940 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6943 mono_mb_emit_ldarg (mb, 1);
6944 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
6945 /* + sizeof (MonoArray) */
6946 mono_mb_emit_icon (mb, sizeof (MonoArray));
6947 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
6948 mono_mb_emit_stloc (mb, size_var);
6950 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
6953 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
6954 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
6955 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
6956 "System", "OverflowException");
6957 g_assert (clause->data.catch_class);
6958 clause->handler_offset = mono_mb_get_label (mb);
6960 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
6961 "System", "OutOfMemoryException");
6962 g_assert (oom_exc_class);
6963 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
6966 mono_mb_emit_byte (mb, CEE_POP);
6967 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
6968 mono_mb_emit_byte (mb, CEE_THROW);
6970 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
6971 mono_mb_set_clauses (mb, 1, clause);
6972 mono_mb_patch_branch (mb, pos_leave);
6975 g_assert_not_reached ();
6978 /* size += ALLOC_ALIGN - 1; */
6979 mono_mb_emit_ldloc (mb, size_var);
6980 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
6981 mono_mb_emit_byte (mb, CEE_ADD);
6982 /* size &= ~(ALLOC_ALIGN - 1); */
6983 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
6984 mono_mb_emit_byte (mb, CEE_AND);
6985 mono_mb_emit_stloc (mb, size_var);
6987 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
6988 if (atype != ATYPE_SMALL) {
6989 mono_mb_emit_ldloc (mb, size_var);
6990 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
6991 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
6995 * We need to modify tlab_next, but the JIT only supports reading, so we read
6996 * another tls var holding its address instead.
6999 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7000 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7001 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7002 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7004 /* p = (void**)tlab_next; */
7005 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7006 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7007 mono_mb_emit_byte (mb, CEE_LDIND_I);
7008 mono_mb_emit_stloc (mb, p_var);
7010 /* new_next = (char*)p + size; */
7011 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7012 mono_mb_emit_ldloc (mb, p_var);
7013 mono_mb_emit_ldloc (mb, size_var);
7014 mono_mb_emit_byte (mb, CEE_CONV_I);
7015 mono_mb_emit_byte (mb, CEE_ADD);
7016 mono_mb_emit_stloc (mb, new_next_var);
7018 /* tlab_next = new_next */
7019 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7020 mono_mb_emit_ldloc (mb, new_next_var);
7021 mono_mb_emit_byte (mb, CEE_STIND_I);
7023 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7024 mono_mb_emit_ldloc (mb, new_next_var);
7025 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7026 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7029 if (atype != ATYPE_SMALL)
7030 mono_mb_patch_short_branch (mb, max_size_branch);
7032 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7033 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7035 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7036 mono_mb_emit_ldarg (mb, 0);
7037 mono_mb_emit_ldloc (mb, size_var);
7038 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7039 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7040 } else if (atype == ATYPE_VECTOR) {
7041 mono_mb_emit_ldarg (mb, 1);
7042 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7044 g_assert_not_reached ();
7046 mono_mb_emit_byte (mb, CEE_RET);
7049 mono_mb_patch_short_branch (mb, slowpath_branch);
7051 /* FIXME: Memory barrier */
7054 mono_mb_emit_ldloc (mb, p_var);
7055 mono_mb_emit_ldarg (mb, 0);
7056 mono_mb_emit_byte (mb, CEE_STIND_I);
7058 if (atype == ATYPE_VECTOR) {
7059 /* arr->max_length = max_length; */
7060 mono_mb_emit_ldloc (mb, p_var);
7061 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7062 mono_mb_emit_ldarg (mb, 1);
7063 mono_mb_emit_byte (mb, CEE_STIND_I);
7067 mono_mb_emit_ldloc (mb, p_var);
7068 mono_mb_emit_byte (mb, CEE_RET);
7070 res = mono_mb_create_method (mb, csig, 8);
7072 mono_method_get_header (res)->init_locals = FALSE;
7074 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7075 info->gc_name = "sgen";
7076 info->alloc_type = atype;
7077 mono_marshal_set_wrapper_info (res, info);
7084 mono_gc_get_gc_name (void)
7089 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7090 static MonoMethod *write_barrier_method;
7093 mono_gc_is_critical_method (MonoMethod *method)
7096 if (method == write_barrier_method)
7099 for (i = 0; i < ATYPE_NUM; ++i)
7100 if (method == alloc_method_cache [i])
7107 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7111 if (!mono_thread_internal_current ())
7112 /* Happens during thread attach */
7117 ji = mono_jit_info_table_find (domain, ip);
7121 return mono_gc_is_critical_method (ji->method);
7125 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7126 * The signature of the called method is:
7127 * object allocate (MonoVTable *vtable)
7130 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7132 #ifdef MANAGED_ALLOCATION
7133 MonoClass *klass = vtable->klass;
7135 #ifdef HAVE_KW_THREAD
7136 int tlab_next_offset = -1;
7137 int tlab_temp_end_offset = -1;
7138 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7139 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7141 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7145 if (!mono_runtime_has_tls_get ())
7147 if (klass->instance_size > tlab_size)
7149 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7153 if (klass->byval_arg.type == MONO_TYPE_STRING)
7155 if (collect_before_allocs)
7158 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7159 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7161 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7168 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7170 #ifdef MANAGED_ALLOCATION
7171 MonoClass *klass = vtable->klass;
7173 #ifdef HAVE_KW_THREAD
7174 int tlab_next_offset = -1;
7175 int tlab_temp_end_offset = -1;
7176 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7177 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7179 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7185 if (!mono_runtime_has_tls_get ())
7187 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7189 if (collect_before_allocs)
7191 g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
7193 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7200 mono_gc_get_managed_allocator_by_type (int atype)
7202 #ifdef MANAGED_ALLOCATION
7205 if (!mono_runtime_has_tls_get ())
7208 mono_loader_lock ();
7209 res = alloc_method_cache [atype];
7211 res = alloc_method_cache [atype] = create_allocator (atype);
7212 mono_loader_unlock ();
7220 mono_gc_get_managed_allocator_types (void)
7227 mono_gc_get_write_barrier (void)
7230 MonoMethodBuilder *mb;
7231 MonoMethodSignature *sig;
7232 #ifdef MANAGED_WBARRIER
7233 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7234 #ifndef SGEN_ALIGN_NURSERY
7235 int label_continue_1, label_continue_2, label_no_wb_5;
7236 int dereferenced_var;
7238 int buffer_var, buffer_index_var, dummy_var;
7240 #ifdef HAVE_KW_THREAD
7241 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7242 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7244 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7245 g_assert (stack_end_offset != -1);
7246 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7247 g_assert (store_remset_buffer_offset != -1);
7248 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7249 g_assert (store_remset_buffer_index_offset != -1);
7250 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7251 g_assert (store_remset_buffer_index_addr_offset != -1);
7255 g_assert (!use_cardtable);
7257 // FIXME: Maybe create a separate version for ctors (the branch would be
7258 // correctly predicted more times)
7259 if (write_barrier_method)
7260 return write_barrier_method;
7262 /* Create the IL version of mono_gc_barrier_generic_store () */
7263 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7264 sig->ret = &mono_defaults.void_class->byval_arg;
7265 sig->params [0] = &mono_defaults.int_class->byval_arg;
7267 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7269 #ifdef MANAGED_WBARRIER
7270 if (mono_runtime_has_tls_get ()) {
7271 #ifdef SGEN_ALIGN_NURSERY
7272 // if (ptr_in_nursery (ptr)) return;
7274 * Masking out the bits might be faster, but we would have to use 64 bit
7275 * immediates, which might be slower.
7277 mono_mb_emit_ldarg (mb, 0);
7278 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7279 mono_mb_emit_byte (mb, CEE_SHR_UN);
7280 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7281 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7283 // if (!ptr_in_nursery (*ptr)) return;
7284 mono_mb_emit_ldarg (mb, 0);
7285 mono_mb_emit_byte (mb, CEE_LDIND_I);
7286 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7287 mono_mb_emit_byte (mb, CEE_SHR_UN);
7288 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7289 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7292 // if (ptr < (nursery_start)) goto continue;
7293 mono_mb_emit_ldarg (mb, 0);
7294 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7295 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7297 // if (ptr >= nursery_end)) goto continue;
7298 mono_mb_emit_ldarg (mb, 0);
7299 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7300 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7303 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7306 mono_mb_patch_branch (mb, label_continue_1);
7307 mono_mb_patch_branch (mb, label_continue_2);
7309 // Dereference and store in local var
7310 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7311 mono_mb_emit_ldarg (mb, 0);
7312 mono_mb_emit_byte (mb, CEE_LDIND_I);
7313 mono_mb_emit_stloc (mb, dereferenced_var);
7315 // if (*ptr < nursery_start) return;
7316 mono_mb_emit_ldloc (mb, dereferenced_var);
7317 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7318 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7320 // if (*ptr >= nursery_end) return;
7321 mono_mb_emit_ldloc (mb, dereferenced_var);
7322 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7323 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7326 // if (ptr >= stack_end) goto need_wb;
7327 mono_mb_emit_ldarg (mb, 0);
7328 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7329 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7331 // if (ptr >= stack_start) return;
7332 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7333 mono_mb_emit_ldarg (mb, 0);
7334 mono_mb_emit_ldloc_addr (mb, dummy_var);
7335 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7338 mono_mb_patch_branch (mb, label_need_wb);
7340 // buffer = STORE_REMSET_BUFFER;
7341 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7342 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7343 mono_mb_emit_stloc (mb, buffer_var);
7345 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7346 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7347 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7348 mono_mb_emit_stloc (mb, buffer_index_var);
7350 // if (buffer [buffer_index] == ptr) return;
7351 mono_mb_emit_ldloc (mb, buffer_var);
7352 mono_mb_emit_ldloc (mb, buffer_index_var);
7353 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7354 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7355 mono_mb_emit_byte (mb, CEE_SHL);
7356 mono_mb_emit_byte (mb, CEE_ADD);
7357 mono_mb_emit_byte (mb, CEE_LDIND_I);
7358 mono_mb_emit_ldarg (mb, 0);
7359 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7362 mono_mb_emit_ldloc (mb, buffer_index_var);
7363 mono_mb_emit_icon (mb, 1);
7364 mono_mb_emit_byte (mb, CEE_ADD);
7365 mono_mb_emit_stloc (mb, buffer_index_var);
7367 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7368 mono_mb_emit_ldloc (mb, buffer_index_var);
7369 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7370 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7372 // buffer [buffer_index] = ptr;
7373 mono_mb_emit_ldloc (mb, buffer_var);
7374 mono_mb_emit_ldloc (mb, buffer_index_var);
7375 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7376 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7377 mono_mb_emit_byte (mb, CEE_SHL);
7378 mono_mb_emit_byte (mb, CEE_ADD);
7379 mono_mb_emit_ldarg (mb, 0);
7380 mono_mb_emit_byte (mb, CEE_STIND_I);
7382 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7383 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7384 mono_mb_emit_ldloc (mb, buffer_index_var);
7385 mono_mb_emit_byte (mb, CEE_STIND_I);
7388 mono_mb_patch_branch (mb, label_no_wb_1);
7389 mono_mb_patch_branch (mb, label_no_wb_2);
7390 mono_mb_patch_branch (mb, label_no_wb_3);
7391 mono_mb_patch_branch (mb, label_no_wb_4);
7392 #ifndef SGEN_ALIGN_NURSERY
7393 mono_mb_patch_branch (mb, label_no_wb_5);
7395 mono_mb_emit_byte (mb, CEE_RET);
7398 mono_mb_patch_branch (mb, label_slow_path);
7402 mono_mb_emit_ldarg (mb, 0);
7403 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7404 mono_mb_emit_byte (mb, CEE_RET);
7406 res = mono_mb_create_method (mb, sig, 16);
7409 mono_loader_lock ();
7410 if (write_barrier_method) {
7411 /* Already created */
7412 mono_free_method (res);
7414 /* double-checked locking */
7415 mono_memory_barrier ();
7416 write_barrier_method = res;
7418 mono_loader_unlock ();
7420 return write_barrier_method;
7424 mono_gc_get_description (void)
7426 return g_strdup ("sgen");
7430 mono_gc_set_desktop_mode (void)
7435 mono_gc_is_moving (void)
7441 mono_gc_is_disabled (void)
7447 mono_sgen_debug_printf (int level, const char *format, ...)
7451 if (level > gc_debug_level)
7454 va_start (ap, format);
7455 vfprintf (gc_debug_file, format, ap);
7460 mono_sgen_get_logfile (void)
7462 return gc_debug_file;
7466 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
7473 mono_sgen_get_nursery_clear_policy (void)
7475 return nursery_clear_policy;
7479 mono_sgen_get_array_fill_vtable (void)
7481 return array_fill_vtable;
7484 #endif /* HAVE_SGEN_GC */