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 "utils/mono-mmap.h"
215 #include "utils/mono-time.h"
216 #include "utils/mono-semaphore.h"
217 #include "utils/mono-counters.h"
218 #include "utils/mono-proclib.h"
220 #include <mono/utils/memcheck.h>
222 #if defined(__MACH__)
223 #include "utils/mach-support.h"
226 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
230 #include "mono/cil/opcode.def"
236 #undef pthread_create
238 #undef pthread_detach
241 * ######################################################################
242 * ######## Types and constants used by the GC.
243 * ######################################################################
246 static int gc_initialized = 0;
247 /* If set, do a minor collection before every X allocation */
248 static guint32 collect_before_allocs = 0;
249 /* If set, do a heap consistency check before each minor collection */
250 static gboolean consistency_check_at_minor_collection = FALSE;
251 /* If set, check that there are no references to the domain left at domain unload */
252 static gboolean xdomain_checks = FALSE;
253 /* If not null, dump the heap after each collection into this file */
254 static FILE *heap_dump_file = NULL;
255 /* If set, mark stacks conservatively, even if precise marking is possible */
256 static gboolean conservative_stack_mark = FALSE;
257 /* If set, do a plausibility check on the scan_starts before and after
259 static gboolean do_scan_starts_check = FALSE;
260 static gboolean disable_minor_collections = FALSE;
261 static gboolean disable_major_collections = FALSE;
263 #ifdef HEAVY_STATISTICS
264 static long long stat_objects_alloced = 0;
265 static long long stat_bytes_alloced = 0;
266 long long stat_objects_alloced_degraded = 0;
267 long long stat_bytes_alloced_degraded = 0;
268 static long long stat_bytes_alloced_los = 0;
270 long long stat_copy_object_called_nursery = 0;
271 long long stat_objects_copied_nursery = 0;
272 long long stat_copy_object_called_major = 0;
273 long long stat_objects_copied_major = 0;
275 long long stat_scan_object_called_nursery = 0;
276 long long stat_scan_object_called_major = 0;
278 long long stat_nursery_copy_object_failed_from_space = 0;
279 long long stat_nursery_copy_object_failed_forwarded = 0;
280 long long stat_nursery_copy_object_failed_pinned = 0;
282 static long long stat_store_remsets = 0;
283 static long long stat_store_remsets_unique = 0;
284 static long long stat_saved_remsets_1 = 0;
285 static long long stat_saved_remsets_2 = 0;
286 static long long stat_local_remsets_processed = 0;
287 static long long stat_global_remsets_added = 0;
288 static long long stat_global_remsets_readded = 0;
289 static long long stat_global_remsets_processed = 0;
290 static long long stat_global_remsets_discarded = 0;
292 static long long stat_wasted_fragments_used = 0;
293 static long long stat_wasted_fragments_bytes = 0;
295 static int stat_wbarrier_set_field = 0;
296 static int stat_wbarrier_set_arrayref = 0;
297 static int stat_wbarrier_arrayref_copy = 0;
298 static int stat_wbarrier_generic_store = 0;
299 static int stat_wbarrier_generic_store_remset = 0;
300 static int stat_wbarrier_set_root = 0;
301 static int stat_wbarrier_value_copy = 0;
302 static int stat_wbarrier_object_copy = 0;
305 static long long stat_pinned_objects = 0;
307 static long long time_minor_pre_collection_fragment_clear = 0;
308 static long long time_minor_pinning = 0;
309 static long long time_minor_scan_remsets = 0;
310 static long long time_minor_scan_card_table = 0;
311 static long long time_minor_scan_pinned = 0;
312 static long long time_minor_scan_registered_roots = 0;
313 static long long time_minor_scan_thread_data = 0;
314 static long long time_minor_finish_gray_stack = 0;
315 static long long time_minor_fragment_creation = 0;
317 static long long time_major_pre_collection_fragment_clear = 0;
318 static long long time_major_pinning = 0;
319 static long long time_major_scan_pinned = 0;
320 static long long time_major_scan_registered_roots = 0;
321 static long long time_major_scan_thread_data = 0;
322 static long long time_major_scan_alloc_pinned = 0;
323 static long long time_major_scan_finalized = 0;
324 static long long time_major_scan_big_objects = 0;
325 static long long time_major_finish_gray_stack = 0;
326 static long long time_major_free_bigobjs = 0;
327 static long long time_major_los_sweep = 0;
328 static long long time_major_sweep = 0;
329 static long long time_major_fragment_creation = 0;
331 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
333 int gc_debug_level = 0;
338 mono_gc_flush_info (void)
340 fflush (gc_debug_file);
345 * Define this to allow the user to change the nursery size by
346 * specifying its value in the MONO_GC_PARAMS environmental
347 * variable. See mono_gc_base_init for details.
349 #define USER_CONFIG 1
351 #define TV_DECLARE SGEN_TV_DECLARE
352 #define TV_GETTIME SGEN_TV_GETTIME
353 #define TV_ELAPSED SGEN_TV_ELAPSED
354 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
356 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
358 /* The method used to clear the nursery */
359 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
360 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
365 CLEAR_AT_TLAB_CREATION
366 } NurseryClearPolicy;
368 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
371 * The young generation is divided into fragments. This is because
372 * we can hand one fragments to a thread for lock-less fast alloc and
373 * because the young generation ends up fragmented anyway by pinned objects.
374 * Once a collection is done, a list of fragments is created. When doing
375 * thread local alloc we use smallish nurseries so we allow new threads to
376 * allocate memory from gen0 without triggering a collection. Threads that
377 * are found to allocate lots of memory are given bigger fragments. This
378 * should make the finalizer thread use little nursery memory after a while.
379 * We should start assigning threads very small fragments: if there are many
380 * threads the nursery will be full of reserved space that the threads may not
381 * use at all, slowing down allocation speed.
382 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
383 * Allocation Buffers (TLABs).
385 typedef struct _Fragment Fragment;
389 char *fragment_start;
390 char *fragment_limit; /* the current soft limit for allocation */
394 /* the runtime can register areas of memory as roots: we keep two lists of roots,
395 * a pinned root set for conservatively scanned roots and a normal one for
396 * precisely scanned roots (currently implemented as a single list).
398 typedef struct _RootRecord RootRecord;
407 * We're never actually using the first element. It's always set to
408 * NULL to simplify the elimination of consecutive duplicate
411 #define STORE_REMSET_BUFFER_SIZE 1024
413 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
414 struct _GenericStoreRememberedSet {
415 GenericStoreRememberedSet *next;
416 /* We need one entry less because the first entry of store
417 remset buffers is always a dummy and we don't copy it. */
418 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
421 /* we have 4 possible values in the low 2 bits */
423 REMSET_LOCATION, /* just a pointer to the exact location */
424 REMSET_RANGE, /* range of pointer fields */
425 REMSET_OBJECT, /* mark all the object for scanning */
426 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
427 REMSET_TYPE_MASK = 0x3
430 #ifdef HAVE_KW_THREAD
431 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
433 static pthread_key_t remembered_set_key;
434 static RememberedSet *global_remset;
435 static RememberedSet *freed_thread_remsets;
436 static GenericStoreRememberedSet *generic_store_remsets = NULL;
438 /*A two slots cache for recently inserted remsets */
439 static gpointer global_remset_cache [2];
441 /* FIXME: later choose a size that takes into account the RememberedSet struct
442 * and doesn't waste any alloc paddin space.
444 #define DEFAULT_REMSET_SIZE 1024
445 static RememberedSet* alloc_remset (int size, gpointer id);
446 static RememberedSet* alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id);
448 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
449 #define object_is_pinned SGEN_OBJECT_IS_PINNED
450 #define pin_object SGEN_PIN_OBJECT
451 #define unpin_object SGEN_UNPIN_OBJECT
453 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_end))
455 #define LOAD_VTABLE SGEN_LOAD_VTABLE
458 safe_name (void* obj)
460 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
461 return vt->klass->name;
464 #define safe_object_get_size mono_sgen_safe_object_get_size
467 mono_sgen_safe_name (void* obj)
469 return safe_name (obj);
473 * ######################################################################
474 * ######## Global data.
475 * ######################################################################
477 static LOCK_DECLARE (gc_mutex);
478 static int gc_disabled = 0;
479 static int num_minor_gcs = 0;
480 static int num_major_gcs = 0;
482 static gboolean use_cardtable;
486 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
487 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
488 static int default_nursery_size = (1 << 22);
489 #ifdef SGEN_ALIGN_NURSERY
490 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
491 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
492 static int default_nursery_bits = 22;
497 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
498 #ifdef SGEN_ALIGN_NURSERY
499 #define DEFAULT_NURSERY_BITS 22
504 #ifndef SGEN_ALIGN_NURSERY
505 #define DEFAULT_NURSERY_BITS -1
508 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
510 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
512 /* the minimum size of a fragment that we consider useful for allocation */
513 #define FRAGMENT_MIN_SIZE (512)
515 static mword pagesize = 4096;
516 static mword nursery_size;
517 static int degraded_mode = 0;
519 static mword total_alloc = 0;
520 /* use this to tune when to do a major/minor collection */
521 static mword memory_pressure = 0;
522 static mword minor_collection_allowance;
523 static int minor_collection_sections_alloced = 0;
525 static GCMemSection *nursery_section = NULL;
526 static mword lowest_heap_address = ~(mword)0;
527 static mword highest_heap_address = 0;
529 static LOCK_DECLARE (interruption_mutex);
530 static LOCK_DECLARE (global_remset_mutex);
531 static LOCK_DECLARE (pin_queue_mutex);
533 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
534 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
536 #define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
537 #define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
539 typedef struct _FinalizeEntry FinalizeEntry;
540 struct _FinalizeEntry {
545 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
546 struct _FinalizeEntryHashTable {
547 FinalizeEntry **table;
552 typedef struct _DisappearingLink DisappearingLink;
553 struct _DisappearingLink {
554 DisappearingLink *next;
558 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
559 struct _DisappearingLinkHashTable {
560 DisappearingLink **table;
565 typedef struct _EphemeronLinkNode EphemeronLinkNode;
567 struct _EphemeronLinkNode {
568 EphemeronLinkNode *next;
577 int current_collection_generation = -1;
580 * The link pointer is hidden by negating each bit. We use the lowest
581 * bit of the link (before negation) to store whether it needs
582 * resurrection tracking.
584 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
585 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
587 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
588 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
591 * The finalizable hash has the object as the key, the
592 * disappearing_link hash, has the link address as key.
594 static FinalizeEntryHashTable minor_finalizable_hash;
595 static FinalizeEntryHashTable major_finalizable_hash;
596 /* objects that are ready to be finalized */
597 static FinalizeEntry *fin_ready_list = NULL;
598 static FinalizeEntry *critical_fin_list = NULL;
600 static DisappearingLinkHashTable minor_disappearing_link_hash;
601 static DisappearingLinkHashTable major_disappearing_link_hash;
603 static EphemeronLinkNode *ephemeron_list;
605 static int num_ready_finalizers = 0;
606 static int no_finalize = 0;
609 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
610 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
611 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
615 /* registered roots: the key to the hash is the root start address */
617 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
619 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
620 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
621 static mword roots_size = 0; /* amount of memory in the root set */
622 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
624 #define GC_ROOT_NUM 32
627 void *objects [GC_ROOT_NUM];
628 int root_types [GC_ROOT_NUM];
629 uintptr_t extra_info [GC_ROOT_NUM];
633 notify_gc_roots (GCRootReport *report)
637 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
642 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
644 if (report->count == GC_ROOT_NUM)
645 notify_gc_roots (report);
646 report->objects [report->count] = object;
647 report->root_types [report->count] = rtype;
648 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
652 * The current allocation cursors
653 * We allocate objects in the nursery.
654 * The nursery is the area between nursery_start and nursery_end.
655 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
656 * from nursery fragments.
657 * tlab_next is the pointer to the space inside the TLAB where the next object will
659 * tlab_temp_end is the pointer to the end of the temporary space reserved for
660 * the allocation: it allows us to set the scan starts at reasonable intervals.
661 * tlab_real_end points to the end of the TLAB.
662 * nursery_frag_real_end points to the end of the currently used nursery fragment.
663 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
664 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
665 * At the next allocation, the area of the nursery where objects can be present is
666 * between MIN(nursery_first_pinned_start, first_fragment_start) and
667 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
669 static char *nursery_start = NULL;
670 static char *nursery_next = NULL;
671 static char *nursery_frag_real_end = NULL;
672 static char *nursery_end = NULL;
673 static char *nursery_last_pinned_end = NULL;
675 #ifdef HAVE_KW_THREAD
676 #define TLAB_ACCESS_INIT
677 #define TLAB_START tlab_start
678 #define TLAB_NEXT tlab_next
679 #define TLAB_TEMP_END tlab_temp_end
680 #define TLAB_REAL_END tlab_real_end
681 #define REMEMBERED_SET remembered_set
682 #define STORE_REMSET_BUFFER store_remset_buffer
683 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
684 #define IN_CRITICAL_REGION thread_info->in_critical_region
686 static pthread_key_t thread_info_key;
687 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
688 #define TLAB_START (__thread_info__->tlab_start)
689 #define TLAB_NEXT (__thread_info__->tlab_next)
690 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
691 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
692 #define REMEMBERED_SET (__thread_info__->remset)
693 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
694 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
695 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
698 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
699 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
700 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
703 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
704 * variables for next+temp_end ?
706 #ifdef HAVE_KW_THREAD
707 static __thread SgenThreadInfo *thread_info;
708 static __thread char *tlab_start;
709 static __thread char *tlab_next;
710 static __thread char *tlab_temp_end;
711 static __thread char *tlab_real_end;
712 static __thread gpointer *store_remset_buffer;
713 static __thread long store_remset_buffer_index;
714 /* Used by the managed allocator/wbarrier */
715 static __thread char **tlab_next_addr;
716 static __thread char *stack_end;
717 static __thread long *store_remset_buffer_index_addr;
720 /* The size of a TLAB */
721 /* The bigger the value, the less often we have to go to the slow path to allocate a new
722 * one, but the more space is wasted by threads not allocating much memory.
724 * FIXME: Make this self-tuning for each thread.
726 static guint32 tlab_size = (1024 * 4);
728 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
729 #define MAX_NURSERY_TLAB_WASTE 512
731 /* fragments that are free and ready to be used for allocation */
732 static Fragment *nursery_fragments = NULL;
733 /* freeelist of fragment structures */
734 static Fragment *fragment_freelist = NULL;
736 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
738 /* Functions supplied by the runtime to be called by the GC */
739 static MonoGCCallbacks gc_callbacks;
741 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
742 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
744 #define ALIGN_UP SGEN_ALIGN_UP
746 #define MOVED_OBJECTS_NUM 64
747 static void *moved_objects [MOVED_OBJECTS_NUM];
748 static int moved_objects_idx = 0;
750 /* Vtable of the objects used to fill out nursery fragments before a collection */
751 static MonoVTable *array_fill_vtable;
753 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
754 pthread_t main_gc_thread = NULL;
758 * ######################################################################
759 * ######## Heap size accounting
760 * ######################################################################
763 static mword max_heap_size = ((mword)0)- ((mword)1);
764 static mword allocated_heap;
766 /*Object was pinned during the current collection*/
767 static mword objects_pinned;
770 mono_sgen_release_space (mword size, int space)
772 allocated_heap -= size;
776 available_free_space (void)
778 return max_heap_size - MIN (allocated_heap, max_heap_size);
782 mono_sgen_try_alloc_space (mword size, int space)
784 if (available_free_space () < size)
787 allocated_heap += size;
792 init_heap_size_limits (glong max_heap)
797 if (max_heap < nursery_size * 4) {
798 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
801 max_heap_size = max_heap - nursery_size;
805 * ######################################################################
806 * ######## Macros and function declarations.
807 * ######################################################################
811 align_pointer (void *ptr)
813 mword p = (mword)ptr;
814 p += sizeof (gpointer) - 1;
815 p &= ~ (sizeof (gpointer) - 1);
819 typedef SgenGrayQueue GrayQueue;
821 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
822 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
824 /* forward declarations */
825 static int stop_world (int generation);
826 static int restart_world (int generation);
827 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
828 static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
829 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
830 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
831 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
832 static void report_finalizer_roots (void);
833 static void report_registered_roots (void);
834 static void find_pinning_ref_from_thread (char *obj, size_t size);
835 static void update_current_thread_stack (void *start);
836 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
837 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
838 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
839 static void null_links_for_domain (MonoDomain *domain, int generation);
840 static gboolean alloc_fragment_for_size (size_t size);
841 static int alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size);
842 static void clear_nursery_fragments (char *next);
843 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
844 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
845 static void optimize_pin_queue (int start_slot);
846 static void clear_remsets (void);
847 static void clear_tlabs (void);
848 static void sort_addresses (void **array, int size);
849 static gboolean drain_gray_stack (GrayQueue *queue, int max_objs);
850 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
851 static gboolean need_major_collection (mword space_needed);
852 static void major_collection (const char *reason);
854 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
856 void describe_ptr (char *ptr);
857 void check_object (char *start);
859 static void check_consistency (void);
860 static void check_major_refs (void);
861 static void check_scan_starts (void);
862 static void check_for_xdomain_refs (void);
863 static void dump_heap (const char *type, int num, const char *reason);
865 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
867 static void init_stats (void);
869 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
870 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
871 static void null_ephemerons_for_domain (MonoDomain *domain);
873 SgenMajorCollector major_collector;
875 #include "sgen-pinning.c"
876 #include "sgen-pinning-stats.c"
877 #include "sgen-gray.c"
878 #include "sgen-workers.c"
879 #include "sgen-cardtable.c"
881 /* Root bitmap descriptors are simpler: the lower three bits describe the type
882 * and we either have 30/62 bitmap bits or nibble-based run-length,
883 * or a complex descriptor, or a user defined marker function.
886 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
891 ROOT_DESC_TYPE_MASK = 0x7,
892 ROOT_DESC_TYPE_SHIFT = 3,
895 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
897 #define MAX_USER_DESCRIPTORS 16
899 static gsize* complex_descriptors = NULL;
900 static int complex_descriptors_size = 0;
901 static int complex_descriptors_next = 0;
902 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
903 static int user_descriptors_next = 0;
906 alloc_complex_descriptor (gsize *bitmap, int numbits)
910 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
911 nwords = numbits / GC_BITS_PER_WORD + 1;
914 res = complex_descriptors_next;
915 /* linear search, so we don't have duplicates with domain load/unload
916 * this should not be performance critical or we'd have bigger issues
917 * (the number and size of complex descriptors should be small).
919 for (i = 0; i < complex_descriptors_next; ) {
920 if (complex_descriptors [i] == nwords) {
922 for (j = 0; j < nwords - 1; ++j) {
923 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
933 i += complex_descriptors [i];
935 if (complex_descriptors_next + nwords > complex_descriptors_size) {
936 int new_size = complex_descriptors_size * 2 + nwords;
937 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
938 complex_descriptors_size = new_size;
940 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
941 complex_descriptors_next += nwords;
942 complex_descriptors [res] = nwords;
943 for (i = 0; i < nwords - 1; ++i) {
944 complex_descriptors [res + 1 + i] = bitmap [i];
945 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
952 mono_sgen_get_complex_descriptor (GCVTable *vt)
954 return complex_descriptors + (vt->desc >> LOW_TYPE_BITS);
958 * Descriptor builders.
961 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
963 return (void*) DESC_TYPE_RUN_LENGTH;
967 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
969 int first_set = -1, num_set = 0, last_set = -1, i;
971 size_t stored_size = obj_size;
972 for (i = 0; i < numbits; ++i) {
973 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
981 * We don't encode the size of types that don't contain
982 * references because they might not be aligned, i.e. the
983 * bottom two bits might be set, which would clash with the
984 * bits we need to encode the descriptor type. Since we don't
985 * use the encoded size to skip objects, other than for
986 * processing remsets, in which case only the positions of
987 * references are relevant, this is not a problem.
990 return (void*)DESC_TYPE_RUN_LENGTH;
991 g_assert (!(stored_size & 0x3));
992 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
993 /* check run-length encoding first: one byte offset, one byte number of pointers
994 * on 64 bit archs, we can have 3 runs, just one on 32.
995 * It may be better to use nibbles.
998 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
999 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1000 return (void*) desc;
1001 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1002 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
1003 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));
1004 return (void*) desc;
1006 /* we know the 2-word header is ptr-free */
1007 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1008 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1009 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1010 return (void*) desc;
1013 /* we know the 2-word header is ptr-free */
1014 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1015 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1016 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1017 return (void*) desc;
1019 /* it's a complex object ... */
1020 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1021 return (void*) desc;
1024 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1026 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1028 int first_set = -1, num_set = 0, last_set = -1, i;
1029 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1030 for (i = 0; i < numbits; ++i) {
1031 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1038 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
1040 return (void*)DESC_TYPE_RUN_LENGTH;
1041 if (elem_size <= MAX_ELEMENT_SIZE) {
1042 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1044 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1046 /* Note: we also handle structs with just ref fields */
1047 if (num_set * sizeof (gpointer) == elem_size) {
1048 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1050 /* FIXME: try run-len first */
1051 /* Note: we can't skip the object header here, because it's not present */
1052 if (last_set <= SMALL_BITMAP_SIZE) {
1053 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1056 /* it's am array of complex structs ... */
1057 desc = DESC_TYPE_COMPLEX_ARR;
1058 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1059 return (void*) desc;
1062 /* Return the bitmap encoded by a descriptor */
1064 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1066 mword d = (mword)descr;
1070 case DESC_TYPE_RUN_LENGTH: {
1071 int first_set = (d >> 16) & 0xff;
1072 int num_set = (d >> 24) & 0xff;
1075 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1077 for (i = first_set; i < first_set + num_set; ++i)
1078 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1080 *numbits = first_set + num_set;
1084 case DESC_TYPE_SMALL_BITMAP:
1085 bitmap = g_new0 (gsize, 1);
1087 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1089 *numbits = GC_BITS_PER_WORD;
1093 g_assert_not_reached ();
1098 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1100 MonoObject *o = (MonoObject*)(obj);
1101 MonoObject *ref = (MonoObject*)*(ptr);
1102 int offset = (char*)(ptr) - (char*)o;
1104 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1106 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1108 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1109 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1111 /* Thread.cached_culture_info */
1112 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1113 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1114 !strcmp(o->vtable->klass->name_space, "System") &&
1115 !strcmp(o->vtable->klass->name, "Object[]"))
1118 * 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
1119 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1120 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1121 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1122 * 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
1123 * 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
1124 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1125 * 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
1126 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1128 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1129 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1130 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1131 !strcmp (o->vtable->klass->name, "MemoryStream"))
1133 /* append_job() in threadpool.c */
1134 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1135 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1136 !strcmp (o->vtable->klass->name_space, "System") &&
1137 !strcmp (o->vtable->klass->name, "Object[]") &&
1138 mono_thread_pool_is_queue_array ((MonoArray*) o))
1144 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1146 MonoObject *o = (MonoObject*)(obj);
1147 MonoObject *ref = (MonoObject*)*(ptr);
1148 int offset = (char*)(ptr) - (char*)o;
1150 MonoClassField *field;
1153 if (!ref || ref->vtable->domain == domain)
1155 if (is_xdomain_ref_allowed (ptr, obj, domain))
1159 for (class = o->vtable->klass; class; class = class->parent) {
1162 for (i = 0; i < class->field.count; ++i) {
1163 if (class->fields[i].offset == offset) {
1164 field = &class->fields[i];
1172 if (ref->vtable->klass == mono_defaults.string_class)
1173 str = mono_string_to_utf8 ((MonoString*)ref);
1176 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1177 o, o->vtable->klass->name_space, o->vtable->klass->name,
1178 offset, field ? field->name : "",
1179 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1180 mono_gc_scan_for_specific_ref (o, TRUE);
1186 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1189 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1191 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1193 #include "sgen-scan-object.h"
1196 static gboolean scan_object_for_specific_ref_precise = TRUE;
1199 #define HANDLE_PTR(ptr,obj) do { \
1200 if ((MonoObject*)*(ptr) == key) { \
1201 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1202 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1207 scan_object_for_specific_ref (char *start, MonoObject *key)
1211 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1214 if (scan_object_for_specific_ref_precise) {
1215 #include "sgen-scan-object.h"
1217 mword *words = (mword*)start;
1218 size_t size = safe_object_get_size ((MonoObject*)start);
1220 for (i = 0; i < size / sizeof (mword); ++i) {
1221 if (words [i] == (mword)key) {
1222 g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
1223 key, start, safe_name (start), i * sizeof (mword));
1230 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1232 while (start < end) {
1236 if (!*(void**)start) {
1237 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1242 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1248 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1250 callback (obj, size, data);
1257 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1259 scan_object_for_specific_ref (obj, key);
1263 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1267 g_print ("found ref to %p in root record %p\n", key, root);
1270 static MonoObject *check_key = NULL;
1271 static RootRecord *check_root = NULL;
1274 check_root_obj_specific_ref_from_marker (void **obj)
1276 check_root_obj_specific_ref (check_root, check_key, *obj);
1280 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1285 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1286 for (root = roots_hash [root_type][i]; root; root = root->next) {
1287 void **start_root = (void**)root->start_root;
1288 mword desc = root->root_desc;
1292 switch (desc & ROOT_DESC_TYPE_MASK) {
1293 case ROOT_DESC_BITMAP:
1294 desc >>= ROOT_DESC_TYPE_SHIFT;
1297 check_root_obj_specific_ref (root, key, *start_root);
1302 case ROOT_DESC_COMPLEX: {
1303 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1304 int bwords = (*bitmap_data) - 1;
1305 void **start_run = start_root;
1307 while (bwords-- > 0) {
1308 gsize bmap = *bitmap_data++;
1309 void **objptr = start_run;
1312 check_root_obj_specific_ref (root, key, *objptr);
1316 start_run += GC_BITS_PER_WORD;
1320 case ROOT_DESC_USER: {
1321 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1322 marker (start_root, check_root_obj_specific_ref_from_marker);
1325 case ROOT_DESC_RUN_LEN:
1326 g_assert_not_reached ();
1328 g_assert_not_reached ();
1337 mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
1342 scan_object_for_specific_ref_precise = precise;
1344 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1345 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1347 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1349 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1351 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1352 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1354 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1355 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1356 void **ptr = (void**)root->start_root;
1358 while (ptr < (void**)root->end_root) {
1359 check_root_obj_specific_ref (root, *ptr, key);
1367 clear_current_nursery_fragment (char *next)
1369 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1370 g_assert (next <= nursery_frag_real_end);
1371 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", next, nursery_frag_real_end));
1372 memset (next, 0, nursery_frag_real_end - next);
1376 /* Clear all remaining nursery fragments */
1378 clear_nursery_fragments (char *next)
1381 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1382 clear_current_nursery_fragment (next);
1383 for (frag = nursery_fragments; frag; frag = frag->next) {
1384 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_start, frag->fragment_end));
1385 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1391 need_remove_object_for_domain (char *start, MonoDomain *domain)
1393 if (mono_object_domain (start) == domain) {
1394 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1395 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1402 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1404 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1405 if (vt->klass == mono_defaults.internal_thread_class)
1406 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1407 /* The object could be a proxy for an object in the domain
1409 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1410 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1412 /* The server could already have been zeroed out, so
1413 we need to check for that, too. */
1414 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1415 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1417 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1422 static MonoDomain *check_domain = NULL;
1425 check_obj_not_in_domain (void **o)
1427 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1431 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1435 check_domain = domain;
1436 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1437 for (root = roots_hash [root_type][i]; root; root = root->next) {
1438 void **start_root = (void**)root->start_root;
1439 mword desc = root->root_desc;
1441 /* The MonoDomain struct is allowed to hold
1442 references to objects in its own domain. */
1443 if (start_root == (void**)domain)
1446 switch (desc & ROOT_DESC_TYPE_MASK) {
1447 case ROOT_DESC_BITMAP:
1448 desc >>= ROOT_DESC_TYPE_SHIFT;
1450 if ((desc & 1) && *start_root)
1451 check_obj_not_in_domain (*start_root);
1456 case ROOT_DESC_COMPLEX: {
1457 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1458 int bwords = (*bitmap_data) - 1;
1459 void **start_run = start_root;
1461 while (bwords-- > 0) {
1462 gsize bmap = *bitmap_data++;
1463 void **objptr = start_run;
1465 if ((bmap & 1) && *objptr)
1466 check_obj_not_in_domain (*objptr);
1470 start_run += GC_BITS_PER_WORD;
1474 case ROOT_DESC_USER: {
1475 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1476 marker (start_root, check_obj_not_in_domain);
1479 case ROOT_DESC_RUN_LEN:
1480 g_assert_not_reached ();
1482 g_assert_not_reached ();
1486 check_domain = NULL;
1490 check_for_xdomain_refs (void)
1494 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1495 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1497 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1499 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1500 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1504 clear_domain_process_object (char *obj, MonoDomain *domain)
1508 process_object_for_domain_clearing (obj, domain);
1509 remove = need_remove_object_for_domain (obj, domain);
1511 if (remove && ((MonoObject*)obj)->synchronisation) {
1512 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1514 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1521 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1523 if (clear_domain_process_object (obj, domain))
1524 memset (obj, 0, size);
1528 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1530 clear_domain_process_object (obj, domain);
1534 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1536 if (need_remove_object_for_domain (obj, domain))
1537 major_collector.free_non_pinned_object (obj, size);
1541 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1543 if (need_remove_object_for_domain (obj, domain))
1544 major_collector.free_pinned_object (obj, size);
1548 * When appdomains are unloaded we can easily remove objects that have finalizers,
1549 * but all the others could still be present in random places on the heap.
1550 * We need a sweep to get rid of them even though it's going to be costly
1552 * The reason we need to remove them is because we access the vtable and class
1553 * structures to know the object size and the reference bitmap: once the domain is
1554 * unloaded the point to random memory.
1557 mono_gc_clear_domain (MonoDomain * domain)
1559 LOSObject *bigobj, *prev;
1564 clear_nursery_fragments (nursery_next);
1566 if (xdomain_checks && domain != mono_get_root_domain ()) {
1567 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1568 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1569 check_for_xdomain_refs ();
1572 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1573 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1575 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1576 to memory returned to the OS.*/
1577 null_ephemerons_for_domain (domain);
1579 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1580 null_links_for_domain (domain, i);
1582 /* We need two passes over major and large objects because
1583 freeing such objects might give their memory back to the OS
1584 (in the case of large objects) or obliterate its vtable
1585 (pinned objects with major-copying or pinned and non-pinned
1586 objects with major-mark&sweep), but we might need to
1587 dereference a pointer from an object to another object if
1588 the first object is a proxy. */
1589 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1590 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1591 clear_domain_process_object (bigobj->data, domain);
1594 for (bigobj = los_object_list; bigobj;) {
1595 if (need_remove_object_for_domain (bigobj->data, domain)) {
1596 LOSObject *to_free = bigobj;
1598 prev->next = bigobj->next;
1600 los_object_list = bigobj->next;
1601 bigobj = bigobj->next;
1602 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1604 mono_sgen_los_free_object (to_free);
1608 bigobj = bigobj->next;
1610 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1611 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1617 global_remset_cache_clear (void)
1619 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1623 * Tries to check if a given remset location was already added to the global remset.
1626 * A 2 entry, LRU cache of recently saw location remsets.
1628 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1630 * Returns TRUE is the element was added..
1633 global_remset_location_was_not_added (gpointer ptr)
1636 gpointer first = global_remset_cache [0], second;
1638 HEAVY_STAT (++stat_global_remsets_discarded);
1642 second = global_remset_cache [1];
1644 if (second == ptr) {
1645 /*Move the second to the front*/
1646 global_remset_cache [0] = second;
1647 global_remset_cache [1] = first;
1649 HEAVY_STAT (++stat_global_remsets_discarded);
1653 global_remset_cache [0] = second;
1654 global_remset_cache [1] = ptr;
1659 * mono_sgen_add_to_global_remset:
1661 * The global remset contains locations which point into newspace after
1662 * a minor collection. This can happen if the objects they point to are pinned.
1664 * LOCKING: If called from a parallel collector, the global remset
1665 * lock must be held. For serial collectors that is not necessary.
1668 mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr)
1671 gboolean lock = major_collector.is_parallel;
1673 if (use_cardtable) {
1674 sgen_card_table_mark_address ((mword)ptr);
1678 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1683 if (!global_remset_location_was_not_added (ptr))
1686 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1687 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1689 HEAVY_STAT (++stat_global_remsets_added);
1692 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1693 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1695 if (global_remset->store_next + 3 < global_remset->end_set) {
1696 *(global_remset->store_next++) = (mword)ptr;
1699 rs = alloc_global_remset (alc, global_remset->end_set - global_remset->data, NULL);
1700 rs->next = global_remset;
1702 *(global_remset->store_next++) = (mword)ptr;
1705 int global_rs_size = 0;
1707 for (rs = global_remset; rs; rs = rs->next) {
1708 global_rs_size += rs->store_next - rs->data;
1710 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1715 UNLOCK_GLOBAL_REMSET;
1721 * Scan objects in the gray stack until the stack is empty. This should be called
1722 * frequently after each object is copied, to achieve better locality and cache
1726 drain_gray_stack (GrayQueue *queue, int max_objs)
1730 if (current_collection_generation == GENERATION_NURSERY) {
1732 GRAY_OBJECT_DEQUEUE (queue, obj);
1735 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1736 major_collector.minor_scan_object (obj, queue);
1741 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1745 for (i = 0; i != max_objs; ++i) {
1746 GRAY_OBJECT_DEQUEUE (queue, obj);
1749 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1750 major_collector.major_scan_object (obj, queue);
1752 } while (max_objs < 0);
1758 * Addresses from start to end are already sorted. This function finds
1759 * the object header for each address and pins the object. The
1760 * addresses must be inside the passed section. The (start of the)
1761 * address array is overwritten with the addresses of the actually
1762 * pinned objects. Return the number of pinned objects.
1765 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1770 void *last_obj = NULL;
1771 size_t last_obj_size = 0;
1774 void **definitely_pinned = start;
1778 * The code below starts the search from an entry in scan_starts, which might point into a nursery
1779 * fragment containing random data. Clearing the nursery fragments takes a lot of time, and searching
1780 * though them too, so lay arrays at each location inside a fragment where a search can start:
1781 * - scan_locations[i]
1783 * - the start of each fragment (the last_obj + last_obj case)
1784 * The third encompasses the first two, since scan_locations [i] can't point inside a nursery fragment.
1786 for (frag = nursery_fragments; frag; frag = frag->next) {
1789 g_assert (frag->fragment_end - frag->fragment_start >= sizeof (MonoArray));
1790 o = (MonoArray*)frag->fragment_start;
1791 memset (o, 0, sizeof (MonoArray));
1792 g_assert (array_fill_vtable);
1793 o->obj.vtable = array_fill_vtable;
1794 /* Mark this as not a real object */
1795 o->obj.synchronisation = GINT_TO_POINTER (-1);
1796 o->max_length = (frag->fragment_end - frag->fragment_start) - sizeof (MonoArray);
1797 g_assert (frag->fragment_start + safe_object_get_size ((MonoObject*)o) == frag->fragment_end);
1800 while (start < end) {
1802 /* the range check should be reduntant */
1803 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1804 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1805 /* multiple pointers to the same object */
1806 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1810 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1811 g_assert (idx < section->num_scan_start);
1812 search_start = (void*)section->scan_starts [idx];
1813 if (!search_start || search_start > addr) {
1816 search_start = section->scan_starts [idx];
1817 if (search_start && search_start <= addr)
1820 if (!search_start || search_start > addr)
1821 search_start = start_nursery;
1823 if (search_start < last_obj)
1824 search_start = (char*)last_obj + last_obj_size;
1825 /* now addr should be in an object a short distance from search_start
1826 * Note that search_start must point to zeroed mem or point to an object.
1830 if (!*(void**)search_start) {
1831 /* Consistency check */
1833 for (frag = nursery_fragments; frag; frag = frag->next) {
1834 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1835 g_assert_not_reached ();
1839 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1842 last_obj = search_start;
1843 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1845 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1846 /* Marks the beginning of a nursery fragment, skip */
1848 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1849 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1850 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));
1851 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1852 pin_object (search_start);
1853 GRAY_OBJECT_ENQUEUE (queue, search_start);
1855 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1856 definitely_pinned [count] = search_start;
1861 /* skip to the next object */
1862 search_start = (void*)((char*)search_start + last_obj_size);
1863 } while (search_start <= addr);
1864 /* we either pinned the correct object or we ignored the addr because
1865 * it points to unused zeroed memory.
1871 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1872 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1873 GCRootReport report;
1875 for (idx = 0; idx < count; ++idx)
1876 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
1877 notify_gc_roots (&report);
1879 stat_pinned_objects += count;
1884 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1886 int num_entries = section->pin_queue_num_entries;
1888 void **start = section->pin_queue_start;
1890 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1891 section->data, section->next_data, queue);
1892 section->pin_queue_num_entries = reduced_to;
1894 section->pin_queue_start = NULL;
1900 mono_sgen_pin_object (void *object, GrayQueue *queue)
1902 if (major_collector.is_parallel) {
1904 /*object arrives pinned*/
1905 pin_stage_ptr (object);
1909 SGEN_PIN_OBJECT (object);
1910 pin_stage_ptr (object);
1913 GRAY_OBJECT_ENQUEUE (queue, object);
1916 /* Sort the addresses in array in increasing order.
1917 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1920 sort_addresses (void **array, int size)
1925 for (i = 1; i < size; ++i) {
1928 int parent = (child - 1) / 2;
1930 if (array [parent] >= array [child])
1933 tmp = array [parent];
1934 array [parent] = array [child];
1935 array [child] = tmp;
1941 for (i = size - 1; i > 0; --i) {
1944 array [i] = array [0];
1950 while (root * 2 + 1 <= end) {
1951 int child = root * 2 + 1;
1953 if (child < end && array [child] < array [child + 1])
1955 if (array [root] >= array [child])
1959 array [root] = array [child];
1960 array [child] = tmp;
1967 static G_GNUC_UNUSED void
1968 print_nursery_gaps (void* start_nursery, void *end_nursery)
1971 gpointer first = start_nursery;
1973 for (i = 0; i < next_pin_slot; ++i) {
1974 next = pin_queue [i];
1975 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1979 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1982 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1984 optimize_pin_queue (int start_slot)
1986 void **start, **cur, **end;
1987 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1988 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1989 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1990 if ((next_pin_slot - start_slot) > 1)
1991 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1992 start = cur = pin_queue + start_slot;
1993 end = pin_queue + next_pin_slot;
1996 while (*start == *cur && cur < end)
2000 next_pin_slot = start - pin_queue;
2001 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2002 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2007 * Scan the memory between start and end and queue values which could be pointers
2008 * to the area between start_nursery and end_nursery for later consideration.
2009 * Typically used for thread stacks.
2012 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2015 while (start < end) {
2016 if (*start >= start_nursery && *start < end_nursery) {
2018 * *start can point to the middle of an object
2019 * note: should we handle pointing at the end of an object?
2020 * pinning in C# code disallows pointing at the end of an object
2021 * but there is some small chance that an optimizing C compiler
2022 * may keep the only reference to an object by pointing
2023 * at the end of it. We ignore this small chance for now.
2024 * Pointers to the end of an object are indistinguishable
2025 * from pointers to the start of the next object in memory
2026 * so if we allow that we'd need to pin two objects...
2027 * We queue the pointer in an array, the
2028 * array will then be sorted and uniqued. This way
2029 * we can coalesce several pinning pointers and it should
2030 * be faster since we'd do a memory scan with increasing
2031 * addresses. Note: we can align the address to the allocation
2032 * alignment, so the unique process is more effective.
2034 mword addr = (mword)*start;
2035 addr &= ~(ALLOC_ALIGN - 1);
2036 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2037 pin_stage_ptr ((void*)addr);
2039 pin_stats_register_address ((char*)addr, pin_type);
2040 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
2045 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2049 * Debugging function: find in the conservative roots where @obj is being pinned.
2051 static G_GNUC_UNUSED void
2052 find_pinning_reference (char *obj, size_t size)
2056 char *endobj = obj + size;
2057 for (i = 0; i < roots_hash_size [0]; ++i) {
2058 for (root = roots_hash [0][i]; root; root = root->next) {
2059 /* if desc is non-null it has precise info */
2060 if (!root->root_desc) {
2061 char ** start = (char**)root->start_root;
2062 while (start < (char**)root->end_root) {
2063 if (*start >= obj && *start < endobj) {
2064 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));
2071 find_pinning_ref_from_thread (obj, size);
2075 * The first thing we do in a collection is to identify pinned objects.
2076 * This function considers all the areas of memory that need to be
2077 * conservatively scanned.
2080 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
2084 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]));
2085 /* objects pinned from the API are inside these roots */
2086 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2087 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2088 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2089 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2092 /* now deal with the thread stacks
2093 * in the future we should be able to conservatively scan only:
2094 * *) the cpu registers
2095 * *) the unmanaged stack frames
2096 * *) the _last_ managed stack frame
2097 * *) pointers slots in managed frames
2099 scan_thread_data (start_nursery, end_nursery, FALSE, queue);
2101 evacuate_pin_staging_area ();
2105 CopyOrMarkObjectFunc func;
2107 } UserCopyOrMarkData;
2109 static pthread_key_t user_copy_or_mark_key;
2112 init_user_copy_or_mark_key (void)
2114 pthread_key_create (&user_copy_or_mark_key, NULL);
2118 set_user_copy_or_mark_data (UserCopyOrMarkData *data)
2120 static pthread_once_t init_control = PTHREAD_ONCE_INIT;
2121 pthread_once (&init_control, init_user_copy_or_mark_key);
2123 pthread_setspecific (user_copy_or_mark_key, data);
2127 single_arg_user_copy_or_mark (void **obj)
2129 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
2131 data->func (obj, data->queue);
2135 * The memory area from start_root to end_root contains pointers to objects.
2136 * Their position is precisely described by @desc (this means that the pointer
2137 * can be either NULL or the pointer to the start of an object).
2138 * This functions copies them to to_space updates them.
2140 * This function is not thread-safe!
2143 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2145 switch (desc & ROOT_DESC_TYPE_MASK) {
2146 case ROOT_DESC_BITMAP:
2147 desc >>= ROOT_DESC_TYPE_SHIFT;
2149 if ((desc & 1) && *start_root) {
2150 copy_func (start_root, queue);
2151 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2152 drain_gray_stack (queue, -1);
2158 case ROOT_DESC_COMPLEX: {
2159 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2160 int bwords = (*bitmap_data) - 1;
2161 void **start_run = start_root;
2163 while (bwords-- > 0) {
2164 gsize bmap = *bitmap_data++;
2165 void **objptr = start_run;
2167 if ((bmap & 1) && *objptr) {
2168 copy_func (objptr, queue);
2169 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2170 drain_gray_stack (queue, -1);
2175 start_run += GC_BITS_PER_WORD;
2179 case ROOT_DESC_USER: {
2180 UserCopyOrMarkData data = { copy_func, queue };
2181 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2182 set_user_copy_or_mark_data (&data);
2183 marker (start_root, single_arg_user_copy_or_mark);
2184 set_user_copy_or_mark_data (NULL);
2187 case ROOT_DESC_RUN_LEN:
2188 g_assert_not_reached ();
2190 g_assert_not_reached ();
2195 reset_heap_boundaries (void)
2197 lowest_heap_address = ~(mword)0;
2198 highest_heap_address = 0;
2202 mono_sgen_update_heap_boundaries (mword low, mword high)
2207 old = lowest_heap_address;
2210 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2213 old = highest_heap_address;
2216 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2220 alloc_fragment (void)
2222 Fragment *frag = fragment_freelist;
2224 fragment_freelist = frag->next;
2228 frag = mono_sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
2234 add_fragment (char *start, char *end)
2238 fragment = alloc_fragment ();
2239 fragment->fragment_start = start;
2240 fragment->fragment_limit = start;
2241 fragment->fragment_end = end;
2242 fragment->next = nursery_fragments;
2243 nursery_fragments = fragment;
2246 /* size must be a power of 2 */
2248 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2250 /* Allocate twice the memory to be able to put the block on an aligned address */
2251 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2256 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2257 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2260 mono_sgen_free_os_memory (mem, aligned - mem);
2261 if (aligned + size < mem + size + alignment)
2262 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2268 * Allocate and setup the data structures needed to be able to allocate objects
2269 * in the nursery. The nursery is stored in nursery_section.
2272 alloc_nursery (void)
2274 GCMemSection *section;
2279 if (nursery_section)
2281 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2282 /* later we will alloc a larger area for the nursery but only activate
2283 * what we need. The rest will be used as expansion if we have too many pinned
2284 * objects in the existing nursery.
2286 /* FIXME: handle OOM */
2287 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2289 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2290 alloc_size = nursery_size;
2291 #ifdef SGEN_ALIGN_NURSERY
2292 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2294 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2296 nursery_start = data;
2297 nursery_end = nursery_start + nursery_size;
2298 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
2299 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));
2300 section->data = section->next_data = data;
2301 section->size = alloc_size;
2302 section->end_data = nursery_end;
2303 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2304 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2305 section->num_scan_start = scan_starts;
2306 section->block.role = MEMORY_ROLE_GEN0;
2307 section->block.next = NULL;
2309 nursery_section = section;
2311 /* Setup the single first large fragment */
2312 add_fragment (nursery_start, nursery_end);
2316 mono_gc_get_nursery (int *shift_bits, size_t *size)
2318 *size = nursery_size;
2319 #ifdef SGEN_ALIGN_NURSERY
2320 *shift_bits = DEFAULT_NURSERY_BITS;
2324 return nursery_start;
2328 mono_gc_precise_stack_mark_enabled (void)
2330 return !conservative_stack_mark;
2334 mono_gc_get_logfile (void)
2336 return mono_sgen_get_logfile ();
2340 report_finalizer_roots_list (FinalizeEntry *list)
2342 GCRootReport report;
2346 for (fin = list; fin; fin = fin->next) {
2349 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2351 notify_gc_roots (&report);
2355 report_finalizer_roots (void)
2357 report_finalizer_roots_list (fin_ready_list);
2358 report_finalizer_roots_list (critical_fin_list);
2361 static GCRootReport *root_report;
2364 single_arg_report_root (void **obj)
2367 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2371 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2373 switch (desc & ROOT_DESC_TYPE_MASK) {
2374 case ROOT_DESC_BITMAP:
2375 desc >>= ROOT_DESC_TYPE_SHIFT;
2377 if ((desc & 1) && *start_root) {
2378 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2384 case ROOT_DESC_COMPLEX: {
2385 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2386 int bwords = (*bitmap_data) - 1;
2387 void **start_run = start_root;
2389 while (bwords-- > 0) {
2390 gsize bmap = *bitmap_data++;
2391 void **objptr = start_run;
2393 if ((bmap & 1) && *objptr) {
2394 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2399 start_run += GC_BITS_PER_WORD;
2403 case ROOT_DESC_USER: {
2404 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2405 root_report = report;
2406 marker (start_root, single_arg_report_root);
2409 case ROOT_DESC_RUN_LEN:
2410 g_assert_not_reached ();
2412 g_assert_not_reached ();
2417 report_registered_roots_by_type (int root_type)
2419 GCRootReport report;
2423 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2424 for (root = roots_hash [root_type][i]; root; root = root->next) {
2425 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2426 precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
2429 notify_gc_roots (&report);
2433 report_registered_roots (void)
2435 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2436 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2440 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2444 for (fin = list; fin; fin = fin->next) {
2447 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2448 copy_func (&fin->object, queue);
2452 static mword fragment_total = 0;
2454 * We found a fragment of free memory in the nursery: memzero it and if
2455 * it is big enough, add it to the list of fragments that can be used for
2459 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2461 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2462 binary_protocol_empty (frag_start, frag_size);
2463 /* Not worth dealing with smaller fragments: need to tune */
2464 if (frag_size >= FRAGMENT_MIN_SIZE) {
2465 /* memsetting just the first chunk start is bound to provide better cache locality */
2466 if (nursery_clear_policy == CLEAR_AT_GC)
2467 memset (frag_start, 0, frag_size);
2469 add_fragment (frag_start, frag_end);
2470 fragment_total += frag_size;
2472 /* Clear unused fragments, pinning depends on this */
2473 /*TODO place an int[] here instead of the memset if size justify it*/
2474 memset (frag_start, 0, frag_size);
2479 generation_name (int generation)
2481 switch (generation) {
2482 case GENERATION_NURSERY: return "nursery";
2483 case GENERATION_OLD: return "old";
2484 default: g_assert_not_reached ();
2488 static DisappearingLinkHashTable*
2489 get_dislink_hash_table (int generation)
2491 switch (generation) {
2492 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2493 case GENERATION_OLD: return &major_disappearing_link_hash;
2494 default: g_assert_not_reached ();
2498 static FinalizeEntryHashTable*
2499 get_finalize_entry_hash_table (int generation)
2501 switch (generation) {
2502 case GENERATION_NURSERY: return &minor_finalizable_hash;
2503 case GENERATION_OLD: return &major_finalizable_hash;
2504 default: g_assert_not_reached ();
2508 static MonoObject **finalized_array = NULL;
2509 static int finalized_array_capacity = 0;
2510 static int finalized_array_entries = 0;
2513 bridge_register_finalized_object (MonoObject *object)
2515 if (!finalized_array)
2518 if (finalized_array_entries >= finalized_array_capacity) {
2519 MonoObject **new_array;
2520 g_assert (finalized_array_entries == finalized_array_capacity);
2521 finalized_array_capacity *= 2;
2522 new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2523 memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
2524 mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
2525 finalized_array = new_array;
2527 finalized_array [finalized_array_entries++] = object;
2531 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2536 int ephemeron_rounds = 0;
2538 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2541 * We copied all the reachable objects. Now it's the time to copy
2542 * the objects that were not referenced by the roots, but by the copied objects.
2543 * we built a stack of objects pointed to by gray_start: they are
2544 * additional roots and we may add more items as we go.
2545 * We loop until gray_start == gray_objects which means no more objects have
2546 * been added. Note this is iterative: no recursion is involved.
2547 * We need to walk the LO list as well in search of marked big objects
2548 * (use a flag since this is needed only on major collections). We need to loop
2549 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2550 * To achieve better cache locality and cache usage, we drain the gray stack
2551 * frequently, after each object is copied, and just finish the work here.
2553 drain_gray_stack (queue, -1);
2555 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2558 We must clear weak links that don't track resurrection before processing object ready for
2559 finalization so they can be cleared before that.
2561 null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
2562 if (generation == GENERATION_OLD)
2563 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
2565 if (finalized_array == NULL && mono_sgen_need_bridge_processing ()) {
2566 finalized_array_capacity = 32;
2567 finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2569 finalized_array_entries = 0;
2571 /* walk the finalization queue and move also the objects that need to be
2572 * finalized: use the finalized objects as new roots so the objects they depend
2573 * on are also not reclaimed. As with the roots above, only objects in the nursery
2574 * are marked/copied.
2575 * We need a loop here, since objects ready for finalizers may reference other objects
2576 * that are fin-ready. Speedup with a flag?
2581 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2582 * before processing finalizable objects to avoid finalizing reachable values.
2584 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2585 * while they are been finalized.
2587 int done_with_ephemerons = 0;
2589 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2590 drain_gray_stack (queue, -1);
2592 } while (!done_with_ephemerons);
2594 fin_ready = num_ready_finalizers;
2595 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2596 if (generation == GENERATION_OLD)
2597 finalize_in_range (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
2599 if (fin_ready != num_ready_finalizers) {
2601 if (finalized_array != NULL)
2602 mono_sgen_bridge_processing (finalized_array_entries, finalized_array);
2605 /* drain the new stack that might have been created */
2606 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2607 drain_gray_stack (queue, -1);
2608 } while (fin_ready != num_ready_finalizers);
2610 if (mono_sgen_need_bridge_processing ())
2611 g_assert (num_loops <= 1);
2614 * Clear ephemeron pairs with unreachable keys.
2615 * We pass the copy func so we can figure out if an array was promoted or not.
2617 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2620 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));
2623 * handle disappearing links
2624 * Note we do this after checking the finalization queue because if an object
2625 * survives (at least long enough to be finalized) we don't clear the link.
2626 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2627 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2630 g_assert (gray_object_queue_is_empty (queue));
2632 null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
2633 if (generation == GENERATION_OLD)
2634 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
2635 if (gray_object_queue_is_empty (queue))
2637 drain_gray_stack (queue, -1);
2640 g_assert (gray_object_queue_is_empty (queue));
2644 mono_sgen_check_section_scan_starts (GCMemSection *section)
2647 for (i = 0; i < section->num_scan_start; ++i) {
2648 if (section->scan_starts [i]) {
2649 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2650 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2656 check_scan_starts (void)
2658 if (!do_scan_starts_check)
2660 mono_sgen_check_section_scan_starts (nursery_section);
2661 major_collector.check_scan_starts ();
2664 static int last_num_pinned = 0;
2667 build_nursery_fragments (void **start, int num_entries)
2669 char *frag_start, *frag_end;
2673 while (nursery_fragments) {
2674 Fragment *next = nursery_fragments->next;
2675 nursery_fragments->next = fragment_freelist;
2676 fragment_freelist = nursery_fragments;
2677 nursery_fragments = next;
2679 frag_start = nursery_start;
2681 /* clear scan starts */
2682 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2683 for (i = 0; i < num_entries; ++i) {
2684 frag_end = start [i];
2685 /* remove the pin bit from pinned objects */
2686 unpin_object (frag_end);
2687 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2688 frag_size = frag_end - frag_start;
2690 add_nursery_frag (frag_size, frag_start, frag_end);
2691 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)start [i]));
2692 frag_start = (char*)start [i] + frag_size;
2694 nursery_last_pinned_end = frag_start;
2695 frag_end = nursery_end;
2696 frag_size = frag_end - frag_start;
2698 add_nursery_frag (frag_size, frag_start, frag_end);
2699 if (!nursery_fragments) {
2700 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
2701 for (i = 0; i < num_entries; ++i) {
2702 DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", start [i], safe_name (start [i]), safe_object_get_size (start [i])));
2707 nursery_next = nursery_frag_real_end = NULL;
2709 /* Clear TLABs for all threads */
2714 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2718 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2719 for (root = roots_hash [root_type][i]; root; root = root->next) {
2720 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2721 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2727 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2729 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2733 mono_sgen_dump_section (GCMemSection *section, const char *type)
2735 char *start = section->data;
2736 char *end = section->data + section->size;
2737 char *occ_start = NULL;
2739 char *old_start = NULL; /* just for debugging */
2741 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2743 while (start < end) {
2747 if (!*(void**)start) {
2749 mono_sgen_dump_occupied (occ_start, start, section->data);
2752 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2755 g_assert (start < section->next_data);
2760 vt = (GCVTable*)LOAD_VTABLE (start);
2763 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2766 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2767 start - section->data,
2768 vt->klass->name_space, vt->klass->name,
2776 mono_sgen_dump_occupied (occ_start, start, section->data);
2778 fprintf (heap_dump_file, "</section>\n");
2782 dump_object (MonoObject *obj, gboolean dump_location)
2784 static char class_name [1024];
2786 MonoClass *class = mono_object_class (obj);
2790 * Python's XML parser is too stupid to parse angle brackets
2791 * in strings, so we just ignore them;
2794 while (class->name [i] && j < sizeof (class_name) - 1) {
2795 if (!strchr ("<>\"", class->name [i]))
2796 class_name [j++] = class->name [i];
2799 g_assert (j < sizeof (class_name));
2802 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2803 class->name_space, class_name,
2804 safe_object_get_size (obj));
2805 if (dump_location) {
2806 const char *location;
2807 if (ptr_in_nursery (obj))
2808 location = "nursery";
2809 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2813 fprintf (heap_dump_file, " location=\"%s\"", location);
2815 fprintf (heap_dump_file, "/>\n");
2819 dump_heap (const char *type, int num, const char *reason)
2824 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2826 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2827 fprintf (heap_dump_file, ">\n");
2828 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2829 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2830 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2831 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2832 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2834 fprintf (heap_dump_file, "<pinned-objects>\n");
2835 for (list = pinned_objects; list; list = list->next)
2836 dump_object (list->obj, TRUE);
2837 fprintf (heap_dump_file, "</pinned-objects>\n");
2839 mono_sgen_dump_section (nursery_section, "nursery");
2841 major_collector.dump_heap (heap_dump_file);
2843 fprintf (heap_dump_file, "<los>\n");
2844 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2845 dump_object ((MonoObject*)bigobj->data, FALSE);
2846 fprintf (heap_dump_file, "</los>\n");
2848 fprintf (heap_dump_file, "</collection>\n");
2852 mono_sgen_register_moved_object (void *obj, void *destination)
2854 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2856 /* FIXME: handle this for parallel collector */
2857 g_assert (!major_collector.is_parallel);
2859 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2860 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2861 moved_objects_idx = 0;
2863 moved_objects [moved_objects_idx++] = obj;
2864 moved_objects [moved_objects_idx++] = destination;
2870 static gboolean inited = FALSE;
2875 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2876 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2877 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2878 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2879 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2880 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2881 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2882 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2883 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2885 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2886 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2887 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2888 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2889 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2890 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2891 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2892 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2893 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2894 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2895 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2896 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2897 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2899 mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2901 #ifdef HEAVY_STATISTICS
2902 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2903 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2904 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2905 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2906 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2907 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2908 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2909 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2911 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2912 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2913 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2914 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2915 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2917 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2918 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2919 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2920 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2922 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2923 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2925 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2926 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2927 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2929 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
2930 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
2932 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2933 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2934 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2935 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2936 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2937 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2938 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2939 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2940 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2946 static gboolean need_calculate_minor_collection_allowance;
2948 static int last_collection_old_num_major_sections;
2949 static mword last_collection_los_memory_usage = 0;
2950 static mword last_collection_old_los_memory_usage;
2951 static mword last_collection_los_memory_alloced;
2954 reset_minor_collection_allowance (void)
2956 need_calculate_minor_collection_allowance = TRUE;
2960 try_calculate_minor_collection_allowance (gboolean overwrite)
2962 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2963 mword los_memory_saved;
2966 g_assert (need_calculate_minor_collection_allowance);
2968 if (!need_calculate_minor_collection_allowance)
2971 if (!*major_collector.have_swept) {
2973 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2977 num_major_sections = major_collector.get_num_major_sections ();
2979 num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
2980 los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
2982 save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2985 * We aim to allow the allocation of as many sections as is
2986 * necessary to reclaim save_target sections in the next
2987 * collection. We assume the collection pattern won't change.
2988 * In the last cycle, we had num_major_sections_saved for
2989 * minor_collection_sections_alloced. Assuming things won't
2990 * change, this must be the same ratio as save_target for
2991 * allowance_target, i.e.
2993 * num_major_sections_saved save_target
2994 * --------------------------------- == ----------------
2995 * minor_collection_sections_alloced allowance_target
2999 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));
3001 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
3003 if (major_collector.have_computed_minor_collection_allowance)
3004 major_collector.have_computed_minor_collection_allowance ();
3006 need_calculate_minor_collection_allowance = FALSE;
3010 need_major_collection (mword space_needed)
3012 mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3013 return (space_needed > available_free_space ()) ||
3014 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
3018 mono_sgen_need_major_collection (mword space_needed)
3020 return need_major_collection (space_needed);
3024 job_gray_queue (WorkerData *worker_data)
3026 return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
3033 } ScanFromRemsetsJobData;
3036 job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
3038 ScanFromRemsetsJobData *job_data = job_data_untyped;
3040 scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
3045 CopyOrMarkObjectFunc func;
3049 } ScanFromRegisteredRootsJobData;
3052 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
3054 ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
3056 scan_from_registered_roots (job_data->func,
3057 job_data->heap_start, job_data->heap_end,
3058 job_data->root_type,
3059 job_gray_queue (worker_data));
3066 } ScanThreadDataJobData;
3069 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
3071 ScanThreadDataJobData *job_data = job_data_untyped;
3073 scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
3074 job_gray_queue (worker_data));
3078 * Collect objects in the nursery. Returns whether to trigger a major
3082 collect_nursery (size_t requested_size)
3084 gboolean needs_major;
3085 size_t max_garbage_amount;
3086 char *orig_nursery_next;
3087 ScanFromRemsetsJobData sfrjd;
3088 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3089 ScanThreadDataJobData stdjd;
3090 TV_DECLARE (all_atv);
3091 TV_DECLARE (all_btv);
3095 if (disable_minor_collections)
3098 mono_perfcounters->gc_collections0++;
3100 current_collection_generation = GENERATION_NURSERY;
3102 binary_protocol_collection (GENERATION_NURSERY);
3103 check_scan_starts ();
3107 orig_nursery_next = nursery_next;
3108 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3109 /* FIXME: optimize later to use the higher address where an object can be present */
3110 nursery_next = MAX (nursery_next, nursery_end);
3112 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)));
3113 max_garbage_amount = nursery_next - nursery_start;
3114 g_assert (nursery_section->size >= max_garbage_amount);
3116 /* world must be stopped already */
3117 TV_GETTIME (all_atv);
3120 /* Pinning no longer depends on clearing all nursery fragments */
3121 clear_current_nursery_fragment (orig_nursery_next);
3124 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3127 check_for_xdomain_refs ();
3129 nursery_section->next_data = nursery_next;
3131 major_collector.start_nursery_collection ();
3133 try_calculate_minor_collection_allowance (FALSE);
3135 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3136 workers_init_distribute_gray_queue ();
3139 mono_stats.minor_gc_count ++;
3141 global_remset_cache_clear ();
3143 /* pin from pinned handles */
3145 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
3146 pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3147 /* identify pinned objects */
3148 optimize_pin_queue (0);
3149 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3150 nursery_section->pin_queue_start = pin_queue;
3151 nursery_section->pin_queue_num_entries = next_pin_slot;
3153 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3154 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3155 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3157 if (consistency_check_at_minor_collection)
3158 check_consistency ();
3160 workers_start_all_workers ();
3163 * Walk all the roots and copy the young objects to the old
3164 * generation, starting from to_space.
3166 * The global remsets must be processed before the workers start
3167 * marking because they might add global remsets.
3169 scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3171 workers_start_marking ();
3173 sfrjd.heap_start = nursery_start;
3174 sfrjd.heap_end = nursery_next;
3175 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_remsets, &sfrjd);
3177 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3179 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3180 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3182 if (use_cardtable) {
3184 card_tables_collect_stats (TRUE);
3185 scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3187 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
3190 if (!major_collector.is_parallel)
3191 drain_gray_stack (&gray_queue, -1);
3193 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3194 report_registered_roots ();
3195 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3196 report_finalizer_roots ();
3198 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3200 /* registered roots, this includes static fields */
3201 scrrjd_normal.func = major_collector.copy_object;
3202 scrrjd_normal.heap_start = nursery_start;
3203 scrrjd_normal.heap_end = nursery_next;
3204 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3205 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
3207 scrrjd_wbarrier.func = major_collector.copy_object;
3208 scrrjd_wbarrier.heap_start = nursery_start;
3209 scrrjd_wbarrier.heap_end = nursery_next;
3210 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3211 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
3214 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3217 stdjd.heap_start = nursery_start;
3218 stdjd.heap_end = nursery_next;
3219 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
3222 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3225 if (major_collector.is_parallel) {
3226 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3227 workers_distribute_gray_queue_sections ();
3233 if (major_collector.is_parallel)
3234 g_assert (gray_object_queue_is_empty (&gray_queue));
3236 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3238 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3239 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3242 * The (single-threaded) finalization code might have done
3243 * some copying/marking so we can only reset the GC thread's
3244 * worker data here instead of earlier when we joined the
3247 if (major_collector.reset_worker_data)
3248 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3250 if (objects_pinned) {
3251 evacuate_pin_staging_area ();
3252 optimize_pin_queue (0);
3253 nursery_section->pin_queue_start = pin_queue;
3254 nursery_section->pin_queue_num_entries = next_pin_slot;
3257 /* walk the pin_queue, build up the fragment list of free memory, unmark
3258 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3261 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3262 build_nursery_fragments (pin_queue, next_pin_slot);
3263 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3265 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3266 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3268 if (consistency_check_at_minor_collection)
3269 check_major_refs ();
3271 major_collector.finish_nursery_collection ();
3273 TV_GETTIME (all_btv);
3274 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3277 dump_heap ("minor", num_minor_gcs - 1, NULL);
3279 /* prepare the pin queue for the next collection */
3280 last_num_pinned = next_pin_slot;
3282 if (fin_ready_list || critical_fin_list) {
3283 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3284 mono_gc_finalize_notify ();
3288 g_assert (gray_object_queue_is_empty (&gray_queue));
3291 card_tables_collect_stats (FALSE);
3293 check_scan_starts ();
3295 binary_protocol_flush_buffers (FALSE);
3297 /*objects are late pinned because of lack of memory, so a major is a good call*/
3298 needs_major = need_major_collection (0) || objects_pinned;
3299 current_collection_generation = -1;
3307 FinalizeEntry *list;
3308 } ScanFinalizerEntriesJobData;
3311 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
3313 ScanFinalizerEntriesJobData *job_data = job_data_untyped;
3315 scan_finalizer_entries (major_collector.copy_or_mark_object,
3317 job_gray_queue (worker_data));
3321 major_do_collection (const char *reason)
3323 LOSObject *bigobj, *prevbo;
3324 TV_DECLARE (all_atv);
3325 TV_DECLARE (all_btv);
3328 /* FIXME: only use these values for the precise scan
3329 * note that to_space pointers should be excluded anyway...
3331 char *heap_start = NULL;
3332 char *heap_end = (char*)-1;
3333 int old_next_pin_slot;
3334 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3335 ScanThreadDataJobData stdjd;
3336 ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
3338 mono_perfcounters->gc_collections1++;
3340 last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3343 * A domain could have been freed, resulting in
3344 * los_memory_usage being less than last_collection_los_memory_usage.
3346 last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3347 last_collection_old_los_memory_usage = los_memory_usage;
3350 //count_ref_nonref_objs ();
3351 //consistency_check ();
3353 binary_protocol_collection (GENERATION_OLD);
3354 check_scan_starts ();
3355 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3356 workers_init_distribute_gray_queue ();
3359 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3361 mono_stats.major_gc_count ++;
3363 /* world must be stopped already */
3364 TV_GETTIME (all_atv);
3367 /* Pinning depends on this */
3368 clear_nursery_fragments (nursery_next);
3371 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3373 nursery_section->next_data = nursery_end;
3374 /* we should also coalesce scanning from sections close to each other
3375 * and deal with pointers outside of the sections later.
3378 if (major_collector.start_major_collection)
3379 major_collector.start_major_collection ();
3381 *major_collector.have_swept = FALSE;
3382 reset_minor_collection_allowance ();
3385 check_for_xdomain_refs ();
3387 /* The remsets are not useful for a major collection */
3389 global_remset_cache_clear ();
3391 card_table_clear ();
3395 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3396 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3397 optimize_pin_queue (0);
3400 * pin_queue now contains all candidate pointers, sorted and
3401 * uniqued. We must do two passes now to figure out which
3402 * objects are pinned.
3404 * The first is to find within the pin_queue the area for each
3405 * section. This requires that the pin_queue be sorted. We
3406 * also process the LOS objects and pinned chunks here.
3408 * The second, destructive, pass is to reduce the section
3409 * areas to pointers to the actually pinned objects.
3411 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3412 /* first pass for the sections */
3413 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3414 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3415 /* identify possible pointers to the insize of large objects */
3416 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3417 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3419 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3420 pin_object (bigobj->data);
3421 /* FIXME: only enqueue if object has references */
3422 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3424 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3425 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));
3428 /* second pass for the sections */
3429 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3430 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3431 old_next_pin_slot = next_pin_slot;
3434 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3435 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3436 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3438 major_collector.init_to_space ();
3440 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3441 main_gc_thread = pthread_self ();
3444 workers_start_all_workers ();
3445 workers_start_marking ();
3447 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3448 report_registered_roots ();
3450 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3452 /* registered roots, this includes static fields */
3453 scrrjd_normal.func = major_collector.copy_or_mark_object;
3454 scrrjd_normal.heap_start = heap_start;
3455 scrrjd_normal.heap_end = heap_end;
3456 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3457 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
3459 scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
3460 scrrjd_wbarrier.heap_start = heap_start;
3461 scrrjd_wbarrier.heap_end = heap_end;
3462 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3463 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
3466 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3469 stdjd.heap_start = heap_start;
3470 stdjd.heap_end = heap_end;
3471 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
3474 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3477 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3479 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3480 report_finalizer_roots ();
3482 /* scan the list of objects ready for finalization */
3483 sfejd_fin_ready.list = fin_ready_list;
3484 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_fin_ready);
3486 sfejd_critical_fin.list = critical_fin_list;
3487 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_critical_fin);
3490 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3491 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3494 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3496 if (major_collector.is_parallel) {
3497 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3498 workers_distribute_gray_queue_sections ();
3504 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3505 main_gc_thread = NULL;
3508 if (major_collector.is_parallel)
3509 g_assert (gray_object_queue_is_empty (&gray_queue));
3511 /* all the objects in the heap */
3512 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3514 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3517 * The (single-threaded) finalization code might have done
3518 * some copying/marking so we can only reset the GC thread's
3519 * worker data here instead of earlier when we joined the
3522 if (major_collector.reset_worker_data)
3523 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3525 if (objects_pinned) {
3526 /*This is slow, but we just OOM'd*/
3527 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3528 evacuate_pin_staging_area ();
3529 optimize_pin_queue (0);
3530 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3534 reset_heap_boundaries ();
3535 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
3537 /* sweep the big objects list */
3539 for (bigobj = los_object_list; bigobj;) {
3540 if (object_is_pinned (bigobj->data)) {
3541 unpin_object (bigobj->data);
3542 mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3545 /* not referenced anywhere, so we can free it */
3547 prevbo->next = bigobj->next;
3549 los_object_list = bigobj->next;
3551 bigobj = bigobj->next;
3552 mono_sgen_los_free_object (to_free);
3556 bigobj = bigobj->next;
3560 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3562 mono_sgen_los_sweep ();
3565 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3567 major_collector.sweep ();
3570 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3572 /* walk the pin_queue, build up the fragment list of free memory, unmark
3573 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3576 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
3579 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3581 TV_GETTIME (all_btv);
3582 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3585 dump_heap ("major", num_major_gcs - 1, reason);
3587 /* prepare the pin queue for the next collection */
3589 if (fin_ready_list || critical_fin_list) {
3590 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3591 mono_gc_finalize_notify ();
3595 g_assert (gray_object_queue_is_empty (&gray_queue));
3597 try_calculate_minor_collection_allowance (TRUE);
3599 minor_collection_sections_alloced = 0;
3600 last_collection_los_memory_usage = los_memory_usage;
3602 major_collector.finish_major_collection ();
3604 check_scan_starts ();
3606 binary_protocol_flush_buffers (FALSE);
3608 //consistency_check ();
3612 major_collection (const char *reason)
3614 if (disable_major_collections) {
3615 collect_nursery (0);
3619 current_collection_generation = GENERATION_OLD;
3620 major_do_collection (reason);
3621 current_collection_generation = -1;
3625 sgen_collect_major_no_lock (const char *reason)
3627 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3629 major_collection (reason);
3631 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3635 * When deciding if it's better to collect or to expand, keep track
3636 * of how much garbage was reclaimed with the last collection: if it's too
3638 * This is called when we could not allocate a small object.
3640 static void __attribute__((noinline))
3641 minor_collect_or_expand_inner (size_t size)
3643 int do_minor_collection = 1;
3645 g_assert (nursery_section);
3646 if (do_minor_collection) {
3647 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3649 if (collect_nursery (size)) {
3650 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3651 major_collection ("minor overflow");
3652 /* keep events symmetric */
3653 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3655 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3657 /* this also sets the proper pointers for the next allocation */
3658 if (!alloc_fragment_for_size (size)) {
3660 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3661 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3662 for (i = 0; i < last_num_pinned; ++i) {
3663 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])));
3667 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3669 //report_internal_mem_usage ();
3673 * ######################################################################
3674 * ######## Memory allocation from the OS
3675 * ######################################################################
3676 * This section of code deals with getting memory from the OS and
3677 * allocating memory for GC-internal data structures.
3678 * Internal memory can be handled with a freelist for small objects.
3684 G_GNUC_UNUSED static void
3685 report_internal_mem_usage (void)
3687 printf ("Internal memory usage:\n");
3688 mono_sgen_report_internal_mem_usage ();
3689 printf ("Pinned memory usage:\n");
3690 major_collector.report_pinned_memory_usage ();
3694 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3695 * This must not require any lock.
3698 mono_sgen_alloc_os_memory (size_t size, int activate)
3701 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3703 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3704 size += pagesize - 1;
3705 size &= ~(pagesize - 1);
3706 ptr = mono_valloc (0, size, prot_flags);
3708 total_alloc += size;
3713 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3716 mono_sgen_free_os_memory (void *addr, size_t size)
3718 mono_vfree (addr, size);
3720 size += pagesize - 1;
3721 size &= ~(pagesize - 1);
3723 total_alloc -= size;
3727 * ######################################################################
3728 * ######## Object allocation
3729 * ######################################################################
3730 * This section of code deals with allocating memory for objects.
3731 * There are several ways:
3732 * *) allocate large objects
3733 * *) allocate normal objects
3734 * *) fast lock-free allocation
3735 * *) allocation of pinned objects
3739 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3741 /* remove from the list */
3743 prev->next = frag->next;
3745 nursery_fragments = frag->next;
3746 nursery_next = frag->fragment_start;
3747 nursery_frag_real_end = frag->fragment_end;
3749 DEBUG (4, fprintf (gc_debug_file, "Using nursery fragment %p-%p, size: %td (req: %zd)\n", nursery_next, nursery_frag_real_end, nursery_frag_real_end - nursery_next, size));
3750 frag->next = fragment_freelist;
3751 fragment_freelist = frag;
3755 * Allocate a new nursery fragment able to hold an object of size @size.
3756 * nursery_next and nursery_frag_real_end are set to the boundaries of the fragment.
3757 * Return TRUE if found, FALSE otherwise.
3760 alloc_fragment_for_size (size_t size)
3762 Fragment *frag, *prev;
3763 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3765 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3766 /* Clear the remaining space, pinning depends on this */
3767 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3771 for (frag = nursery_fragments; frag; frag = frag->next) {
3772 if (size <= (frag->fragment_end - frag->fragment_start)) {
3773 setup_fragment (frag, prev, size);
3782 * Same as alloc_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3783 * This improves nursery usage.
3786 alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3788 Fragment *frag, *prev, *min_prev;
3789 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, desired size: %zd minimum size %zd\n", nursery_frag_real_end, desired_size, minimum_size));
3791 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3792 /* Clear the remaining space, pinning depends on this */
3793 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3796 min_prev = GINT_TO_POINTER (-1);
3799 for (frag = nursery_fragments; frag; frag = frag->next) {
3800 int frag_size = frag->fragment_end - frag->fragment_start;
3801 if (desired_size <= frag_size) {
3802 setup_fragment (frag, prev, desired_size);
3803 return desired_size;
3805 if (minimum_size <= frag_size)
3811 if (min_prev != GINT_TO_POINTER (-1)) {
3814 frag = min_prev->next;
3816 frag = nursery_fragments;
3818 frag_size = frag->fragment_end - frag->fragment_start;
3819 HEAVY_STAT (++stat_wasted_fragments_used);
3820 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3822 setup_fragment (frag, min_prev, minimum_size);
3830 alloc_degraded (MonoVTable *vtable, size_t size)
3832 if (need_major_collection (0)) {
3833 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3835 major_collection ("degraded overflow");
3837 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3840 return major_collector.alloc_degraded (vtable, size);
3844 * Provide a variant that takes just the vtable for small fixed-size objects.
3845 * The aligned size is already computed and stored in vt->gc_descr.
3846 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3847 * processing. We can keep track of where objects start, for example,
3848 * so when we scan the thread stacks for pinned objects, we can start
3849 * a search for the pinned object in SCAN_START_SIZE chunks.
3852 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3854 /* FIXME: handle OOM */
3859 HEAVY_STAT (++stat_objects_alloced);
3860 if (size <= MAX_SMALL_OBJ_SIZE)
3861 HEAVY_STAT (stat_bytes_alloced += size);
3863 HEAVY_STAT (stat_bytes_alloced_los += size);
3865 size = ALIGN_UP (size);
3867 g_assert (vtable->gc_descr);
3869 if (G_UNLIKELY (collect_before_allocs)) {
3870 static int alloc_count;
3872 InterlockedIncrement (&alloc_count);
3873 if (((alloc_count % collect_before_allocs) == 0) && nursery_section) {
3874 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3876 collect_nursery (0);
3878 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3879 if (!degraded_mode && !alloc_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
3881 g_assert_not_reached ();
3887 * We must already have the lock here instead of after the
3888 * fast path because we might be interrupted in the fast path
3889 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3890 * and we'll end up allocating an object in a fragment which
3891 * no longer belongs to us.
3893 * The managed allocator does not do this, but it's treated
3894 * specially by the world-stopping code.
3897 if (size > MAX_SMALL_OBJ_SIZE) {
3898 p = mono_sgen_los_alloc_large_inner (vtable, size);
3900 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3902 p = (void**)TLAB_NEXT;
3903 /* FIXME: handle overflow */
3904 new_next = (char*)p + size;
3905 TLAB_NEXT = new_next;
3907 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3911 * FIXME: We might need a memory barrier here so the change to tlab_next is
3912 * visible before the vtable store.
3915 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3916 binary_protocol_alloc (p , vtable, size);
3917 g_assert (*p == NULL);
3920 g_assert (TLAB_NEXT == new_next);
3927 /* there are two cases: the object is too big or we run out of space in the TLAB */
3928 /* we also reach here when the thread does its first allocation after a minor
3929 * collection, since the tlab_ variables are initialized to NULL.
3930 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3931 * objects that need finalizers can have the high bit set in their size
3932 * so the above check fails and we can readily add the object to the queue.
3933 * This avoids taking again the GC lock when registering, but this is moot when
3934 * doing thread-local allocation, so it may not be a good idea.
3936 g_assert (TLAB_NEXT == new_next);
3937 if (TLAB_NEXT >= TLAB_REAL_END) {
3939 * Run out of space in the TLAB. When this happens, some amount of space
3940 * remains in the TLAB, but not enough to satisfy the current allocation
3941 * request. Currently, we retire the TLAB in all cases, later we could
3942 * keep it if the remaining space is above a treshold, and satisfy the
3943 * allocation directly from the nursery.
3946 /* when running in degraded mode, we continue allocing that way
3947 * for a while, to decrease the number of useless nursery collections.
3949 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3950 p = alloc_degraded (vtable, size);
3951 binary_protocol_alloc_degraded (p, vtable, size);
3955 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3956 if (size > tlab_size) {
3957 /* Allocate directly from the nursery */
3958 if (nursery_next + size >= nursery_frag_real_end) {
3959 if (!alloc_fragment_for_size (size)) {
3960 minor_collect_or_expand_inner (size);
3961 if (degraded_mode) {
3962 p = alloc_degraded (vtable, size);
3963 binary_protocol_alloc_degraded (p, vtable, size);
3969 p = (void*)nursery_next;
3970 nursery_next += size;
3971 if (nursery_next > nursery_frag_real_end) {
3976 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3977 memset (p, 0, size);
3980 int alloc_size = tlab_size;
3981 int available_in_nursery = nursery_frag_real_end - nursery_next;
3983 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3985 if (alloc_size >= available_in_nursery) {
3986 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3987 alloc_size = available_in_nursery;
3989 alloc_size = alloc_fragment_for_size_range (tlab_size, size);
3991 alloc_size = tlab_size;
3992 minor_collect_or_expand_inner (tlab_size);
3993 if (degraded_mode) {
3994 p = alloc_degraded (vtable, size);
3995 binary_protocol_alloc_degraded (p, vtable, size);
4002 /* Allocate a new TLAB from the current nursery fragment */
4003 TLAB_START = nursery_next;
4004 nursery_next += alloc_size;
4005 TLAB_NEXT = TLAB_START;
4006 TLAB_REAL_END = TLAB_START + alloc_size;
4007 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
4009 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
4010 memset (TLAB_START, 0, alloc_size);
4013 /* Allocate from the TLAB */
4014 p = (void*)TLAB_NEXT;
4016 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4018 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4021 /* Reached tlab_temp_end */
4023 /* record the scan start so we can find pinned objects more easily */
4024 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4025 /* we just bump tlab_temp_end as well */
4026 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4027 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4032 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4033 binary_protocol_alloc (p, vtable, size);
4041 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4047 size = ALIGN_UP (size);
4049 g_assert (vtable->gc_descr);
4050 if (size <= MAX_SMALL_OBJ_SIZE) {
4051 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4053 p = (void**)TLAB_NEXT;
4054 /* FIXME: handle overflow */
4055 new_next = (char*)p + size;
4056 TLAB_NEXT = new_next;
4058 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4062 * FIXME: We might need a memory barrier here so the change to tlab_next is
4063 * visible before the vtable store.
4066 HEAVY_STAT (++stat_objects_alloced);
4067 HEAVY_STAT (stat_bytes_alloced += size);
4069 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4070 binary_protocol_alloc (p, vtable, size);
4071 g_assert (*p == NULL);
4074 g_assert (TLAB_NEXT == new_next);
4083 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4086 #ifndef DISABLE_CRITICAL_REGION
4088 ENTER_CRITICAL_REGION;
4089 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4091 EXIT_CRITICAL_REGION;
4094 EXIT_CRITICAL_REGION;
4097 res = mono_gc_alloc_obj_nolock (vtable, size);
4099 if (G_UNLIKELY (!res))
4100 return mono_gc_out_of_memory (size);
4105 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4108 #ifndef DISABLE_CRITICAL_REGION
4110 ENTER_CRITICAL_REGION;
4111 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4113 arr->max_length = max_length;
4114 EXIT_CRITICAL_REGION;
4117 EXIT_CRITICAL_REGION;
4122 arr = mono_gc_alloc_obj_nolock (vtable, size);
4123 if (G_UNLIKELY (!arr)) {
4125 return mono_gc_out_of_memory (size);
4128 arr->max_length = max_length;
4136 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4139 MonoArrayBounds *bounds;
4143 arr = mono_gc_alloc_obj_nolock (vtable, size);
4144 if (G_UNLIKELY (!arr)) {
4146 return mono_gc_out_of_memory (size);
4149 arr->max_length = max_length;
4151 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4152 arr->bounds = bounds;
4160 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4163 #ifndef DISABLE_CRITICAL_REGION
4165 ENTER_CRITICAL_REGION;
4166 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4169 EXIT_CRITICAL_REGION;
4172 EXIT_CRITICAL_REGION;
4177 str = mono_gc_alloc_obj_nolock (vtable, size);
4178 if (G_UNLIKELY (!str)) {
4180 return mono_gc_out_of_memory (size);
4191 * To be used for interned strings and possibly MonoThread, reflection handles.
4192 * We may want to explicitly free these objects.
4195 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4198 size = ALIGN_UP (size);
4201 if (size > MAX_SMALL_OBJ_SIZE) {
4202 /* large objects are always pinned anyway */
4203 p = mono_sgen_los_alloc_large_inner (vtable, size);
4205 DEBUG (9, g_assert (vtable->klass->inited));
4206 p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
4209 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4210 binary_protocol_alloc_pinned (p, vtable, size);
4218 mono_gc_alloc_mature (MonoVTable *vtable)
4221 size_t size = ALIGN_UP (vtable->klass->instance_size);
4223 res = alloc_degraded (vtable, size);
4226 if (G_UNLIKELY (vtable->klass->has_finalize))
4227 mono_object_register_finalizer ((MonoObject*)res);
4233 * ######################################################################
4234 * ######## Finalization support
4235 * ######################################################################
4239 * this is valid for the nursery: if the object has been forwarded it means it's
4240 * still refrenced from a root. If it is pinned it's still alive as well.
4241 * Return TRUE if @obj is ready to be finalized.
4243 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4246 is_critical_finalizer (FinalizeEntry *entry)
4251 if (!mono_defaults.critical_finalizer_object)
4254 obj = entry->object;
4255 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4257 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4261 queue_finalization_entry (FinalizeEntry *entry) {
4262 if (is_critical_finalizer (entry)) {
4263 entry->next = critical_fin_list;
4264 critical_fin_list = entry;
4266 entry->next = fin_ready_list;
4267 fin_ready_list = entry;
4271 /* LOCKING: requires that the GC lock is held */
4273 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4275 FinalizeEntry **finalizable_hash = hash_table->table;
4276 mword finalizable_hash_size = hash_table->size;
4279 FinalizeEntry **new_hash;
4280 FinalizeEntry *entry, *next;
4281 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4283 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4284 for (i = 0; i < finalizable_hash_size; ++i) {
4285 for (entry = finalizable_hash [i]; entry; entry = next) {
4286 hash = mono_object_hash (entry->object) % new_size;
4288 entry->next = new_hash [hash];
4289 new_hash [hash] = entry;
4292 mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4293 hash_table->table = new_hash;
4294 hash_table->size = new_size;
4297 /* LOCKING: requires that the GC lock is held */
4299 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4301 if (hash_table->num_registered >= hash_table->size * 2)
4302 rehash_fin_table (hash_table);
4305 /* LOCKING: requires that the GC lock is held */
4307 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
4309 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4310 FinalizeEntry *entry, *prev;
4312 FinalizeEntry **finalizable_hash = hash_table->table;
4313 mword finalizable_hash_size = hash_table->size;
4317 for (i = 0; i < finalizable_hash_size; ++i) {
4319 for (entry = finalizable_hash [i]; entry;) {
4320 if ((char*)entry->object >= start && (char*)entry->object < end && !major_collector.is_object_live (entry->object)) {
4321 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4322 char *copy = entry->object;
4323 copy_func ((void**)©, queue);
4326 FinalizeEntry *next;
4327 /* remove and put in fin_ready_list */
4329 prev->next = entry->next;
4331 finalizable_hash [i] = entry->next;
4333 num_ready_finalizers++;
4334 hash_table->num_registered--;
4335 queue_finalization_entry (entry);
4336 bridge_register_finalized_object ((MonoObject*)copy);
4337 /* Make it survive */
4338 from = entry->object;
4339 entry->object = copy;
4340 DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", entry->object, safe_name (entry->object), from, num_ready_finalizers, hash_table->num_registered));
4344 char *from = entry->object;
4345 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4346 FinalizeEntry *next = entry->next;
4347 unsigned int major_hash;
4348 /* remove from the list */
4350 prev->next = entry->next;
4352 finalizable_hash [i] = entry->next;
4353 hash_table->num_registered--;
4355 entry->object = copy;
4357 /* insert it into the major hash */
4358 rehash_fin_table_if_necessary (&major_finalizable_hash);
4359 major_hash = mono_object_hash ((MonoObject*) copy) %
4360 major_finalizable_hash.size;
4361 entry->next = major_finalizable_hash.table [major_hash];
4362 major_finalizable_hash.table [major_hash] = entry;
4363 major_finalizable_hash.num_registered++;
4365 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4370 /* update pointer */
4371 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4372 entry->object = copy;
4377 entry = entry->next;
4383 object_is_reachable (char *object, char *start, char *end)
4385 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4386 if (object < start || object >= end)
4388 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4392 mono_sgen_object_is_live (void *obj)
4394 if (ptr_in_nursery (obj))
4395 return object_is_pinned (obj);
4396 if (current_collection_generation == GENERATION_NURSERY)
4398 return major_collector.is_object_live (obj);
4401 /* LOCKING: requires that the GC lock is held */
4403 null_ephemerons_for_domain (MonoDomain *domain)
4405 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4408 MonoObject *object = (MonoObject*)current->array;
4410 if (object && !object->vtable) {
4411 EphemeronLinkNode *tmp = current;
4414 prev->next = current->next;
4416 ephemeron_list = current->next;
4418 current = current->next;
4419 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4422 current = current->next;
4427 /* LOCKING: requires that the GC lock is held */
4429 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4431 int was_in_nursery, was_promoted;
4432 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4434 Ephemeron *cur, *array_end;
4438 char *object = current->array;
4440 if (!object_is_reachable (object, start, end)) {
4441 EphemeronLinkNode *tmp = current;
4443 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4446 prev->next = current->next;
4448 ephemeron_list = current->next;
4450 current = current->next;
4451 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4456 was_in_nursery = ptr_in_nursery (object);
4457 copy_func ((void**)&object, queue);
4458 current->array = object;
4460 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4461 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4463 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4465 array = (MonoArray*)object;
4466 cur = mono_array_addr (array, Ephemeron, 0);
4467 array_end = cur + mono_array_length_fast (array);
4468 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4470 for (; cur < array_end; ++cur) {
4471 char *key = (char*)cur->key;
4473 if (!key || key == tombstone)
4476 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4477 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4478 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4480 if (!object_is_reachable (key, start, end)) {
4481 cur->key = tombstone;
4487 if (ptr_in_nursery (key)) {/*key was not promoted*/
4488 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4489 mono_sgen_add_to_global_remset (queue->allocator, &cur->key);
4491 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4492 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4493 mono_sgen_add_to_global_remset (queue->allocator, &cur->value);
4498 current = current->next;
4502 /* LOCKING: requires that the GC lock is held */
4504 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4506 int nothing_marked = 1;
4507 EphemeronLinkNode *current = ephemeron_list;
4509 Ephemeron *cur, *array_end;
4512 for (current = ephemeron_list; current; current = current->next) {
4513 char *object = current->array;
4514 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4516 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4517 if (object < start || object >= end)
4520 /*It has to be alive*/
4521 if (!object_is_reachable (object, start, end)) {
4522 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4526 copy_func ((void**)&object, queue);
4528 array = (MonoArray*)object;
4529 cur = mono_array_addr (array, Ephemeron, 0);
4530 array_end = cur + mono_array_length_fast (array);
4531 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4533 for (; cur < array_end; ++cur) {
4534 char *key = cur->key;
4536 if (!key || key == tombstone)
4539 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4540 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4541 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4543 if (object_is_reachable (key, start, end)) {
4544 char *value = cur->value;
4546 copy_func ((void**)&cur->key, queue);
4548 if (!object_is_reachable (value, start, end))
4550 copy_func ((void**)&cur->value, queue);
4556 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4557 return nothing_marked;
4560 /* LOCKING: requires that the GC lock is held */
4562 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
4564 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4565 DisappearingLink **disappearing_link_hash = hash->table;
4566 int disappearing_link_hash_size = hash->size;
4567 DisappearingLink *entry, *prev;
4569 if (!hash->num_links)
4571 for (i = 0; i < disappearing_link_hash_size; ++i) {
4573 for (entry = disappearing_link_hash [i]; entry;) {
4575 gboolean track = DISLINK_TRACK (entry);
4578 * Tracked references are processed after
4579 * finalization handling whereas standard weak
4580 * references are processed before. If an
4581 * object is still not marked after finalization
4582 * handling it means that it either doesn't have
4583 * a finalizer or the finalizer has already run,
4584 * so we must null a tracking reference.
4586 if (track == before_finalization) {
4588 entry = entry->next;
4592 object = DISLINK_OBJECT (entry);
4594 if (object >= start && object < end && !major_collector.is_object_live (object)) {
4595 if (object_is_fin_ready (object)) {
4596 void **p = entry->link;
4597 DisappearingLink *old;
4599 /* remove from list */
4601 prev->next = entry->next;
4603 disappearing_link_hash [i] = entry->next;
4604 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4606 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4611 char *copy = object;
4612 copy_func ((void**)©, queue);
4614 /* Update pointer if it's moved. If the object
4615 * has been moved out of the nursery, we need to
4616 * remove the link from the minor hash table to
4619 * FIXME: what if an object is moved earlier?
4622 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4623 void **link = entry->link;
4624 DisappearingLink *old;
4625 /* remove from list */
4627 prev->next = entry->next;
4629 disappearing_link_hash [i] = entry->next;
4631 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4635 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4636 track, GENERATION_OLD);
4638 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4642 *entry->link = HIDE_POINTER (copy, track);
4643 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4648 entry = entry->next;
4653 /* LOCKING: requires that the GC lock is held */
4655 null_links_for_domain (MonoDomain *domain, int generation)
4657 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4658 DisappearingLink **disappearing_link_hash = hash->table;
4659 int disappearing_link_hash_size = hash->size;
4660 DisappearingLink *entry, *prev;
4662 for (i = 0; i < disappearing_link_hash_size; ++i) {
4664 for (entry = disappearing_link_hash [i]; entry; ) {
4665 char *object = DISLINK_OBJECT (entry);
4666 if (object && !((MonoObject*)object)->vtable) {
4667 DisappearingLink *next = entry->next;
4672 disappearing_link_hash [i] = next;
4674 if (*(entry->link)) {
4675 *(entry->link) = NULL;
4676 g_warning ("Disappearing link %p not freed", entry->link);
4678 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4685 entry = entry->next;
4690 /* LOCKING: requires that the GC lock is held */
4692 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4693 FinalizeEntryHashTable *hash_table)
4695 FinalizeEntry **finalizable_hash = hash_table->table;
4696 mword finalizable_hash_size = hash_table->size;
4697 FinalizeEntry *entry, *prev;
4700 if (no_finalize || !out_size || !out_array)
4703 for (i = 0; i < finalizable_hash_size; ++i) {
4705 for (entry = finalizable_hash [i]; entry;) {
4706 if (mono_object_domain (entry->object) == domain) {
4707 FinalizeEntry *next;
4708 /* remove and put in out_array */
4710 prev->next = entry->next;
4712 finalizable_hash [i] = entry->next;
4714 hash_table->num_registered--;
4715 out_array [count ++] = entry->object;
4716 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, hash_table->num_registered));
4718 if (count == out_size)
4723 entry = entry->next;
4730 * mono_gc_finalizers_for_domain:
4731 * @domain: the unloading appdomain
4732 * @out_array: output array
4733 * @out_size: size of output array
4735 * Store inside @out_array up to @out_size objects that belong to the unloading
4736 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4737 * until it returns 0.
4738 * The items are removed from the finalizer data structure, so the caller is supposed
4740 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4743 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4748 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4749 if (result < out_size) {
4750 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4751 &major_finalizable_hash);
4759 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4761 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4762 FinalizeEntry **finalizable_hash;
4763 mword finalizable_hash_size;
4764 FinalizeEntry *entry, *prev;
4768 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4769 hash = mono_object_hash (obj);
4771 rehash_fin_table_if_necessary (hash_table);
4772 finalizable_hash = hash_table->table;
4773 finalizable_hash_size = hash_table->size;
4774 hash %= finalizable_hash_size;
4776 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4777 if (entry->object == obj) {
4779 /* remove from the list */
4781 prev->next = entry->next;
4783 finalizable_hash [hash] = entry->next;
4784 hash_table->num_registered--;
4785 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered));
4786 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4794 /* request to deregister, but already out of the list */
4798 entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4799 entry->object = obj;
4800 entry->next = finalizable_hash [hash];
4801 finalizable_hash [hash] = entry;
4802 hash_table->num_registered++;
4803 DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d) to %s table\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered, generation_name (generation)));
4808 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4810 if (ptr_in_nursery (obj))
4811 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4813 register_for_finalization (obj, user_data, GENERATION_OLD);
4817 rehash_dislink (DisappearingLinkHashTable *hash_table)
4819 DisappearingLink **disappearing_link_hash = hash_table->table;
4820 int disappearing_link_hash_size = hash_table->size;
4823 DisappearingLink **new_hash;
4824 DisappearingLink *entry, *next;
4825 int new_size = g_spaced_primes_closest (hash_table->num_links);
4827 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4828 for (i = 0; i < disappearing_link_hash_size; ++i) {
4829 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4830 hash = mono_aligned_addr_hash (entry->link) % new_size;
4832 entry->next = new_hash [hash];
4833 new_hash [hash] = entry;
4836 mono_sgen_free_internal_dynamic (disappearing_link_hash,
4837 disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4838 hash_table->table = new_hash;
4839 hash_table->size = new_size;
4842 /* LOCKING: assumes the GC lock is held */
4844 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4846 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4847 DisappearingLink *entry, *prev;
4849 DisappearingLink **disappearing_link_hash = hash_table->table;
4850 int disappearing_link_hash_size = hash_table->size;
4852 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4853 rehash_dislink (hash_table);
4854 disappearing_link_hash = hash_table->table;
4855 disappearing_link_hash_size = hash_table->size;
4857 /* FIXME: add check that link is not in the heap */
4858 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4859 entry = disappearing_link_hash [hash];
4861 for (; entry; entry = entry->next) {
4862 /* link already added */
4863 if (link == entry->link) {
4864 /* NULL obj means remove */
4867 prev->next = entry->next;
4869 disappearing_link_hash [hash] = entry->next;
4870 hash_table->num_links--;
4871 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4872 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4875 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4883 entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4884 *link = HIDE_POINTER (obj, track);
4886 entry->next = disappearing_link_hash [hash];
4887 disappearing_link_hash [hash] = entry;
4888 hash_table->num_links++;
4889 DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p to %s table\n", entry, obj, obj->vtable->klass->name, link, generation_name (generation)));
4892 /* LOCKING: assumes the GC lock is held */
4894 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4896 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4897 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4899 if (ptr_in_nursery (obj))
4900 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4902 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4907 mono_gc_invoke_finalizers (void)
4909 FinalizeEntry *entry = NULL;
4910 gboolean entry_is_critical = FALSE;
4913 /* FIXME: batch to reduce lock contention */
4914 while (fin_ready_list || critical_fin_list) {
4918 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4920 /* We have finalized entry in the last
4921 interation, now we need to remove it from
4924 *list = entry->next;
4926 FinalizeEntry *e = *list;
4927 while (e->next != entry)
4929 e->next = entry->next;
4931 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4935 /* Now look for the first non-null entry. */
4936 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4939 entry_is_critical = FALSE;
4941 entry_is_critical = TRUE;
4942 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4947 g_assert (entry->object);
4948 num_ready_finalizers--;
4949 obj = entry->object;
4950 entry->object = NULL;
4951 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4959 g_assert (entry->object == NULL);
4961 /* the object is on the stack so it is pinned */
4962 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4963 mono_gc_run_finalize (obj, NULL);
4970 mono_gc_pending_finalizers (void)
4972 return fin_ready_list || critical_fin_list;
4975 /* Negative value to remove */
4977 mono_gc_add_memory_pressure (gint64 value)
4979 /* FIXME: Use interlocked functions */
4981 memory_pressure += value;
4986 mono_sgen_register_major_sections_alloced (int num_sections)
4988 minor_collection_sections_alloced += num_sections;
4992 mono_sgen_get_minor_collection_allowance (void)
4994 return minor_collection_allowance;
4998 * ######################################################################
4999 * ######## registered roots support
5000 * ######################################################################
5004 rehash_roots (gboolean pinned)
5008 RootRecord **new_hash;
5009 RootRecord *entry, *next;
5012 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
5013 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5014 for (i = 0; i < roots_hash_size [pinned]; ++i) {
5015 for (entry = roots_hash [pinned][i]; entry; entry = next) {
5016 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5018 entry->next = new_hash [hash];
5019 new_hash [hash] = entry;
5022 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5023 roots_hash [pinned] = new_hash;
5024 roots_hash_size [pinned] = new_size;
5028 find_root (int root_type, char *start, guint32 addr_hash)
5030 RootRecord *new_root;
5032 guint32 hash = addr_hash % roots_hash_size [root_type];
5033 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5034 /* we allow changing the size and the descriptor (for thread statics etc) */
5035 if (new_root->start_root == start) {
5044 * We do not coalesce roots.
5047 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5049 RootRecord *new_root;
5050 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5053 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5054 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5057 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5058 new_root = find_root (i, start, addr_hash);
5059 /* we allow changing the size and the descriptor (for thread statics etc) */
5061 size_t old_size = new_root->end_root - new_root->start_root;
5062 new_root->end_root = new_root->start_root + size;
5063 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5064 ((new_root->root_desc == 0) && (descr == NULL)));
5065 new_root->root_desc = (mword)descr;
5067 roots_size -= old_size;
5072 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
5074 new_root->start_root = start;
5075 new_root->end_root = new_root->start_root + size;
5076 new_root->root_desc = (mword)descr;
5078 hash = addr_hash % roots_hash_size [root_type];
5079 num_roots_entries [root_type]++;
5080 new_root->next = roots_hash [root_type] [hash];
5081 roots_hash [root_type][hash] = new_root;
5082 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));
5092 mono_gc_register_root (char *start, size_t size, void *descr)
5094 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5098 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5100 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5104 mono_gc_deregister_root (char* addr)
5106 RootRecord *tmp, *prev;
5107 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5111 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5112 hash = addr_hash % roots_hash_size [root_type];
5113 tmp = roots_hash [root_type][hash];
5116 if (tmp->start_root == (char*)addr) {
5118 prev->next = tmp->next;
5120 roots_hash [root_type][hash] = tmp->next;
5121 roots_size -= (tmp->end_root - tmp->start_root);
5122 num_roots_entries [root_type]--;
5123 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5124 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
5135 * ######################################################################
5136 * ######## Thread handling (stop/start code)
5137 * ######################################################################
5140 #if USE_SIGNAL_BASED_START_STOP_WORLD
5142 static MonoSemType suspend_ack_semaphore;
5143 static MonoSemType *suspend_ack_semaphore_ptr;
5144 static unsigned int global_stop_count = 0;
5146 static sigset_t suspend_signal_mask;
5149 static MonoContext cur_thread_ctx = {0};
5151 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5155 update_current_thread_stack (void *start)
5157 int stack_guard = 0;
5158 #ifndef USE_MONO_CTX
5159 void *ptr = cur_thread_regs;
5161 SgenThreadInfo *info = mono_thread_info_current ();
5163 info->stack_start = align_pointer (&stack_guard);
5164 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5166 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
5167 info->monoctx = &cur_thread_ctx;
5169 ARCH_STORE_REGS (ptr);
5170 info->stopped_regs = ptr;
5172 if (gc_callbacks.thread_suspend_func)
5173 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5177 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5178 * have cross-domain checks in the write barrier.
5180 //#define XDOMAIN_CHECKS_IN_WBARRIER
5182 #ifndef SGEN_BINARY_PROTOCOL
5183 #ifndef HEAVY_STATISTICS
5184 #define MANAGED_ALLOCATION
5185 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5186 #define MANAGED_WBARRIER
5192 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5195 mono_sgen_wait_for_suspend_ack (int count)
5197 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5198 /* mach thread_resume is synchronous so we dont need to wait for them */
5202 for (i = 0; i < count; ++i) {
5203 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5204 if (errno != EINTR) {
5205 g_error ("sem_wait ()");
5213 restart_threads_until_none_in_managed_allocator (void)
5215 SgenThreadInfo *info;
5216 int num_threads_died = 0;
5217 int sleep_duration = -1;
5220 int restart_count = 0, restarted_count = 0;
5221 /* restart all threads that stopped in the
5223 FOREACH_THREAD_SAFE (info) {
5227 if (!info->stack_start || info->in_critical_region ||
5228 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5229 binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
5230 result = mono_sgen_resume_thread (info);
5237 /* we set the stopped_ip to
5238 NULL for threads which
5239 we're not restarting so
5240 that we can easily identify
5242 info->stopped_ip = NULL;
5243 info->stopped_domain = NULL;
5245 } END_FOREACH_THREAD_SAFE
5246 /* if no threads were restarted, we're done */
5247 if (restart_count == 0)
5250 /* wait for the threads to signal their restart */
5251 mono_sgen_wait_for_suspend_ack (restart_count);
5253 if (sleep_duration < 0) {
5257 g_usleep (sleep_duration);
5258 sleep_duration += 10;
5261 /* stop them again */
5262 FOREACH_THREAD (info) {
5264 if (info->skip || info->stopped_ip == NULL)
5266 result = mono_sgen_suspend_thread (info);
5273 } END_FOREACH_THREAD
5274 /* some threads might have died */
5275 num_threads_died += restart_count - restarted_count;
5276 /* wait for the threads to signal their suspension
5278 mono_sgen_wait_for_suspend_ack (restart_count);
5281 return num_threads_died;
5284 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5286 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5288 SgenThreadInfo *info;
5290 int old_errno = errno;
5292 MonoContext monoctx;
5294 gpointer regs [ARCH_NUM_REGS];
5296 gpointer stack_start;
5298 info = mono_thread_info_current ();
5300 /* This can happen while a thread is dying */
5303 info->stopped_domain = mono_domain_get ();
5304 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5305 stop_count = global_stop_count;
5306 /* duplicate signal */
5307 if (0 && info->stop_count == stop_count) {
5311 #ifdef HAVE_KW_THREAD
5312 /* update the remset info in the thread data structure */
5313 info->remset = remembered_set;
5315 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5316 /* If stack_start is not within the limits, then don't set it
5317 in info and we will be restarted. */
5318 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5319 info->stack_start = stack_start;
5322 mono_sigctx_to_monoctx (context, &monoctx);
5323 info->monoctx = &monoctx;
5325 ARCH_COPY_SIGCTX_REGS (regs, context);
5326 info->stopped_regs = regs;
5329 g_assert (!info->stack_start);
5332 /* Notify the JIT */
5333 if (gc_callbacks.thread_suspend_func)
5334 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5336 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
5337 /* notify the waiting thread */
5338 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5339 info->stop_count = stop_count;
5341 /* wait until we receive the restart signal */
5344 sigsuspend (&suspend_signal_mask);
5345 } while (info->signal != restart_signal_num);
5347 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
5348 /* notify the waiting thread */
5349 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5355 restart_handler (int sig)
5357 SgenThreadInfo *info;
5358 int old_errno = errno;
5360 info = mono_thread_info_current ();
5361 info->signal = restart_signal_num;
5362 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
5368 acquire_gc_locks (void)
5374 release_gc_locks (void)
5376 UNLOCK_INTERRUPTION;
5379 static TV_DECLARE (stop_world_time);
5380 static unsigned long max_pause_usec = 0;
5382 /* LOCKING: assumes the GC lock is held */
5384 stop_world (int generation)
5388 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
5389 acquire_gc_locks ();
5391 update_current_thread_stack (&count);
5393 global_stop_count++;
5394 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 ()));
5395 TV_GETTIME (stop_world_time);
5396 count = mono_sgen_thread_handshake (suspend_signal_num);
5397 count -= restart_threads_until_none_in_managed_allocator ();
5398 g_assert (count >= 0);
5399 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5400 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
5404 /* LOCKING: assumes the GC lock is held */
5406 restart_world (int generation)
5409 SgenThreadInfo *info;
5410 TV_DECLARE (end_sw);
5413 /* notify the profiler of the leftovers */
5414 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5415 if (moved_objects_idx) {
5416 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5417 moved_objects_idx = 0;
5420 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
5421 FOREACH_THREAD (info) {
5422 info->stack_start = NULL;
5424 info->monoctx = NULL;
5426 info->stopped_regs = NULL;
5428 } END_FOREACH_THREAD
5430 release_gc_locks ();
5432 count = mono_sgen_thread_handshake (restart_signal_num);
5433 TV_GETTIME (end_sw);
5434 usec = TV_ELAPSED (stop_world_time, end_sw);
5435 max_pause_usec = MAX (usec, max_pause_usec);
5436 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5437 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
5441 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5444 mono_sgen_get_current_collection_generation (void)
5446 return current_collection_generation;
5450 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5452 gc_callbacks = *callbacks;
5456 mono_gc_get_gc_callbacks ()
5458 return &gc_callbacks;
5461 /* Variables holding start/end nursery so it won't have to be passed at every call */
5462 static void *scan_area_arg_start, *scan_area_arg_end;
5465 mono_gc_conservatively_scan_area (void *start, void *end)
5467 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5471 mono_gc_scan_object (void *obj)
5473 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
5475 if (current_collection_generation == GENERATION_NURSERY)
5476 major_collector.copy_object (&obj, data->queue);
5478 major_collector.copy_or_mark_object (&obj, data->queue);
5483 * Mark from thread stacks and registers.
5486 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
5488 SgenThreadInfo *info;
5490 scan_area_arg_start = start_nursery;
5491 scan_area_arg_end = end_nursery;
5493 FOREACH_THREAD (info) {
5495 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));
5498 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));
5499 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
5500 UserCopyOrMarkData data = { NULL, queue };
5501 set_user_copy_or_mark_data (&data);
5502 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5503 set_user_copy_or_mark_data (NULL);
5504 } else if (!precise) {
5505 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5510 conservatively_pin_objects_from ((void**)info->monoctx, (void**)info->monoctx + ARCH_NUM_REGS,
5511 start_nursery, end_nursery, PIN_TYPE_STACK);
5514 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5515 start_nursery, end_nursery, PIN_TYPE_STACK);
5517 } END_FOREACH_THREAD
5521 find_pinning_ref_from_thread (char *obj, size_t size)
5524 SgenThreadInfo *info;
5525 char *endobj = obj + size;
5527 FOREACH_THREAD (info) {
5528 char **start = (char**)info->stack_start;
5531 while (start < (char**)info->stack_end) {
5532 if (*start >= obj && *start < endobj) {
5533 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));
5538 for (j = 0; j < ARCH_NUM_REGS; ++j) {
5540 mword w = ((mword*)info->monoctx) [j];
5542 mword w = (mword)info->stopped_regs [j];
5545 if (w >= (mword)obj && w < (mword)obj + size)
5546 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)));
5547 } END_FOREACH_THREAD
5552 ptr_on_stack (void *ptr)
5554 gpointer stack_start = &stack_start;
5555 SgenThreadInfo *info = mono_thread_info_current ();
5557 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5563 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
5570 HEAVY_STAT (++stat_global_remsets_processed);
5572 HEAVY_STAT (++stat_local_remsets_processed);
5574 /* FIXME: exclude stack locations */
5575 switch ((*p) & REMSET_TYPE_MASK) {
5576 case REMSET_LOCATION:
5578 //__builtin_prefetch (ptr);
5579 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5580 gpointer old = *ptr;
5581 major_collector.copy_object (ptr, queue);
5582 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5584 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5585 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5587 * If the object is pinned, each reference to it from nonpinned objects
5588 * becomes part of the global remset, which can grow very large.
5590 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5591 mono_sgen_add_to_global_remset (queue->allocator, ptr);
5594 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5598 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5599 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5602 while (count-- > 0) {
5603 major_collector.copy_object (ptr, queue);
5604 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5605 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5606 mono_sgen_add_to_global_remset (queue->allocator, ptr);
5611 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5612 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5614 major_collector.minor_scan_object ((char*)ptr, queue);
5616 case REMSET_VTYPE: {
5617 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5618 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5623 ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
5627 g_assert_not_reached ();
5632 #ifdef HEAVY_STATISTICS
5634 collect_store_remsets (RememberedSet *remset, mword *bumper)
5636 mword *p = remset->data;
5641 while (p < remset->store_next) {
5642 switch ((*p) & REMSET_TYPE_MASK) {
5643 case REMSET_LOCATION:
5646 ++stat_saved_remsets_1;
5648 if (*p == last1 || *p == last2) {
5649 ++stat_saved_remsets_2;
5666 g_assert_not_reached ();
5676 RememberedSet *remset;
5678 SgenThreadInfo *info;
5680 mword *addresses, *bumper, *p, *r;
5682 FOREACH_THREAD (info) {
5683 for (remset = info->remset; remset; remset = remset->next)
5684 size += remset->store_next - remset->data;
5685 } END_FOREACH_THREAD
5686 for (remset = freed_thread_remsets; remset; remset = remset->next)
5687 size += remset->store_next - remset->data;
5688 for (remset = global_remset; remset; remset = remset->next)
5689 size += remset->store_next - remset->data;
5691 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5693 FOREACH_THREAD (info) {
5694 for (remset = info->remset; remset; remset = remset->next)
5695 bumper = collect_store_remsets (remset, bumper);
5696 } END_FOREACH_THREAD
5697 for (remset = global_remset; remset; remset = remset->next)
5698 bumper = collect_store_remsets (remset, bumper);
5699 for (remset = freed_thread_remsets; remset; remset = remset->next)
5700 bumper = collect_store_remsets (remset, bumper);
5702 g_assert (bumper <= addresses + size);
5704 stat_store_remsets += bumper - addresses;
5706 sort_addresses ((void**)addresses, bumper - addresses);
5709 while (r < bumper) {
5715 stat_store_remsets_unique += p - addresses;
5717 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5722 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5724 *info->store_remset_buffer_index_addr = 0;
5725 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5729 remset_byte_size (RememberedSet *remset)
5731 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5735 scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5737 RememberedSet *remset;
5738 mword *p, *next_p, *store_pos;
5740 /* the global one */
5741 for (remset = global_remset; remset; remset = remset->next) {
5742 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));
5743 store_pos = remset->data;
5744 for (p = remset->data; p < remset->store_next; p = next_p) {
5745 void **ptr = (void**)p [0];
5747 /*Ignore previously processed remset.*/
5748 if (!global_remset_location_was_not_added (ptr)) {
5753 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5756 * Clear global remsets of locations which no longer point to the
5757 * nursery. Otherwise, they could grow indefinitely between major
5760 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5762 if (ptr_in_nursery (*ptr)) {
5763 *store_pos ++ = p [0];
5764 HEAVY_STAT (++stat_global_remsets_readded);
5768 /* Truncate the remset */
5769 remset->store_next = store_pos;
5774 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5777 SgenThreadInfo *info;
5778 RememberedSet *remset;
5779 GenericStoreRememberedSet *store_remset;
5782 #ifdef HEAVY_STATISTICS
5786 /* the generic store ones */
5787 store_remset = generic_store_remsets;
5788 while (store_remset) {
5789 GenericStoreRememberedSet *next = store_remset->next;
5791 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5792 gpointer addr = store_remset->data [i];
5794 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5797 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5799 store_remset = next;
5801 generic_store_remsets = NULL;
5803 /* the per-thread ones */
5804 FOREACH_THREAD (info) {
5805 RememberedSet *next;
5807 for (remset = info->remset; remset; remset = next) {
5808 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));
5809 for (p = remset->data; p < remset->store_next;)
5810 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5811 remset->store_next = remset->data;
5812 next = remset->next;
5813 remset->next = NULL;
5814 if (remset != info->remset) {
5815 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5816 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5819 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5820 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5821 clear_thread_store_remset_buffer (info);
5822 } END_FOREACH_THREAD
5824 /* the freed thread ones */
5825 while (freed_thread_remsets) {
5826 RememberedSet *next;
5827 remset = freed_thread_remsets;
5828 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));
5829 for (p = remset->data; p < remset->store_next;)
5830 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5831 next = remset->next;
5832 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5833 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5834 freed_thread_remsets = next;
5839 * Clear the info in the remembered sets: we're doing a major collection, so
5840 * the per-thread ones are not needed and the global ones will be reconstructed
5844 clear_remsets (void)
5846 SgenThreadInfo *info;
5847 RememberedSet *remset, *next;
5849 /* the global list */
5850 for (remset = global_remset; remset; remset = next) {
5851 remset->store_next = remset->data;
5852 next = remset->next;
5853 remset->next = NULL;
5854 if (remset != global_remset) {
5855 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5856 mono_sgen_free_internal_dynamic_delayed (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET,
5857 mono_sgen_get_unmanaged_allocator ());
5860 /* the generic store ones */
5861 while (generic_store_remsets) {
5862 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5863 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5864 generic_store_remsets = gs_next;
5866 /* the per-thread ones */
5867 FOREACH_THREAD (info) {
5868 for (remset = info->remset; remset; remset = next) {
5869 remset->store_next = remset->data;
5870 next = remset->next;
5871 remset->next = NULL;
5872 if (remset != info->remset) {
5873 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5874 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5877 clear_thread_store_remset_buffer (info);
5878 } END_FOREACH_THREAD
5880 /* the freed thread ones */
5881 while (freed_thread_remsets) {
5882 next = freed_thread_remsets->next;
5883 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5884 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5885 freed_thread_remsets = next;
5890 * Clear the thread local TLAB variables for all threads.
5895 SgenThreadInfo *info;
5897 FOREACH_THREAD (info) {
5898 /* A new TLAB will be allocated when the thread does its first allocation */
5899 *info->tlab_start_addr = NULL;
5900 *info->tlab_next_addr = NULL;
5901 *info->tlab_temp_end_addr = NULL;
5902 *info->tlab_real_end_addr = NULL;
5903 } END_FOREACH_THREAD
5907 sgen_thread_register (SgenThreadInfo* info, void *addr)
5909 #ifndef HAVE_KW_THREAD
5910 SgenThreadInfo *__thread_info__ = info;
5914 #ifndef HAVE_KW_THREAD
5915 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5917 g_assert (!pthread_getspecific (thread_info_key));
5918 pthread_setspecific (thread_info_key, info);
5923 info->stop_count = -1;
5926 info->stack_start = NULL;
5927 info->tlab_start_addr = &TLAB_START;
5928 info->tlab_next_addr = &TLAB_NEXT;
5929 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5930 info->tlab_real_end_addr = &TLAB_REAL_END;
5931 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5932 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5933 info->stopped_ip = NULL;
5934 info->stopped_domain = NULL;
5936 info->monoctx = NULL;
5938 info->stopped_regs = NULL;
5941 binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
5943 #ifdef HAVE_KW_THREAD
5944 tlab_next_addr = &tlab_next;
5945 store_remset_buffer_index_addr = &store_remset_buffer_index;
5948 #if defined(__MACH__)
5949 info->mach_port = mach_thread_self ();
5952 /* try to get it with attributes first */
5953 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5957 pthread_attr_t attr;
5958 pthread_getattr_np (pthread_self (), &attr);
5959 pthread_attr_getstack (&attr, &sstart, &size);
5960 info->stack_start_limit = sstart;
5961 info->stack_end = (char*)sstart + size;
5962 pthread_attr_destroy (&attr);
5964 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5965 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5966 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5969 /* FIXME: we assume the stack grows down */
5970 gsize stack_bottom = (gsize)addr;
5971 stack_bottom += 4095;
5972 stack_bottom &= ~4095;
5973 info->stack_end = (char*)stack_bottom;
5977 #ifdef HAVE_KW_THREAD
5978 stack_end = info->stack_end;
5981 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
5982 pthread_setspecific (remembered_set_key, info->remset);
5983 #ifdef HAVE_KW_THREAD
5984 remembered_set = info->remset;
5987 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5988 STORE_REMSET_BUFFER_INDEX = 0;
5990 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p)\n", info, (gpointer)mono_thread_info_get_tid (info)));
5992 if (gc_callbacks.thread_attach_func)
5993 info->runtime_data = gc_callbacks.thread_attach_func ();
6000 add_generic_store_remset_from_buffer (gpointer *buffer)
6002 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
6003 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6004 remset->next = generic_store_remsets;
6005 generic_store_remsets = remset;
6009 sgen_thread_unregister (SgenThreadInfo *p)
6011 RememberedSet *rset;
6013 /* If a delegate is passed to native code and invoked on a thread we dont
6014 * know about, the jit will register it with mono_jit_thread_attach, but
6015 * we have no way of knowing when that thread goes away. SGen has a TSD
6016 * so we assume that if the domain is still registered, we can detach
6019 if (mono_domain_get ())
6020 mono_thread_detach (mono_thread_current ());
6024 binary_protocol_thread_unregister ((gpointer)id);
6025 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)mono_thread_info_get_tid (p)));
6027 #if defined(__MACH__)
6028 mach_port_deallocate (current_task (), p->mach_port);
6031 if (gc_callbacks.thread_detach_func) {
6032 gc_callbacks.thread_detach_func (p->runtime_data);
6033 p->runtime_data = NULL;
6037 if (freed_thread_remsets) {
6038 for (rset = p->remset; rset->next; rset = rset->next)
6040 rset->next = freed_thread_remsets;
6041 freed_thread_remsets = p->remset;
6043 freed_thread_remsets = p->remset;
6046 if (*p->store_remset_buffer_index_addr)
6047 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6048 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6054 sgen_thread_attach (SgenThreadInfo *info)
6057 /*this is odd, can we get attached before the gc is inited?*/
6061 if (gc_callbacks.thread_attach_func && !info->runtime_data)
6062 info->runtime_data = gc_callbacks.thread_attach_func ();
6064 /* Need a better place to initialize this */
6065 if (!array_fill_vtable && mono_get_root_domain ()) {
6066 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
6071 mono_gc_register_thread (void *baseptr)
6073 return mono_thread_info_attach (baseptr) != NULL;
6077 * mono_gc_set_stack_end:
6079 * Set the end of the current threads stack to STACK_END. The stack space between
6080 * STACK_END and the real end of the threads stack will not be scanned during collections.
6083 mono_gc_set_stack_end (void *stack_end)
6085 SgenThreadInfo *info;
6088 info = mono_thread_info_current ();
6090 g_assert (stack_end < info->stack_end);
6091 info->stack_end = stack_end;
6096 #if USE_PTHREAD_INTERCEPT
6100 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6102 return mono_threads_pthread_create (new_thread, attr, start_routine, arg);
6106 mono_gc_pthread_join (pthread_t thread, void **retval)
6108 return pthread_join (thread, retval);
6112 mono_gc_pthread_detach (pthread_t thread)
6114 return pthread_detach (thread);
6117 #endif /* USE_PTHREAD_INTERCEPT */
6120 * ######################################################################
6121 * ######## Write barriers
6122 * ######################################################################
6126 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
6129 dummy_use (gpointer v) {
6130 __asm__ volatile ("" : "=r"(v) : "r"(v));
6134 static RememberedSet*
6135 alloc_remset (int size, gpointer id) {
6136 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6137 res->store_next = res->data;
6138 res->end_set = res->data + size;
6140 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6144 static RememberedSet*
6145 alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id)
6147 RememberedSet* res = mono_sgen_alloc_internal_full (alc, sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6148 res->store_next = res->data;
6149 res->end_set = res->data + size;
6151 DEBUG (4, fprintf (gc_debug_file, "Allocated global remset size %d at %p for %p\n", size, res->data, id));
6156 * Note: the write barriers first do the needed GC work and then do the actual store:
6157 * this way the value is visible to the conservative GC scan after the write barrier
6158 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6159 * the conservative scan, otherwise by the remembered set scan.
6162 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6164 HEAVY_STAT (++stat_wbarrier_set_field);
6165 if (ptr_in_nursery (field_ptr)) {
6166 *(void**)field_ptr = value;
6169 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6170 if (use_cardtable) {
6171 *(void**)field_ptr = value;
6172 if (ptr_in_nursery (value))
6173 sgen_card_table_mark_address ((mword)field_ptr);
6180 rs = REMEMBERED_SET;
6181 if (rs->store_next < rs->end_set) {
6182 *(rs->store_next++) = (mword)field_ptr;
6183 *(void**)field_ptr = value;
6187 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6188 rs->next = REMEMBERED_SET;
6189 REMEMBERED_SET = rs;
6190 #ifdef HAVE_KW_THREAD
6191 mono_thread_info_current ()->remset = rs;
6193 *(rs->store_next++) = (mword)field_ptr;
6194 *(void**)field_ptr = value;
6200 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6202 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6203 if (ptr_in_nursery (slot_ptr)) {
6204 *(void**)slot_ptr = value;
6207 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6208 if (use_cardtable) {
6209 *(void**)slot_ptr = value;
6210 if (ptr_in_nursery (value))
6211 sgen_card_table_mark_address ((mword)slot_ptr);
6218 rs = REMEMBERED_SET;
6219 if (rs->store_next < rs->end_set) {
6220 *(rs->store_next++) = (mword)slot_ptr;
6221 *(void**)slot_ptr = value;
6225 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6226 rs->next = REMEMBERED_SET;
6227 REMEMBERED_SET = rs;
6228 #ifdef HAVE_KW_THREAD
6229 mono_thread_info_current ()->remset = rs;
6231 *(rs->store_next++) = (mword)slot_ptr;
6232 *(void**)slot_ptr = value;
6238 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6240 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6241 /*This check can be done without taking a lock since dest_ptr array is pinned*/
6242 if (ptr_in_nursery (dest_ptr) || count <= 0) {
6243 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6247 if (use_cardtable) {
6248 gpointer *dest = dest_ptr;
6249 gpointer *src = src_ptr;
6251 /*overlapping that required backward copying*/
6252 if (src < dest && (src + count) > dest) {
6253 gpointer *start = dest;
6257 for (; dest >= start; --src, --dest) {
6258 gpointer value = *src;
6260 if (ptr_in_nursery (value))
6261 sgen_card_table_mark_address ((mword)dest);
6265 gpointer *end = dest + count;
6266 for (; dest < end; ++src, ++dest) {
6267 gpointer value = *src;
6269 if (ptr_in_nursery (value))
6270 sgen_card_table_mark_address ((mword)dest);
6278 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6280 rs = REMEMBERED_SET;
6281 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6282 if (rs->store_next + 1 < rs->end_set) {
6283 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6284 *(rs->store_next++) = count;
6288 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6289 rs->next = REMEMBERED_SET;
6290 REMEMBERED_SET = rs;
6291 #ifdef HAVE_KW_THREAD
6292 mono_thread_info_current ()->remset = rs;
6294 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6295 *(rs->store_next++) = count;
6301 static char *found_obj;
6304 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
6306 char *ptr = user_data;
6308 if (ptr >= obj && ptr < obj + size) {
6309 g_assert (!found_obj);
6314 /* for use in the debugger */
6315 char* find_object_for_ptr (char *ptr);
6317 find_object_for_ptr (char *ptr)
6319 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6321 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6322 find_object_for_ptr_callback, ptr, TRUE);
6328 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
6333 * Very inefficient, but this is debugging code, supposed to
6334 * be called from gdb, so we don't care.
6337 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
6342 evacuate_remset_buffer (void)
6347 buffer = STORE_REMSET_BUFFER;
6349 add_generic_store_remset_from_buffer (buffer);
6350 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6352 STORE_REMSET_BUFFER_INDEX = 0;
6356 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6362 HEAVY_STAT (++stat_wbarrier_generic_store);
6364 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6365 /* FIXME: ptr_in_heap must be called with the GC lock held */
6366 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6367 char *start = find_object_for_ptr (ptr);
6368 MonoObject *value = *(MonoObject**)ptr;
6372 MonoObject *obj = (MonoObject*)start;
6373 if (obj->vtable->domain != value->vtable->domain)
6374 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6380 if (*(gpointer*)ptr)
6381 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6383 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6384 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6388 if (use_cardtable) {
6389 if (ptr_in_nursery(*(gpointer*)ptr))
6390 sgen_card_table_mark_address ((mword)ptr);
6396 buffer = STORE_REMSET_BUFFER;
6397 index = STORE_REMSET_BUFFER_INDEX;
6398 /* This simple optimization eliminates a sizable portion of
6399 entries. Comparing it to the last but one entry as well
6400 doesn't eliminate significantly more entries. */
6401 if (buffer [index] == ptr) {
6406 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6407 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6410 if (index >= STORE_REMSET_BUFFER_SIZE) {
6411 evacuate_remset_buffer ();
6412 index = STORE_REMSET_BUFFER_INDEX;
6413 g_assert (index == 0);
6416 buffer [index] = ptr;
6417 STORE_REMSET_BUFFER_INDEX = index;
6423 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6425 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6426 *(void**)ptr = value;
6427 if (ptr_in_nursery (value))
6428 mono_gc_wbarrier_generic_nostore (ptr);
6432 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
6434 mword *dest = _dest;
6439 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
6444 size -= SIZEOF_VOID_P;
6451 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6454 size_t size = count * mono_class_value_size (klass, NULL);
6456 HEAVY_STAT (++stat_wbarrier_value_copy);
6457 g_assert (klass->valuetype);
6459 memmove (dest, src, size);
6460 if (use_cardtable) {
6461 sgen_card_table_mark_range ((mword)dest, size);
6463 rs = REMEMBERED_SET;
6464 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
6468 g_assert (klass->gc_descr_inited);
6469 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));
6471 if (rs->store_next + 3 < rs->end_set) {
6472 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6473 *(rs->store_next++) = (mword)klass->gc_descr;
6474 *(rs->store_next++) = (mword)count;
6478 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6479 rs->next = REMEMBERED_SET;
6480 REMEMBERED_SET = rs;
6481 #ifdef HAVE_KW_THREAD
6482 mono_thread_info_current ()->remset = rs;
6484 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6485 *(rs->store_next++) = (mword)klass->gc_descr;
6486 *(rs->store_next++) = (mword)count;
6492 * mono_gc_wbarrier_object_copy:
6494 * Write barrier to call when obj is the result of a clone or copy of an object.
6497 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6503 HEAVY_STAT (++stat_wbarrier_object_copy);
6504 rs = REMEMBERED_SET;
6505 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6506 size = mono_object_class (obj)->instance_size;
6508 /* do not copy the sync state */
6509 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6510 size - sizeof (MonoObject));
6511 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6515 if (rs->store_next < rs->end_set) {
6516 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6520 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6521 rs->next = REMEMBERED_SET;
6522 REMEMBERED_SET = rs;
6524 #ifdef HAVE_KW_THREAD
6525 mono_thread_info_current ()->remset = rs;
6527 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6532 * ######################################################################
6533 * ######## Collector debugging
6534 * ######################################################################
6537 const char*descriptor_types [] = {
6549 describe_ptr (char *ptr)
6556 if (ptr_in_nursery (ptr)) {
6557 printf ("Pointer inside nursery.\n");
6559 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
6561 printf ("Pointer is the start of object %p in LOS space.\n", start);
6563 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
6565 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
6566 printf ("Pointer inside oldspace.\n");
6567 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
6568 printf ("Pointer is inside a pinned chunk.\n");
6570 printf ("Pointer unknown.\n");
6575 if (object_is_pinned (ptr))
6576 printf ("Object is pinned.\n");
6578 if (object_is_forwarded (ptr))
6579 printf ("Object is forwared.\n");
6581 // FIXME: Handle pointers to the inside of objects
6582 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6584 printf ("VTable: %p\n", vtable);
6585 if (vtable == NULL) {
6586 printf ("VTable is invalid (empty).\n");
6589 if (ptr_in_nursery (vtable)) {
6590 printf ("VTable is invalid (points inside nursery).\n");
6593 printf ("Class: %s\n", vtable->klass->name);
6595 desc = ((GCVTable*)vtable)->desc;
6596 printf ("Descriptor: %lx\n", (long)desc);
6599 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6603 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6609 switch ((*p) & REMSET_TYPE_MASK) {
6610 case REMSET_LOCATION:
6611 if (*p == (mword)addr)
6615 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6617 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6621 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6622 count = safe_object_get_size ((MonoObject*)ptr);
6623 count = ALIGN_UP (count);
6624 count /= sizeof (mword);
6625 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6629 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6633 switch (desc & 0x7) {
6634 case DESC_TYPE_RUN_LENGTH:
6635 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6637 case DESC_TYPE_SMALL_BITMAP:
6638 OBJ_BITMAP_SIZE (skip_size, desc, start);
6642 g_assert_not_reached ();
6645 /* The descriptor includes the size of MonoObject */
6646 skip_size -= sizeof (MonoObject);
6648 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6653 g_assert_not_reached ();
6659 * Return whenever ADDR occurs in the remembered sets
6662 find_in_remsets (char *addr)
6665 SgenThreadInfo *info;
6666 RememberedSet *remset;
6667 GenericStoreRememberedSet *store_remset;
6669 gboolean found = FALSE;
6671 /* the global one */
6672 for (remset = global_remset; remset; remset = remset->next) {
6673 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));
6674 for (p = remset->data; p < remset->store_next;) {
6675 p = find_in_remset_loc (p, addr, &found);
6681 /* the generic store ones */
6682 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6683 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6684 if (store_remset->data [i] == addr)
6689 /* the per-thread ones */
6690 FOREACH_THREAD (info) {
6692 for (remset = info->remset; remset; remset = remset->next) {
6693 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));
6694 for (p = remset->data; p < remset->store_next;) {
6695 p = find_in_remset_loc (p, addr, &found);
6700 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6701 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6704 } END_FOREACH_THREAD
6706 /* the freed thread ones */
6707 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6708 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));
6709 for (p = remset->data; p < remset->store_next;) {
6710 p = find_in_remset_loc (p, addr, &found);
6719 static gboolean missing_remsets;
6722 * We let a missing remset slide if the target object is pinned,
6723 * because the store might have happened but the remset not yet added,
6724 * but in that case the target must be pinned. We might theoretically
6725 * miss some missing remsets this way, but it's very unlikely.
6728 #define HANDLE_PTR(ptr,obj) do { \
6729 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6730 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6731 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); \
6732 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6733 if (!object_is_pinned (*(ptr))) \
6734 missing_remsets = TRUE; \
6740 * Check that each object reference which points into the nursery can
6741 * be found in the remembered sets.
6744 check_consistency_callback (char *start, size_t size, void *dummy)
6746 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6747 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6749 #define SCAN_OBJECT_ACTION
6750 #include "sgen-scan-object.h"
6754 * Perform consistency check of the heap.
6756 * Assumes the world is stopped.
6759 check_consistency (void)
6761 // Need to add more checks
6763 missing_remsets = FALSE;
6765 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6767 // Check that oldspace->newspace pointers are registered with the collector
6768 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6770 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6772 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6774 if (!binary_protocol_is_enabled ())
6775 g_assert (!missing_remsets);
6780 #define HANDLE_PTR(ptr,obj) do { \
6781 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6782 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6786 check_major_refs_callback (char *start, size_t size, void *dummy)
6788 #define SCAN_OBJECT_ACTION
6789 #include "sgen-scan-object.h"
6793 check_major_refs (void)
6795 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6796 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6799 /* Check that the reference is valid */
6801 #define HANDLE_PTR(ptr,obj) do { \
6803 g_assert (safe_name (*(ptr)) != NULL); \
6810 * Perform consistency check on an object. Currently we only check that the
6811 * reference fields are valid.
6814 check_object (char *start)
6819 #include "sgen-scan-object.h"
6823 * ######################################################################
6824 * ######## Other mono public interface functions.
6825 * ######################################################################
6828 #define REFS_SIZE 128
6831 MonoGCReferences callback;
6835 MonoObject *refs [REFS_SIZE];
6836 uintptr_t offsets [REFS_SIZE];
6840 #define HANDLE_PTR(ptr,obj) do { \
6842 if (hwi->count == REFS_SIZE) { \
6843 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
6847 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
6848 hwi->refs [hwi->count++] = *(ptr); \
6853 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6855 #include "sgen-scan-object.h"
6859 walk_references (char *start, size_t size, void *data)
6861 HeapWalkInfo *hwi = data;
6864 collect_references (hwi, start, size);
6865 if (hwi->count || !hwi->called)
6866 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6870 * mono_gc_walk_heap:
6871 * @flags: flags for future use
6872 * @callback: a function pointer called for each object in the heap
6873 * @data: a user data pointer that is passed to callback
6875 * This function can be used to iterate over all the live objects in the heap:
6876 * for each object, @callback is invoked, providing info about the object's
6877 * location in memory, its class, its size and the objects it references.
6878 * For each referenced object it's offset from the object address is
6879 * reported in the offsets array.
6880 * The object references may be buffered, so the callback may be invoked
6881 * multiple times for the same object: in all but the first call, the size
6882 * argument will be zero.
6883 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6884 * profiler event handler.
6886 * Returns: a non-zero value if the GC doesn't support heap walking
6889 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6894 hwi.callback = callback;
6897 clear_nursery_fragments (nursery_next);
6898 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
6900 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
6901 mono_sgen_los_iterate_objects (walk_references, &hwi);
6907 mono_gc_collect (int generation)
6912 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6913 stop_world (generation);
6914 if (generation == 0) {
6915 collect_nursery (0);
6917 major_collection ("user request");
6919 restart_world (generation);
6920 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6925 mono_gc_max_generation (void)
6931 mono_gc_collection_count (int generation)
6933 if (generation == 0)
6934 return num_minor_gcs;
6935 return num_major_gcs;
6939 mono_gc_get_used_size (void)
6943 tot = los_memory_usage;
6944 tot += nursery_section->next_data - nursery_section->data;
6945 tot += major_collector.get_used_size ();
6946 /* FIXME: account for pinned objects */
6952 mono_gc_get_heap_size (void)
6958 mono_gc_disable (void)
6966 mono_gc_enable (void)
6974 mono_gc_get_los_limit (void)
6976 return MAX_SMALL_OBJ_SIZE;
6980 mono_object_is_alive (MonoObject* o)
6986 mono_gc_get_generation (MonoObject *obj)
6988 if (ptr_in_nursery (obj))
6994 mono_gc_enable_events (void)
6999 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7002 mono_gc_register_disappearing_link (obj, link_addr, track);
7007 mono_gc_weak_link_remove (void **link_addr)
7010 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7015 mono_gc_weak_link_get (void **link_addr)
7019 return (MonoObject*) REVEAL_POINTER (*link_addr);
7023 mono_gc_ephemeron_array_add (MonoObject *obj)
7025 EphemeronLinkNode *node;
7029 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
7034 node->array = (char*)obj;
7035 node->next = ephemeron_list;
7036 ephemeron_list = node;
7038 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
7045 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7048 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
7049 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7050 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7052 mword complex = alloc_complex_descriptor (bitmap, numbits);
7053 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7057 static void *all_ref_root_descrs [32];
7060 mono_gc_make_root_descr_all_refs (int numbits)
7064 int num_bytes = numbits / 8;
7066 if (numbits < 32 && all_ref_root_descrs [numbits])
7067 return all_ref_root_descrs [numbits];
7069 gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
7070 memset (gc_bitmap, 0xff, num_bytes);
7071 if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT))
7072 gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
7073 else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
7074 gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
7076 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
7077 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
7081 all_ref_root_descrs [numbits] = descr;
7087 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7091 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7092 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7093 user_descriptors [user_descriptors_next ++] = marker;
7099 mono_gc_alloc_fixed (size_t size, void *descr)
7101 /* FIXME: do a single allocation */
7102 void *res = calloc (1, size);
7105 if (!mono_gc_register_root (res, size, descr)) {
7113 mono_gc_free_fixed (void* addr)
7115 mono_gc_deregister_root (addr);
7120 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7124 result = func (data);
7125 UNLOCK_INTERRUPTION;
7130 mono_gc_is_gc_thread (void)
7134 result = mono_thread_info_current () != NULL;
7140 mono_gc_base_init (void)
7142 MonoThreadInfoCallbacks cb;
7145 char *major_collector_opt = NULL;
7146 struct sigaction sinfo;
7150 /* the gc_initialized guard seems to imply this method is
7151 idempotent, but LOCK_INIT(gc_mutex) might not be. It's
7152 defined in sgen-gc.h as nothing, so there's no danger at
7154 LOCK_INIT (gc_mutex);
7156 if (gc_initialized) {
7160 pagesize = mono_pagesize ();
7161 gc_debug_file = stdout;
7163 cb.thread_register = sgen_thread_register;
7164 cb.thread_unregister = sgen_thread_unregister;
7165 cb.thread_attach = sgen_thread_attach;
7166 mono_threads_init (&cb, sizeof (SgenThreadInfo));
7168 LOCK_INIT (interruption_mutex);
7169 LOCK_INIT (global_remset_mutex);
7170 LOCK_INIT (pin_queue_mutex);
7172 if ((env = getenv ("MONO_GC_PARAMS"))) {
7173 opts = g_strsplit (env, ",", -1);
7174 for (ptr = opts; *ptr; ++ptr) {
7176 if (g_str_has_prefix (opt, "major=")) {
7177 opt = strchr (opt, '=') + 1;
7178 major_collector_opt = g_strdup (opt);
7186 mono_sgen_init_internal_allocator ();
7188 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
7189 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
7190 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
7191 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
7192 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
7193 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
7194 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
7195 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
7196 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
7198 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
7199 mono_sgen_marksweep_init (&major_collector);
7200 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
7201 mono_sgen_marksweep_fixed_init (&major_collector);
7202 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
7203 mono_sgen_marksweep_par_init (&major_collector);
7204 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
7205 mono_sgen_marksweep_fixed_par_init (&major_collector);
7206 } else if (!strcmp (major_collector_opt, "copying")) {
7207 mono_sgen_copying_init (&major_collector);
7209 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
7213 #ifdef SGEN_HAVE_CARDTABLE
7214 use_cardtable = major_collector.supports_cardtable;
7216 use_cardtable = FALSE;
7219 num_workers = mono_cpu_count ();
7220 g_assert (num_workers > 0);
7221 if (num_workers > 16)
7224 /* Keep this the default for now */
7225 conservative_stack_mark = TRUE;
7228 for (ptr = opts; *ptr; ++ptr) {
7230 if (g_str_has_prefix (opt, "major="))
7232 if (g_str_has_prefix (opt, "wbarrier=")) {
7233 opt = strchr (opt, '=') + 1;
7234 if (strcmp (opt, "remset") == 0) {
7235 use_cardtable = FALSE;
7236 } else if (strcmp (opt, "cardtable") == 0) {
7237 if (!use_cardtable) {
7238 if (major_collector.supports_cardtable)
7239 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
7241 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
7247 if (g_str_has_prefix (opt, "max-heap-size=")) {
7248 opt = strchr (opt, '=') + 1;
7249 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
7250 if ((max_heap & (mono_pagesize () - 1))) {
7251 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
7255 fprintf (stderr, "max-heap-size must be an integer.\n");
7260 if (g_str_has_prefix (opt, "workers=")) {
7263 if (!major_collector.is_parallel) {
7264 fprintf (stderr, "The workers= option can only be used for parallel collectors.");
7267 opt = strchr (opt, '=') + 1;
7268 val = strtol (opt, &endptr, 10);
7269 if (!*opt || *endptr) {
7270 fprintf (stderr, "Cannot parse the workers= option value.");
7273 if (val <= 0 || val > 16) {
7274 fprintf (stderr, "The number of workers must be in the range 1 to 16.");
7277 num_workers = (int)val;
7280 if (g_str_has_prefix (opt, "stack-mark=")) {
7281 opt = strchr (opt, '=') + 1;
7282 if (!strcmp (opt, "precise")) {
7283 conservative_stack_mark = FALSE;
7284 } else if (!strcmp (opt, "conservative")) {
7285 conservative_stack_mark = TRUE;
7287 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
7293 if (g_str_has_prefix (opt, "nursery-size=")) {
7295 opt = strchr (opt, '=') + 1;
7296 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
7297 default_nursery_size = val;
7298 #ifdef SGEN_ALIGN_NURSERY
7299 if ((val & (val - 1))) {
7300 fprintf (stderr, "The nursery size must be a power of two.\n");
7304 default_nursery_bits = 0;
7305 while (1 << (++ default_nursery_bits) != default_nursery_size)
7309 fprintf (stderr, "nursery-size must be an integer.\n");
7315 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
7316 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
7317 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7318 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7319 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
7320 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
7321 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
7322 if (major_collector.print_gc_param_usage)
7323 major_collector.print_gc_param_usage ();
7330 if (major_collector.is_parallel)
7331 workers_init (num_workers);
7333 if (major_collector_opt)
7334 g_free (major_collector_opt);
7336 nursery_size = DEFAULT_NURSERY_SIZE;
7337 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7338 init_heap_size_limits (max_heap);
7342 if ((env = getenv ("MONO_GC_DEBUG"))) {
7343 opts = g_strsplit (env, ",", -1);
7344 for (ptr = opts; ptr && *ptr; ptr ++) {
7346 if (opt [0] >= '0' && opt [0] <= '9') {
7347 gc_debug_level = atoi (opt);
7352 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7353 gc_debug_file = fopen (rf, "wb");
7355 gc_debug_file = stderr;
7358 } else if (!strcmp (opt, "collect-before-allocs")) {
7359 collect_before_allocs = 1;
7360 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
7361 char *arg = strchr (opt, '=') + 1;
7362 collect_before_allocs = atoi (arg);
7363 } else if (!strcmp (opt, "check-at-minor-collections")) {
7364 consistency_check_at_minor_collection = TRUE;
7365 nursery_clear_policy = CLEAR_AT_GC;
7366 } else if (!strcmp (opt, "xdomain-checks")) {
7367 xdomain_checks = TRUE;
7368 } else if (!strcmp (opt, "clear-at-gc")) {
7369 nursery_clear_policy = CLEAR_AT_GC;
7370 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
7371 nursery_clear_policy = CLEAR_AT_GC;
7372 } else if (!strcmp (opt, "check-scan-starts")) {
7373 do_scan_starts_check = TRUE;
7374 } else if (!strcmp (opt, "disable-minor")) {
7375 disable_minor_collections = TRUE;
7376 } else if (!strcmp (opt, "disable-major")) {
7377 disable_major_collections = TRUE;
7378 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7379 char *filename = strchr (opt, '=') + 1;
7380 nursery_clear_policy = CLEAR_AT_GC;
7381 heap_dump_file = fopen (filename, "w");
7383 fprintf (heap_dump_file, "<sgen-dump>\n");
7384 #ifdef SGEN_BINARY_PROTOCOL
7385 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7386 char *filename = strchr (opt, '=') + 1;
7387 binary_protocol_init (filename);
7390 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7391 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7392 fprintf (stderr, "Valid options are:\n");
7393 fprintf (stderr, " collect-before-allocs[=<n>]\n");
7394 fprintf (stderr, " check-at-minor-collections\n");
7395 fprintf (stderr, " disable-minor\n");
7396 fprintf (stderr, " disable-major\n");
7397 fprintf (stderr, " xdomain-checks\n");
7398 fprintf (stderr, " clear-at-gc\n");
7405 if (major_collector.post_param_init)
7406 major_collector.post_param_init ();
7408 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7409 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7411 sigfillset (&sinfo.sa_mask);
7412 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7413 sinfo.sa_sigaction = suspend_handler;
7414 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7415 g_error ("failed sigaction");
7418 sinfo.sa_handler = restart_handler;
7419 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7420 g_error ("failed sigaction");
7423 sigfillset (&suspend_signal_mask);
7424 sigdelset (&suspend_signal_mask, restart_signal_num);
7426 global_remset = alloc_remset (1024, NULL);
7427 global_remset->next = NULL;
7429 pthread_key_create (&remembered_set_key, NULL);
7431 #ifndef HAVE_KW_THREAD
7432 pthread_key_create (&thread_info_key, NULL);
7438 gc_initialized = TRUE;
7440 mono_thread_info_attach (&sinfo);
7444 mono_gc_get_suspend_signal (void)
7446 return suspend_signal_num;
7456 #ifdef HAVE_KW_THREAD
7457 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7458 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7459 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7460 mono_mb_emit_i4 ((mb), (offset)); \
7465 * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
7466 * where the two are the same.
7469 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7470 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7471 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7472 mono_mb_emit_i4 ((mb), thread_info_key); \
7473 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7474 mono_mb_emit_byte ((mb), CEE_ADD); \
7475 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7478 #define EMIT_TLS_ACCESS(mb,member,dummy) do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
7483 #ifdef MANAGED_ALLOCATION
7484 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7485 * for each class. This is currently not easy to do, as it is hard to generate basic
7486 * blocks + branches, but it is easy with the linear IL codebase.
7488 * For this to work we'd need to solve the TLAB race, first. Now we
7489 * require the allocator to be in a few known methods to make sure
7490 * that they are executed atomically via the restart mechanism.
7493 create_allocator (int atype)
7495 int p_var, size_var;
7496 guint32 slowpath_branch, max_size_branch;
7497 MonoMethodBuilder *mb;
7499 MonoMethodSignature *csig;
7500 static gboolean registered = FALSE;
7501 int tlab_next_addr_var, new_next_var;
7503 const char *name = NULL;
7504 AllocatorWrapperInfo *info;
7506 #ifdef HAVE_KW_THREAD
7507 int tlab_next_addr_offset = -1;
7508 int tlab_temp_end_offset = -1;
7510 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7511 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7513 g_assert (tlab_next_addr_offset != -1);
7514 g_assert (tlab_temp_end_offset != -1);
7518 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7519 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7523 if (atype == ATYPE_SMALL) {
7525 name = "AllocSmall";
7526 } else if (atype == ATYPE_NORMAL) {
7529 } else if (atype == ATYPE_VECTOR) {
7531 name = "AllocVector";
7533 g_assert_not_reached ();
7536 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7537 csig->ret = &mono_defaults.object_class->byval_arg;
7538 for (i = 0; i < num_params; ++i)
7539 csig->params [i] = &mono_defaults.int_class->byval_arg;
7541 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7542 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7543 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7544 /* size = vtable->klass->instance_size; */
7545 mono_mb_emit_ldarg (mb, 0);
7546 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7547 mono_mb_emit_byte (mb, CEE_ADD);
7548 mono_mb_emit_byte (mb, CEE_LDIND_I);
7549 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7550 mono_mb_emit_byte (mb, CEE_ADD);
7551 /* FIXME: assert instance_size stays a 4 byte integer */
7552 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7553 mono_mb_emit_stloc (mb, size_var);
7554 } else if (atype == ATYPE_VECTOR) {
7555 MonoExceptionClause *clause;
7557 MonoClass *oom_exc_class;
7560 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7561 mono_mb_emit_ldarg (mb, 1);
7562 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7563 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7564 mono_mb_emit_exception (mb, "OverflowException", NULL);
7565 mono_mb_patch_short_branch (mb, pos);
7567 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7568 clause->try_offset = mono_mb_get_label (mb);
7570 /* vtable->klass->sizes.element_size */
7571 mono_mb_emit_ldarg (mb, 0);
7572 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7573 mono_mb_emit_byte (mb, CEE_ADD);
7574 mono_mb_emit_byte (mb, CEE_LDIND_I);
7575 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7576 mono_mb_emit_byte (mb, CEE_ADD);
7577 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7580 mono_mb_emit_ldarg (mb, 1);
7581 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7582 /* + sizeof (MonoArray) */
7583 mono_mb_emit_icon (mb, sizeof (MonoArray));
7584 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7585 mono_mb_emit_stloc (mb, size_var);
7587 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7590 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7591 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7592 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7593 "System", "OverflowException");
7594 g_assert (clause->data.catch_class);
7595 clause->handler_offset = mono_mb_get_label (mb);
7597 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7598 "System", "OutOfMemoryException");
7599 g_assert (oom_exc_class);
7600 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7603 mono_mb_emit_byte (mb, CEE_POP);
7604 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7605 mono_mb_emit_byte (mb, CEE_THROW);
7607 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7608 mono_mb_set_clauses (mb, 1, clause);
7609 mono_mb_patch_branch (mb, pos_leave);
7612 g_assert_not_reached ();
7615 /* size += ALLOC_ALIGN - 1; */
7616 mono_mb_emit_ldloc (mb, size_var);
7617 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7618 mono_mb_emit_byte (mb, CEE_ADD);
7619 /* size &= ~(ALLOC_ALIGN - 1); */
7620 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7621 mono_mb_emit_byte (mb, CEE_AND);
7622 mono_mb_emit_stloc (mb, size_var);
7624 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7625 if (atype != ATYPE_SMALL) {
7626 mono_mb_emit_ldloc (mb, size_var);
7627 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7628 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7632 * We need to modify tlab_next, but the JIT only supports reading, so we read
7633 * another tls var holding its address instead.
7636 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7637 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7638 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7639 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7641 /* p = (void**)tlab_next; */
7642 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7643 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7644 mono_mb_emit_byte (mb, CEE_LDIND_I);
7645 mono_mb_emit_stloc (mb, p_var);
7647 /* new_next = (char*)p + size; */
7648 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7649 mono_mb_emit_ldloc (mb, p_var);
7650 mono_mb_emit_ldloc (mb, size_var);
7651 mono_mb_emit_byte (mb, CEE_CONV_I);
7652 mono_mb_emit_byte (mb, CEE_ADD);
7653 mono_mb_emit_stloc (mb, new_next_var);
7655 /* tlab_next = new_next */
7656 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7657 mono_mb_emit_ldloc (mb, new_next_var);
7658 mono_mb_emit_byte (mb, CEE_STIND_I);
7660 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7661 mono_mb_emit_ldloc (mb, new_next_var);
7662 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7663 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7666 if (atype != ATYPE_SMALL)
7667 mono_mb_patch_short_branch (mb, max_size_branch);
7669 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7670 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7672 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7673 mono_mb_emit_ldarg (mb, 0);
7674 mono_mb_emit_ldloc (mb, size_var);
7675 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7676 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7677 } else if (atype == ATYPE_VECTOR) {
7678 mono_mb_emit_ldarg (mb, 1);
7679 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7681 g_assert_not_reached ();
7683 mono_mb_emit_byte (mb, CEE_RET);
7686 mono_mb_patch_short_branch (mb, slowpath_branch);
7688 /* FIXME: Memory barrier */
7691 mono_mb_emit_ldloc (mb, p_var);
7692 mono_mb_emit_ldarg (mb, 0);
7693 mono_mb_emit_byte (mb, CEE_STIND_I);
7695 if (atype == ATYPE_VECTOR) {
7696 /* arr->max_length = max_length; */
7697 mono_mb_emit_ldloc (mb, p_var);
7698 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7699 mono_mb_emit_ldarg (mb, 1);
7700 mono_mb_emit_byte (mb, CEE_STIND_I);
7704 mono_mb_emit_ldloc (mb, p_var);
7705 mono_mb_emit_byte (mb, CEE_RET);
7707 res = mono_mb_create_method (mb, csig, 8);
7709 mono_method_get_header (res)->init_locals = FALSE;
7711 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7712 info->gc_name = "sgen";
7713 info->alloc_type = atype;
7714 mono_marshal_set_wrapper_info (res, info);
7721 mono_gc_get_gc_name (void)
7726 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7727 static MonoMethod *write_barrier_method;
7730 mono_gc_is_critical_method (MonoMethod *method)
7733 if (method == write_barrier_method)
7736 for (i = 0; i < ATYPE_NUM; ++i)
7737 if (method == alloc_method_cache [i])
7744 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7748 if (!mono_thread_internal_current ())
7749 /* Happens during thread attach */
7754 ji = mono_jit_info_table_find (domain, ip);
7758 return mono_gc_is_critical_method (ji->method);
7762 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7763 * The signature of the called method is:
7764 * object allocate (MonoVTable *vtable)
7767 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7769 #ifdef MANAGED_ALLOCATION
7770 MonoClass *klass = vtable->klass;
7772 #ifdef HAVE_KW_THREAD
7773 int tlab_next_offset = -1;
7774 int tlab_temp_end_offset = -1;
7775 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7776 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7778 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7782 if (!mono_runtime_has_tls_get ())
7784 if (klass->instance_size > tlab_size)
7786 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7790 if (klass->byval_arg.type == MONO_TYPE_STRING)
7792 if (collect_before_allocs)
7795 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7796 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7798 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7805 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7807 #ifdef MANAGED_ALLOCATION
7808 MonoClass *klass = vtable->klass;
7810 #ifdef HAVE_KW_THREAD
7811 int tlab_next_offset = -1;
7812 int tlab_temp_end_offset = -1;
7813 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7814 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7816 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7822 if (!mono_runtime_has_tls_get ())
7824 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7826 if (collect_before_allocs)
7828 g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
7830 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7837 mono_gc_get_managed_allocator_by_type (int atype)
7839 #ifdef MANAGED_ALLOCATION
7842 if (!mono_runtime_has_tls_get ())
7845 mono_loader_lock ();
7846 res = alloc_method_cache [atype];
7848 res = alloc_method_cache [atype] = create_allocator (atype);
7849 mono_loader_unlock ();
7857 mono_gc_get_managed_allocator_types (void)
7864 mono_gc_get_write_barrier (void)
7867 MonoMethodBuilder *mb;
7868 MonoMethodSignature *sig;
7869 #ifdef MANAGED_WBARRIER
7870 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7871 #ifndef SGEN_ALIGN_NURSERY
7872 int label_continue_1, label_continue_2, label_no_wb_5;
7873 int dereferenced_var;
7875 int buffer_var, buffer_index_var, dummy_var;
7877 #ifdef HAVE_KW_THREAD
7878 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7879 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7881 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7882 g_assert (stack_end_offset != -1);
7883 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7884 g_assert (store_remset_buffer_offset != -1);
7885 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7886 g_assert (store_remset_buffer_index_offset != -1);
7887 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7888 g_assert (store_remset_buffer_index_addr_offset != -1);
7892 g_assert (!use_cardtable);
7894 // FIXME: Maybe create a separate version for ctors (the branch would be
7895 // correctly predicted more times)
7896 if (write_barrier_method)
7897 return write_barrier_method;
7899 /* Create the IL version of mono_gc_barrier_generic_store () */
7900 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7901 sig->ret = &mono_defaults.void_class->byval_arg;
7902 sig->params [0] = &mono_defaults.int_class->byval_arg;
7904 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7906 #ifdef MANAGED_WBARRIER
7907 if (mono_runtime_has_tls_get ()) {
7908 #ifdef SGEN_ALIGN_NURSERY
7909 // if (ptr_in_nursery (ptr)) return;
7911 * Masking out the bits might be faster, but we would have to use 64 bit
7912 * immediates, which might be slower.
7914 mono_mb_emit_ldarg (mb, 0);
7915 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7916 mono_mb_emit_byte (mb, CEE_SHR_UN);
7917 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7918 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7920 // if (!ptr_in_nursery (*ptr)) return;
7921 mono_mb_emit_ldarg (mb, 0);
7922 mono_mb_emit_byte (mb, CEE_LDIND_I);
7923 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7924 mono_mb_emit_byte (mb, CEE_SHR_UN);
7925 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7926 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7929 // if (ptr < (nursery_start)) goto continue;
7930 mono_mb_emit_ldarg (mb, 0);
7931 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7932 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7934 // if (ptr >= nursery_end)) goto continue;
7935 mono_mb_emit_ldarg (mb, 0);
7936 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7937 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7940 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7943 mono_mb_patch_branch (mb, label_continue_1);
7944 mono_mb_patch_branch (mb, label_continue_2);
7946 // Dereference and store in local var
7947 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7948 mono_mb_emit_ldarg (mb, 0);
7949 mono_mb_emit_byte (mb, CEE_LDIND_I);
7950 mono_mb_emit_stloc (mb, dereferenced_var);
7952 // if (*ptr < nursery_start) return;
7953 mono_mb_emit_ldloc (mb, dereferenced_var);
7954 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7955 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7957 // if (*ptr >= nursery_end) return;
7958 mono_mb_emit_ldloc (mb, dereferenced_var);
7959 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7960 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7963 // if (ptr >= stack_end) goto need_wb;
7964 mono_mb_emit_ldarg (mb, 0);
7965 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7966 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7968 // if (ptr >= stack_start) return;
7969 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7970 mono_mb_emit_ldarg (mb, 0);
7971 mono_mb_emit_ldloc_addr (mb, dummy_var);
7972 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7975 mono_mb_patch_branch (mb, label_need_wb);
7977 // buffer = STORE_REMSET_BUFFER;
7978 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7979 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7980 mono_mb_emit_stloc (mb, buffer_var);
7982 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7983 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7984 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7985 mono_mb_emit_stloc (mb, buffer_index_var);
7987 // if (buffer [buffer_index] == ptr) return;
7988 mono_mb_emit_ldloc (mb, buffer_var);
7989 mono_mb_emit_ldloc (mb, buffer_index_var);
7990 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7991 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7992 mono_mb_emit_byte (mb, CEE_SHL);
7993 mono_mb_emit_byte (mb, CEE_ADD);
7994 mono_mb_emit_byte (mb, CEE_LDIND_I);
7995 mono_mb_emit_ldarg (mb, 0);
7996 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7999 mono_mb_emit_ldloc (mb, buffer_index_var);
8000 mono_mb_emit_icon (mb, 1);
8001 mono_mb_emit_byte (mb, CEE_ADD);
8002 mono_mb_emit_stloc (mb, buffer_index_var);
8004 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
8005 mono_mb_emit_ldloc (mb, buffer_index_var);
8006 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
8007 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
8009 // buffer [buffer_index] = ptr;
8010 mono_mb_emit_ldloc (mb, buffer_var);
8011 mono_mb_emit_ldloc (mb, buffer_index_var);
8012 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8013 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8014 mono_mb_emit_byte (mb, CEE_SHL);
8015 mono_mb_emit_byte (mb, CEE_ADD);
8016 mono_mb_emit_ldarg (mb, 0);
8017 mono_mb_emit_byte (mb, CEE_STIND_I);
8019 // STORE_REMSET_BUFFER_INDEX = buffer_index;
8020 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8021 mono_mb_emit_ldloc (mb, buffer_index_var);
8022 mono_mb_emit_byte (mb, CEE_STIND_I);
8025 mono_mb_patch_branch (mb, label_no_wb_1);
8026 mono_mb_patch_branch (mb, label_no_wb_2);
8027 mono_mb_patch_branch (mb, label_no_wb_3);
8028 mono_mb_patch_branch (mb, label_no_wb_4);
8029 #ifndef SGEN_ALIGN_NURSERY
8030 mono_mb_patch_branch (mb, label_no_wb_5);
8032 mono_mb_emit_byte (mb, CEE_RET);
8035 mono_mb_patch_branch (mb, label_slow_path);
8039 mono_mb_emit_ldarg (mb, 0);
8040 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
8041 mono_mb_emit_byte (mb, CEE_RET);
8043 res = mono_mb_create_method (mb, sig, 16);
8046 mono_loader_lock ();
8047 if (write_barrier_method) {
8048 /* Already created */
8049 mono_free_method (res);
8051 /* double-checked locking */
8052 mono_memory_barrier ();
8053 write_barrier_method = res;
8055 mono_loader_unlock ();
8057 return write_barrier_method;
8061 mono_gc_get_description (void)
8063 return g_strdup ("sgen");
8067 mono_gc_set_desktop_mode (void)
8072 mono_gc_is_moving (void)
8078 mono_gc_is_disabled (void)
8084 mono_sgen_debug_printf (int level, const char *format, ...)
8088 if (level > gc_debug_level)
8091 va_start (ap, format);
8092 vfprintf (gc_debug_file, format, ap);
8097 mono_sgen_get_logfile (void)
8099 return gc_debug_file;
8103 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
8109 #endif /* HAVE_SGEN_GC */