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 /* 0 means not initialized, 1 is initialized, -1 means in progress */
247 static gint32 gc_initialized = 0;
248 /* If set, do a minor collection before every X allocation */
249 static guint32 collect_before_allocs = 0;
250 /* If set, do a heap consistency check before each minor collection */
251 static gboolean consistency_check_at_minor_collection = FALSE;
252 /* If set, check that there are no references to the domain left at domain unload */
253 static gboolean xdomain_checks = FALSE;
254 /* If not null, dump the heap after each collection into this file */
255 static FILE *heap_dump_file = NULL;
256 /* If set, mark stacks conservatively, even if precise marking is possible */
257 static gboolean conservative_stack_mark = FALSE;
258 /* If set, do a plausibility check on the scan_starts before and after
260 static gboolean do_scan_starts_check = FALSE;
261 static gboolean disable_minor_collections = FALSE;
262 static gboolean disable_major_collections = FALSE;
264 #ifdef HEAVY_STATISTICS
265 static long long stat_objects_alloced = 0;
266 static long long stat_bytes_alloced = 0;
267 long long stat_objects_alloced_degraded = 0;
268 long long stat_bytes_alloced_degraded = 0;
269 static long long stat_bytes_alloced_los = 0;
271 long long stat_copy_object_called_nursery = 0;
272 long long stat_objects_copied_nursery = 0;
273 long long stat_copy_object_called_major = 0;
274 long long stat_objects_copied_major = 0;
276 long long stat_scan_object_called_nursery = 0;
277 long long stat_scan_object_called_major = 0;
279 long long stat_nursery_copy_object_failed_from_space = 0;
280 long long stat_nursery_copy_object_failed_forwarded = 0;
281 long long stat_nursery_copy_object_failed_pinned = 0;
283 static long long stat_store_remsets = 0;
284 static long long stat_store_remsets_unique = 0;
285 static long long stat_saved_remsets_1 = 0;
286 static long long stat_saved_remsets_2 = 0;
287 static long long stat_local_remsets_processed = 0;
288 static long long stat_global_remsets_added = 0;
289 static long long stat_global_remsets_readded = 0;
290 static long long stat_global_remsets_processed = 0;
291 static long long stat_global_remsets_discarded = 0;
293 static long long stat_wasted_fragments_used = 0;
294 static long long stat_wasted_fragments_bytes = 0;
296 static int stat_wbarrier_set_field = 0;
297 static int stat_wbarrier_set_arrayref = 0;
298 static int stat_wbarrier_arrayref_copy = 0;
299 static int stat_wbarrier_generic_store = 0;
300 static int stat_wbarrier_generic_store_remset = 0;
301 static int stat_wbarrier_set_root = 0;
302 static int stat_wbarrier_value_copy = 0;
303 static int stat_wbarrier_object_copy = 0;
306 static long long stat_pinned_objects = 0;
308 static long long time_minor_pre_collection_fragment_clear = 0;
309 static long long time_minor_pinning = 0;
310 static long long time_minor_scan_remsets = 0;
311 static long long time_minor_scan_card_table = 0;
312 static long long time_minor_scan_pinned = 0;
313 static long long time_minor_scan_registered_roots = 0;
314 static long long time_minor_scan_thread_data = 0;
315 static long long time_minor_finish_gray_stack = 0;
316 static long long time_minor_fragment_creation = 0;
318 static long long time_major_pre_collection_fragment_clear = 0;
319 static long long time_major_pinning = 0;
320 static long long time_major_scan_pinned = 0;
321 static long long time_major_scan_registered_roots = 0;
322 static long long time_major_scan_thread_data = 0;
323 static long long time_major_scan_alloc_pinned = 0;
324 static long long time_major_scan_finalized = 0;
325 static long long time_major_scan_big_objects = 0;
326 static long long time_major_finish_gray_stack = 0;
327 static long long time_major_free_bigobjs = 0;
328 static long long time_major_los_sweep = 0;
329 static long long time_major_sweep = 0;
330 static long long time_major_fragment_creation = 0;
332 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
334 int gc_debug_level = 0;
339 mono_gc_flush_info (void)
341 fflush (gc_debug_file);
346 * Define this to allow the user to change the nursery size by
347 * specifying its value in the MONO_GC_PARAMS environmental
348 * variable. See mono_gc_base_init for details.
350 #define USER_CONFIG 1
352 #define TV_DECLARE SGEN_TV_DECLARE
353 #define TV_GETTIME SGEN_TV_GETTIME
354 #define TV_ELAPSED SGEN_TV_ELAPSED
355 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
357 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
359 /* The method used to clear the nursery */
360 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
361 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
366 CLEAR_AT_TLAB_CREATION
367 } NurseryClearPolicy;
369 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
372 * The young generation is divided into fragments. This is because
373 * we can hand one fragments to a thread for lock-less fast alloc and
374 * because the young generation ends up fragmented anyway by pinned objects.
375 * Once a collection is done, a list of fragments is created. When doing
376 * thread local alloc we use smallish nurseries so we allow new threads to
377 * allocate memory from gen0 without triggering a collection. Threads that
378 * are found to allocate lots of memory are given bigger fragments. This
379 * should make the finalizer thread use little nursery memory after a while.
380 * We should start assigning threads very small fragments: if there are many
381 * threads the nursery will be full of reserved space that the threads may not
382 * use at all, slowing down allocation speed.
383 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
384 * Allocation Buffers (TLABs).
386 typedef struct _Fragment Fragment;
390 char *fragment_start;
391 char *fragment_limit; /* the current soft limit for allocation */
395 /* the runtime can register areas of memory as roots: we keep two lists of roots,
396 * a pinned root set for conservatively scanned roots and a normal one for
397 * precisely scanned roots (currently implemented as a single list).
399 typedef struct _RootRecord RootRecord;
408 * We're never actually using the first element. It's always set to
409 * NULL to simplify the elimination of consecutive duplicate
412 #define STORE_REMSET_BUFFER_SIZE 1023
414 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
415 struct _GenericStoreRememberedSet {
416 GenericStoreRememberedSet *next;
417 /* We need one entry less because the first entry of store
418 remset buffers is always a dummy and we don't copy it. */
419 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
422 /* we have 4 possible values in the low 2 bits */
424 REMSET_LOCATION, /* just a pointer to the exact location */
425 REMSET_RANGE, /* range of pointer fields */
426 REMSET_OBJECT, /* mark all the object for scanning */
427 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
428 REMSET_TYPE_MASK = 0x3
431 #ifdef HAVE_KW_THREAD
432 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
434 static pthread_key_t remembered_set_key;
435 static RememberedSet *global_remset;
436 static RememberedSet *freed_thread_remsets;
437 static GenericStoreRememberedSet *generic_store_remsets = NULL;
439 /*A two slots cache for recently inserted remsets */
440 static gpointer global_remset_cache [2];
442 /* FIXME: later choose a size that takes into account the RememberedSet struct
443 * and doesn't waste any alloc paddin space.
445 #define DEFAULT_REMSET_SIZE 1024
446 static RememberedSet* alloc_remset (int size, gpointer id, gboolean global);
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 (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_remset (global_remset->end_set - global_remset->data, NULL, TRUE);
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);
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 (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 (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 (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 (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);
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 (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 (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 (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 (job_scan_finalizer_entries, &sfejd_fin_ready);
3486 sfejd_critical_fin.list = critical_fin_list;
3487 workers_enqueue_job (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 (&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 (&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 (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 (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 /* See the comment at the end of sgen_thread_unregister() */
5726 if (*info->store_remset_buffer_addr)
5727 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5731 remset_byte_size (RememberedSet *remset)
5733 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5737 scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5739 RememberedSet *remset;
5740 mword *p, *next_p, *store_pos;
5742 /* the global one */
5743 for (remset = global_remset; remset; remset = remset->next) {
5744 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));
5745 store_pos = remset->data;
5746 for (p = remset->data; p < remset->store_next; p = next_p) {
5747 void **ptr = (void**)p [0];
5749 /*Ignore previously processed remset.*/
5750 if (!global_remset_location_was_not_added (ptr)) {
5755 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5758 * Clear global remsets of locations which no longer point to the
5759 * nursery. Otherwise, they could grow indefinitely between major
5762 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5764 if (ptr_in_nursery (*ptr)) {
5765 *store_pos ++ = p [0];
5766 HEAVY_STAT (++stat_global_remsets_readded);
5770 /* Truncate the remset */
5771 remset->store_next = store_pos;
5776 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5779 SgenThreadInfo *info;
5780 RememberedSet *remset;
5781 GenericStoreRememberedSet *store_remset;
5784 #ifdef HEAVY_STATISTICS
5788 /* the generic store ones */
5789 store_remset = generic_store_remsets;
5790 while (store_remset) {
5791 GenericStoreRememberedSet *next = store_remset->next;
5793 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5794 gpointer addr = store_remset->data [i];
5796 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5799 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5801 store_remset = next;
5803 generic_store_remsets = NULL;
5805 /* the per-thread ones */
5806 FOREACH_THREAD (info) {
5807 RememberedSet *next;
5809 for (remset = info->remset; remset; remset = next) {
5810 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));
5811 for (p = remset->data; p < remset->store_next;)
5812 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5813 remset->store_next = remset->data;
5814 next = remset->next;
5815 remset->next = NULL;
5816 if (remset != info->remset) {
5817 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5818 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5821 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5822 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5823 clear_thread_store_remset_buffer (info);
5824 } END_FOREACH_THREAD
5826 /* the freed thread ones */
5827 while (freed_thread_remsets) {
5828 RememberedSet *next;
5829 remset = freed_thread_remsets;
5830 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));
5831 for (p = remset->data; p < remset->store_next;)
5832 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5833 next = remset->next;
5834 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5835 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5836 freed_thread_remsets = next;
5841 * Clear the info in the remembered sets: we're doing a major collection, so
5842 * the per-thread ones are not needed and the global ones will be reconstructed
5846 clear_remsets (void)
5848 SgenThreadInfo *info;
5849 RememberedSet *remset, *next;
5851 /* the global list */
5852 for (remset = global_remset; remset; remset = next) {
5853 remset->store_next = remset->data;
5854 next = remset->next;
5855 remset->next = NULL;
5856 if (remset != global_remset) {
5857 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5858 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5861 /* the generic store ones */
5862 while (generic_store_remsets) {
5863 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5864 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5865 generic_store_remsets = gs_next;
5867 /* the per-thread ones */
5868 FOREACH_THREAD (info) {
5869 for (remset = info->remset; remset; remset = next) {
5870 remset->store_next = remset->data;
5871 next = remset->next;
5872 remset->next = NULL;
5873 if (remset != info->remset) {
5874 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5875 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5878 clear_thread_store_remset_buffer (info);
5879 } END_FOREACH_THREAD
5881 /* the freed thread ones */
5882 while (freed_thread_remsets) {
5883 next = freed_thread_remsets->next;
5884 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5885 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5886 freed_thread_remsets = next;
5891 * Clear the thread local TLAB variables for all threads.
5896 SgenThreadInfo *info;
5898 FOREACH_THREAD (info) {
5899 /* A new TLAB will be allocated when the thread does its first allocation */
5900 *info->tlab_start_addr = NULL;
5901 *info->tlab_next_addr = NULL;
5902 *info->tlab_temp_end_addr = NULL;
5903 *info->tlab_real_end_addr = NULL;
5904 } END_FOREACH_THREAD
5908 sgen_thread_register (SgenThreadInfo* info, void *addr)
5910 #ifndef HAVE_KW_THREAD
5911 SgenThreadInfo *__thread_info__ = info;
5915 #ifndef HAVE_KW_THREAD
5916 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5918 g_assert (!pthread_getspecific (thread_info_key));
5919 pthread_setspecific (thread_info_key, info);
5924 info->stop_count = -1;
5927 info->stack_start = NULL;
5928 info->tlab_start_addr = &TLAB_START;
5929 info->tlab_next_addr = &TLAB_NEXT;
5930 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5931 info->tlab_real_end_addr = &TLAB_REAL_END;
5932 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5933 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5934 info->stopped_ip = NULL;
5935 info->stopped_domain = NULL;
5937 info->monoctx = NULL;
5939 info->stopped_regs = NULL;
5942 binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
5944 #ifdef HAVE_KW_THREAD
5945 tlab_next_addr = &tlab_next;
5946 store_remset_buffer_index_addr = &store_remset_buffer_index;
5949 #if defined(__MACH__)
5950 info->mach_port = mach_thread_self ();
5953 /* try to get it with attributes first */
5954 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5958 pthread_attr_t attr;
5959 pthread_getattr_np (pthread_self (), &attr);
5960 pthread_attr_getstack (&attr, &sstart, &size);
5961 info->stack_start_limit = sstart;
5962 info->stack_end = (char*)sstart + size;
5963 pthread_attr_destroy (&attr);
5965 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5966 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5967 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5970 /* FIXME: we assume the stack grows down */
5971 gsize stack_bottom = (gsize)addr;
5972 stack_bottom += 4095;
5973 stack_bottom &= ~4095;
5974 info->stack_end = (char*)stack_bottom;
5978 #ifdef HAVE_KW_THREAD
5979 stack_end = info->stack_end;
5982 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info, FALSE);
5983 pthread_setspecific (remembered_set_key, info->remset);
5984 #ifdef HAVE_KW_THREAD
5985 remembered_set = info->remset;
5988 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5989 STORE_REMSET_BUFFER_INDEX = 0;
5991 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p)\n", info, (gpointer)mono_thread_info_get_tid (info)));
5993 if (gc_callbacks.thread_attach_func)
5994 info->runtime_data = gc_callbacks.thread_attach_func ();
6001 add_generic_store_remset_from_buffer (gpointer *buffer)
6003 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
6004 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6005 remset->next = generic_store_remsets;
6006 generic_store_remsets = remset;
6010 sgen_thread_unregister (SgenThreadInfo *p)
6012 RememberedSet *rset;
6014 /* If a delegate is passed to native code and invoked on a thread we dont
6015 * know about, the jit will register it with mono_jit_thread_attach, but
6016 * we have no way of knowing when that thread goes away. SGen has a TSD
6017 * so we assume that if the domain is still registered, we can detach
6020 if (mono_domain_get ())
6021 mono_thread_detach (mono_thread_current ());
6025 binary_protocol_thread_unregister ((gpointer)id);
6026 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)mono_thread_info_get_tid (p)));
6028 #if defined(__MACH__)
6029 mach_port_deallocate (current_task (), p->mach_port);
6032 if (gc_callbacks.thread_detach_func) {
6033 gc_callbacks.thread_detach_func (p->runtime_data);
6034 p->runtime_data = NULL;
6038 if (freed_thread_remsets) {
6039 for (rset = p->remset; rset->next; rset = rset->next)
6041 rset->next = freed_thread_remsets;
6042 freed_thread_remsets = p->remset;
6044 freed_thread_remsets = p->remset;
6047 if (*p->store_remset_buffer_index_addr)
6048 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6049 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6051 * This is currently not strictly required, but we do it
6052 * anyway in case we change thread unregistering:
6054 * If the thread is removed from the thread list after
6055 * unregistering (this is currently not the case), and a
6056 * collection occurs, clear_remsets() would want to memset
6057 * this buffer, which would either clobber memory or crash.
6059 *p->store_remset_buffer_addr = NULL;
6065 sgen_thread_attach (SgenThreadInfo *info)
6068 /*this is odd, can we get attached before the gc is inited?*/
6072 if (gc_callbacks.thread_attach_func && !info->runtime_data)
6073 info->runtime_data = gc_callbacks.thread_attach_func ();
6075 /* Need a better place to initialize this */
6076 if (!array_fill_vtable && mono_get_root_domain ()) {
6077 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
6082 mono_gc_register_thread (void *baseptr)
6084 return mono_thread_info_attach (baseptr) != NULL;
6088 * mono_gc_set_stack_end:
6090 * Set the end of the current threads stack to STACK_END. The stack space between
6091 * STACK_END and the real end of the threads stack will not be scanned during collections.
6094 mono_gc_set_stack_end (void *stack_end)
6096 SgenThreadInfo *info;
6099 info = mono_thread_info_current ();
6101 g_assert (stack_end < info->stack_end);
6102 info->stack_end = stack_end;
6107 #if USE_PTHREAD_INTERCEPT
6111 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6113 return mono_threads_pthread_create (new_thread, attr, start_routine, arg);
6117 mono_gc_pthread_join (pthread_t thread, void **retval)
6119 return pthread_join (thread, retval);
6123 mono_gc_pthread_detach (pthread_t thread)
6125 return pthread_detach (thread);
6128 #endif /* USE_PTHREAD_INTERCEPT */
6131 * ######################################################################
6132 * ######## Write barriers
6133 * ######################################################################
6137 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
6140 dummy_use (gpointer v) {
6141 __asm__ volatile ("" : "=r"(v) : "r"(v));
6145 static RememberedSet*
6146 alloc_remset (int size, gpointer id, gboolean global)
6148 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6149 res->store_next = res->data;
6150 res->end_set = res->data + size;
6152 DEBUG (4, fprintf (gc_debug_file, "Allocated%s remset size %d at %p for %p\n", global ? " global" : "", size, res->data, id));
6157 * Note: the write barriers first do the needed GC work and then do the actual store:
6158 * this way the value is visible to the conservative GC scan after the write barrier
6159 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6160 * the conservative scan, otherwise by the remembered set scan.
6163 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6165 HEAVY_STAT (++stat_wbarrier_set_field);
6166 if (ptr_in_nursery (field_ptr)) {
6167 *(void**)field_ptr = value;
6170 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6171 if (use_cardtable) {
6172 *(void**)field_ptr = value;
6173 if (ptr_in_nursery (value))
6174 sgen_card_table_mark_address ((mword)field_ptr);
6181 rs = REMEMBERED_SET;
6182 if (rs->store_next < rs->end_set) {
6183 *(rs->store_next++) = (mword)field_ptr;
6184 *(void**)field_ptr = value;
6188 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
6189 rs->next = REMEMBERED_SET;
6190 REMEMBERED_SET = rs;
6191 #ifdef HAVE_KW_THREAD
6192 mono_thread_info_current ()->remset = rs;
6194 *(rs->store_next++) = (mword)field_ptr;
6195 *(void**)field_ptr = value;
6201 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6203 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6204 if (ptr_in_nursery (slot_ptr)) {
6205 *(void**)slot_ptr = value;
6208 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6209 if (use_cardtable) {
6210 *(void**)slot_ptr = value;
6211 if (ptr_in_nursery (value))
6212 sgen_card_table_mark_address ((mword)slot_ptr);
6219 rs = REMEMBERED_SET;
6220 if (rs->store_next < rs->end_set) {
6221 *(rs->store_next++) = (mword)slot_ptr;
6222 *(void**)slot_ptr = value;
6226 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
6227 rs->next = REMEMBERED_SET;
6228 REMEMBERED_SET = rs;
6229 #ifdef HAVE_KW_THREAD
6230 mono_thread_info_current ()->remset = rs;
6232 *(rs->store_next++) = (mword)slot_ptr;
6233 *(void**)slot_ptr = value;
6239 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6241 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6242 /*This check can be done without taking a lock since dest_ptr array is pinned*/
6243 if (ptr_in_nursery (dest_ptr) || count <= 0) {
6244 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6248 if (use_cardtable) {
6249 gpointer *dest = dest_ptr;
6250 gpointer *src = src_ptr;
6252 /*overlapping that required backward copying*/
6253 if (src < dest && (src + count) > dest) {
6254 gpointer *start = dest;
6258 for (; dest >= start; --src, --dest) {
6259 gpointer value = *src;
6261 if (ptr_in_nursery (value))
6262 sgen_card_table_mark_address ((mword)dest);
6266 gpointer *end = dest + count;
6267 for (; dest < end; ++src, ++dest) {
6268 gpointer value = *src;
6270 if (ptr_in_nursery (value))
6271 sgen_card_table_mark_address ((mword)dest);
6279 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6281 rs = REMEMBERED_SET;
6282 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6283 if (rs->store_next + 1 < rs->end_set) {
6284 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6285 *(rs->store_next++) = count;
6289 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
6290 rs->next = REMEMBERED_SET;
6291 REMEMBERED_SET = rs;
6292 #ifdef HAVE_KW_THREAD
6293 mono_thread_info_current ()->remset = rs;
6295 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6296 *(rs->store_next++) = count;
6302 static char *found_obj;
6305 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
6307 char *ptr = user_data;
6309 if (ptr >= obj && ptr < obj + size) {
6310 g_assert (!found_obj);
6315 /* for use in the debugger */
6316 char* find_object_for_ptr (char *ptr);
6318 find_object_for_ptr (char *ptr)
6320 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6322 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6323 find_object_for_ptr_callback, ptr, TRUE);
6329 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
6334 * Very inefficient, but this is debugging code, supposed to
6335 * be called from gdb, so we don't care.
6338 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
6343 evacuate_remset_buffer (void)
6348 buffer = STORE_REMSET_BUFFER;
6350 add_generic_store_remset_from_buffer (buffer);
6351 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6353 STORE_REMSET_BUFFER_INDEX = 0;
6357 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6363 HEAVY_STAT (++stat_wbarrier_generic_store);
6365 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6366 /* FIXME: ptr_in_heap must be called with the GC lock held */
6367 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6368 char *start = find_object_for_ptr (ptr);
6369 MonoObject *value = *(MonoObject**)ptr;
6373 MonoObject *obj = (MonoObject*)start;
6374 if (obj->vtable->domain != value->vtable->domain)
6375 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6381 if (*(gpointer*)ptr)
6382 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6384 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6385 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6389 if (use_cardtable) {
6390 if (ptr_in_nursery(*(gpointer*)ptr))
6391 sgen_card_table_mark_address ((mword)ptr);
6397 buffer = STORE_REMSET_BUFFER;
6398 index = STORE_REMSET_BUFFER_INDEX;
6399 /* This simple optimization eliminates a sizable portion of
6400 entries. Comparing it to the last but one entry as well
6401 doesn't eliminate significantly more entries. */
6402 if (buffer [index] == ptr) {
6407 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6408 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6411 if (index >= STORE_REMSET_BUFFER_SIZE) {
6412 evacuate_remset_buffer ();
6413 index = STORE_REMSET_BUFFER_INDEX;
6414 g_assert (index == 0);
6417 buffer [index] = ptr;
6418 STORE_REMSET_BUFFER_INDEX = index;
6424 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6426 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6427 *(void**)ptr = value;
6428 if (ptr_in_nursery (value))
6429 mono_gc_wbarrier_generic_nostore (ptr);
6433 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
6435 mword *dest = _dest;
6440 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
6445 size -= SIZEOF_VOID_P;
6452 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6455 size_t size = count * mono_class_value_size (klass, NULL);
6457 HEAVY_STAT (++stat_wbarrier_value_copy);
6458 g_assert (klass->valuetype);
6460 memmove (dest, src, size);
6461 if (use_cardtable) {
6462 sgen_card_table_mark_range ((mword)dest, size);
6464 rs = REMEMBERED_SET;
6465 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
6469 g_assert (klass->gc_descr_inited);
6470 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));
6472 if (rs->store_next + 3 < rs->end_set) {
6473 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6474 *(rs->store_next++) = (mword)klass->gc_descr;
6475 *(rs->store_next++) = (mword)count;
6479 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
6480 rs->next = REMEMBERED_SET;
6481 REMEMBERED_SET = rs;
6482 #ifdef HAVE_KW_THREAD
6483 mono_thread_info_current ()->remset = rs;
6485 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6486 *(rs->store_next++) = (mword)klass->gc_descr;
6487 *(rs->store_next++) = (mword)count;
6493 * mono_gc_wbarrier_object_copy:
6495 * Write barrier to call when obj is the result of a clone or copy of an object.
6498 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6504 HEAVY_STAT (++stat_wbarrier_object_copy);
6505 rs = REMEMBERED_SET;
6506 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6507 size = mono_object_class (obj)->instance_size;
6509 /* do not copy the sync state */
6510 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6511 size - sizeof (MonoObject));
6512 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6516 if (rs->store_next < rs->end_set) {
6517 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6521 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
6522 rs->next = REMEMBERED_SET;
6523 REMEMBERED_SET = rs;
6525 #ifdef HAVE_KW_THREAD
6526 mono_thread_info_current ()->remset = rs;
6528 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6533 * ######################################################################
6534 * ######## Collector debugging
6535 * ######################################################################
6538 const char*descriptor_types [] = {
6550 describe_ptr (char *ptr)
6557 if (ptr_in_nursery (ptr)) {
6558 printf ("Pointer inside nursery.\n");
6560 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
6562 printf ("Pointer is the start of object %p in LOS space.\n", start);
6564 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
6566 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
6567 printf ("Pointer inside oldspace.\n");
6568 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
6569 printf ("Pointer is inside a pinned chunk.\n");
6571 printf ("Pointer unknown.\n");
6576 if (object_is_pinned (ptr))
6577 printf ("Object is pinned.\n");
6579 if (object_is_forwarded (ptr))
6580 printf ("Object is forwared.\n");
6582 // FIXME: Handle pointers to the inside of objects
6583 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6585 printf ("VTable: %p\n", vtable);
6586 if (vtable == NULL) {
6587 printf ("VTable is invalid (empty).\n");
6590 if (ptr_in_nursery (vtable)) {
6591 printf ("VTable is invalid (points inside nursery).\n");
6594 printf ("Class: %s\n", vtable->klass->name);
6596 desc = ((GCVTable*)vtable)->desc;
6597 printf ("Descriptor: %lx\n", (long)desc);
6600 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6604 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6610 switch ((*p) & REMSET_TYPE_MASK) {
6611 case REMSET_LOCATION:
6612 if (*p == (mword)addr)
6616 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6618 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6622 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6623 count = safe_object_get_size ((MonoObject*)ptr);
6624 count = ALIGN_UP (count);
6625 count /= sizeof (mword);
6626 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6630 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6634 switch (desc & 0x7) {
6635 case DESC_TYPE_RUN_LENGTH:
6636 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6638 case DESC_TYPE_SMALL_BITMAP:
6639 OBJ_BITMAP_SIZE (skip_size, desc, start);
6643 g_assert_not_reached ();
6646 /* The descriptor includes the size of MonoObject */
6647 skip_size -= sizeof (MonoObject);
6649 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6654 g_assert_not_reached ();
6660 * Return whenever ADDR occurs in the remembered sets
6663 find_in_remsets (char *addr)
6666 SgenThreadInfo *info;
6667 RememberedSet *remset;
6668 GenericStoreRememberedSet *store_remset;
6670 gboolean found = FALSE;
6672 /* the global one */
6673 for (remset = global_remset; remset; remset = remset->next) {
6674 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));
6675 for (p = remset->data; p < remset->store_next;) {
6676 p = find_in_remset_loc (p, addr, &found);
6682 /* the generic store ones */
6683 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6684 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6685 if (store_remset->data [i] == addr)
6690 /* the per-thread ones */
6691 FOREACH_THREAD (info) {
6693 for (remset = info->remset; remset; remset = remset->next) {
6694 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));
6695 for (p = remset->data; p < remset->store_next;) {
6696 p = find_in_remset_loc (p, addr, &found);
6701 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6702 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6705 } END_FOREACH_THREAD
6707 /* the freed thread ones */
6708 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6709 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));
6710 for (p = remset->data; p < remset->store_next;) {
6711 p = find_in_remset_loc (p, addr, &found);
6720 static gboolean missing_remsets;
6723 * We let a missing remset slide if the target object is pinned,
6724 * because the store might have happened but the remset not yet added,
6725 * but in that case the target must be pinned. We might theoretically
6726 * miss some missing remsets this way, but it's very unlikely.
6729 #define HANDLE_PTR(ptr,obj) do { \
6730 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6731 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6732 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); \
6733 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6734 if (!object_is_pinned (*(ptr))) \
6735 missing_remsets = TRUE; \
6741 * Check that each object reference which points into the nursery can
6742 * be found in the remembered sets.
6745 check_consistency_callback (char *start, size_t size, void *dummy)
6747 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6748 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6750 #define SCAN_OBJECT_ACTION
6751 #include "sgen-scan-object.h"
6755 * Perform consistency check of the heap.
6757 * Assumes the world is stopped.
6760 check_consistency (void)
6762 // Need to add more checks
6764 missing_remsets = FALSE;
6766 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6768 // Check that oldspace->newspace pointers are registered with the collector
6769 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6771 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6773 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6775 if (!binary_protocol_is_enabled ())
6776 g_assert (!missing_remsets);
6781 #define HANDLE_PTR(ptr,obj) do { \
6782 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6783 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6787 check_major_refs_callback (char *start, size_t size, void *dummy)
6789 #define SCAN_OBJECT_ACTION
6790 #include "sgen-scan-object.h"
6794 check_major_refs (void)
6796 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6797 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6800 /* Check that the reference is valid */
6802 #define HANDLE_PTR(ptr,obj) do { \
6804 g_assert (safe_name (*(ptr)) != NULL); \
6811 * Perform consistency check on an object. Currently we only check that the
6812 * reference fields are valid.
6815 check_object (char *start)
6820 #include "sgen-scan-object.h"
6824 * ######################################################################
6825 * ######## Other mono public interface functions.
6826 * ######################################################################
6829 #define REFS_SIZE 128
6832 MonoGCReferences callback;
6836 MonoObject *refs [REFS_SIZE];
6837 uintptr_t offsets [REFS_SIZE];
6841 #define HANDLE_PTR(ptr,obj) do { \
6843 if (hwi->count == REFS_SIZE) { \
6844 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
6848 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
6849 hwi->refs [hwi->count++] = *(ptr); \
6854 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6856 #include "sgen-scan-object.h"
6860 walk_references (char *start, size_t size, void *data)
6862 HeapWalkInfo *hwi = data;
6865 collect_references (hwi, start, size);
6866 if (hwi->count || !hwi->called)
6867 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6871 * mono_gc_walk_heap:
6872 * @flags: flags for future use
6873 * @callback: a function pointer called for each object in the heap
6874 * @data: a user data pointer that is passed to callback
6876 * This function can be used to iterate over all the live objects in the heap:
6877 * for each object, @callback is invoked, providing info about the object's
6878 * location in memory, its class, its size and the objects it references.
6879 * For each referenced object it's offset from the object address is
6880 * reported in the offsets array.
6881 * The object references may be buffered, so the callback may be invoked
6882 * multiple times for the same object: in all but the first call, the size
6883 * argument will be zero.
6884 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6885 * profiler event handler.
6887 * Returns: a non-zero value if the GC doesn't support heap walking
6890 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6895 hwi.callback = callback;
6898 clear_nursery_fragments (nursery_next);
6899 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
6901 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
6902 mono_sgen_los_iterate_objects (walk_references, &hwi);
6908 mono_gc_collect (int generation)
6913 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6914 stop_world (generation);
6915 if (generation == 0) {
6916 collect_nursery (0);
6918 major_collection ("user request");
6920 restart_world (generation);
6921 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6926 mono_gc_max_generation (void)
6932 mono_gc_collection_count (int generation)
6934 if (generation == 0)
6935 return num_minor_gcs;
6936 return num_major_gcs;
6940 mono_gc_get_used_size (void)
6944 tot = los_memory_usage;
6945 tot += nursery_section->next_data - nursery_section->data;
6946 tot += major_collector.get_used_size ();
6947 /* FIXME: account for pinned objects */
6953 mono_gc_get_heap_size (void)
6959 mono_gc_disable (void)
6967 mono_gc_enable (void)
6975 mono_gc_get_los_limit (void)
6977 return MAX_SMALL_OBJ_SIZE;
6981 mono_object_is_alive (MonoObject* o)
6987 mono_gc_get_generation (MonoObject *obj)
6989 if (ptr_in_nursery (obj))
6995 mono_gc_enable_events (void)
7000 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7003 mono_gc_register_disappearing_link (obj, link_addr, track);
7008 mono_gc_weak_link_remove (void **link_addr)
7011 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7016 mono_gc_weak_link_get (void **link_addr)
7020 return (MonoObject*) REVEAL_POINTER (*link_addr);
7024 mono_gc_ephemeron_array_add (MonoObject *obj)
7026 EphemeronLinkNode *node;
7030 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
7035 node->array = (char*)obj;
7036 node->next = ephemeron_list;
7037 ephemeron_list = node;
7039 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
7046 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7049 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
7050 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7051 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7053 mword complex = alloc_complex_descriptor (bitmap, numbits);
7054 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7058 static void *all_ref_root_descrs [32];
7061 mono_gc_make_root_descr_all_refs (int numbits)
7065 int num_bytes = numbits / 8;
7067 if (numbits < 32 && all_ref_root_descrs [numbits])
7068 return all_ref_root_descrs [numbits];
7070 gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
7071 memset (gc_bitmap, 0xff, num_bytes);
7072 if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT))
7073 gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
7074 else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
7075 gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
7077 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
7078 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
7082 all_ref_root_descrs [numbits] = descr;
7088 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7092 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7093 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7094 user_descriptors [user_descriptors_next ++] = marker;
7100 mono_gc_alloc_fixed (size_t size, void *descr)
7102 /* FIXME: do a single allocation */
7103 void *res = calloc (1, size);
7106 if (!mono_gc_register_root (res, size, descr)) {
7114 mono_gc_free_fixed (void* addr)
7116 mono_gc_deregister_root (addr);
7121 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7125 result = func (data);
7126 UNLOCK_INTERRUPTION;
7131 mono_gc_is_gc_thread (void)
7135 result = mono_thread_info_current () != NULL;
7141 mono_gc_base_init (void)
7143 MonoThreadInfoCallbacks cb;
7146 char *major_collector_opt = NULL;
7147 struct sigaction sinfo;
7153 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
7156 /* already inited */
7159 /* being inited by another thread */
7163 /* we will init it */
7166 g_assert_not_reached ();
7168 } while (result != 0);
7170 LOCK_INIT (gc_mutex);
7172 pagesize = mono_pagesize ();
7173 gc_debug_file = stdout;
7175 cb.thread_register = sgen_thread_register;
7176 cb.thread_unregister = sgen_thread_unregister;
7177 cb.thread_attach = sgen_thread_attach;
7178 mono_threads_init (&cb, sizeof (SgenThreadInfo));
7180 LOCK_INIT (interruption_mutex);
7181 LOCK_INIT (global_remset_mutex);
7182 LOCK_INIT (pin_queue_mutex);
7184 if ((env = getenv ("MONO_GC_PARAMS"))) {
7185 opts = g_strsplit (env, ",", -1);
7186 for (ptr = opts; *ptr; ++ptr) {
7188 if (g_str_has_prefix (opt, "major=")) {
7189 opt = strchr (opt, '=') + 1;
7190 major_collector_opt = g_strdup (opt);
7198 mono_sgen_init_internal_allocator ();
7200 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
7201 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
7202 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
7203 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
7204 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
7205 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
7206 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
7207 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
7208 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
7210 pthread_key_create (&remembered_set_key, NULL);
7212 #ifndef HAVE_KW_THREAD
7213 pthread_key_create (&thread_info_key, NULL);
7217 * This needs to happen before any internal allocations because
7218 * it inits the small id which is required for hazard pointer
7221 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7222 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7224 sigfillset (&sinfo.sa_mask);
7225 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7226 sinfo.sa_sigaction = suspend_handler;
7227 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7228 g_error ("failed sigaction");
7231 sinfo.sa_handler = restart_handler;
7232 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7233 g_error ("failed sigaction");
7236 sigfillset (&suspend_signal_mask);
7237 sigdelset (&suspend_signal_mask, restart_signal_num);
7239 mono_thread_info_attach (&sinfo);
7241 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
7242 mono_sgen_marksweep_init (&major_collector);
7243 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
7244 mono_sgen_marksweep_fixed_init (&major_collector);
7245 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
7246 mono_sgen_marksweep_par_init (&major_collector);
7247 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
7248 mono_sgen_marksweep_fixed_par_init (&major_collector);
7249 } else if (!strcmp (major_collector_opt, "copying")) {
7250 mono_sgen_copying_init (&major_collector);
7252 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
7256 #ifdef SGEN_HAVE_CARDTABLE
7257 use_cardtable = major_collector.supports_cardtable;
7259 use_cardtable = FALSE;
7262 num_workers = mono_cpu_count ();
7263 g_assert (num_workers > 0);
7264 if (num_workers > 16)
7267 /* Keep this the default for now */
7268 conservative_stack_mark = TRUE;
7271 for (ptr = opts; *ptr; ++ptr) {
7273 if (g_str_has_prefix (opt, "major="))
7275 if (g_str_has_prefix (opt, "wbarrier=")) {
7276 opt = strchr (opt, '=') + 1;
7277 if (strcmp (opt, "remset") == 0) {
7278 use_cardtable = FALSE;
7279 } else if (strcmp (opt, "cardtable") == 0) {
7280 if (!use_cardtable) {
7281 if (major_collector.supports_cardtable)
7282 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
7284 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
7290 if (g_str_has_prefix (opt, "max-heap-size=")) {
7291 opt = strchr (opt, '=') + 1;
7292 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
7293 if ((max_heap & (mono_pagesize () - 1))) {
7294 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
7298 fprintf (stderr, "max-heap-size must be an integer.\n");
7303 if (g_str_has_prefix (opt, "workers=")) {
7306 if (!major_collector.is_parallel) {
7307 fprintf (stderr, "The workers= option can only be used for parallel collectors.");
7310 opt = strchr (opt, '=') + 1;
7311 val = strtol (opt, &endptr, 10);
7312 if (!*opt || *endptr) {
7313 fprintf (stderr, "Cannot parse the workers= option value.");
7316 if (val <= 0 || val > 16) {
7317 fprintf (stderr, "The number of workers must be in the range 1 to 16.");
7320 num_workers = (int)val;
7323 if (g_str_has_prefix (opt, "stack-mark=")) {
7324 opt = strchr (opt, '=') + 1;
7325 if (!strcmp (opt, "precise")) {
7326 conservative_stack_mark = FALSE;
7327 } else if (!strcmp (opt, "conservative")) {
7328 conservative_stack_mark = TRUE;
7330 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
7336 if (g_str_has_prefix (opt, "nursery-size=")) {
7338 opt = strchr (opt, '=') + 1;
7339 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
7340 default_nursery_size = val;
7341 #ifdef SGEN_ALIGN_NURSERY
7342 if ((val & (val - 1))) {
7343 fprintf (stderr, "The nursery size must be a power of two.\n");
7347 default_nursery_bits = 0;
7348 while (1 << (++ default_nursery_bits) != default_nursery_size)
7352 fprintf (stderr, "nursery-size must be an integer.\n");
7358 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
7359 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
7360 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7361 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7362 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
7363 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
7364 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
7365 if (major_collector.print_gc_param_usage)
7366 major_collector.print_gc_param_usage ();
7373 if (major_collector.is_parallel)
7374 workers_init (num_workers);
7376 if (major_collector_opt)
7377 g_free (major_collector_opt);
7379 nursery_size = DEFAULT_NURSERY_SIZE;
7380 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7381 init_heap_size_limits (max_heap);
7385 if ((env = getenv ("MONO_GC_DEBUG"))) {
7386 opts = g_strsplit (env, ",", -1);
7387 for (ptr = opts; ptr && *ptr; ptr ++) {
7389 if (opt [0] >= '0' && opt [0] <= '9') {
7390 gc_debug_level = atoi (opt);
7395 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7396 gc_debug_file = fopen (rf, "wb");
7398 gc_debug_file = stderr;
7401 } else if (!strcmp (opt, "collect-before-allocs")) {
7402 collect_before_allocs = 1;
7403 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
7404 char *arg = strchr (opt, '=') + 1;
7405 collect_before_allocs = atoi (arg);
7406 } else if (!strcmp (opt, "check-at-minor-collections")) {
7407 consistency_check_at_minor_collection = TRUE;
7408 nursery_clear_policy = CLEAR_AT_GC;
7409 } else if (!strcmp (opt, "xdomain-checks")) {
7410 xdomain_checks = TRUE;
7411 } else if (!strcmp (opt, "clear-at-gc")) {
7412 nursery_clear_policy = CLEAR_AT_GC;
7413 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
7414 nursery_clear_policy = CLEAR_AT_GC;
7415 } else if (!strcmp (opt, "check-scan-starts")) {
7416 do_scan_starts_check = TRUE;
7417 } else if (!strcmp (opt, "disable-minor")) {
7418 disable_minor_collections = TRUE;
7419 } else if (!strcmp (opt, "disable-major")) {
7420 disable_major_collections = TRUE;
7421 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7422 char *filename = strchr (opt, '=') + 1;
7423 nursery_clear_policy = CLEAR_AT_GC;
7424 heap_dump_file = fopen (filename, "w");
7426 fprintf (heap_dump_file, "<sgen-dump>\n");
7427 #ifdef SGEN_BINARY_PROTOCOL
7428 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7429 char *filename = strchr (opt, '=') + 1;
7430 binary_protocol_init (filename);
7433 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7434 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7435 fprintf (stderr, "Valid options are:\n");
7436 fprintf (stderr, " collect-before-allocs[=<n>]\n");
7437 fprintf (stderr, " check-at-minor-collections\n");
7438 fprintf (stderr, " disable-minor\n");
7439 fprintf (stderr, " disable-major\n");
7440 fprintf (stderr, " xdomain-checks\n");
7441 fprintf (stderr, " clear-at-gc\n");
7448 if (major_collector.post_param_init)
7449 major_collector.post_param_init ();
7451 global_remset = alloc_remset (1024, NULL, FALSE);
7452 global_remset->next = NULL;
7461 mono_gc_get_suspend_signal (void)
7463 return suspend_signal_num;
7473 #ifdef HAVE_KW_THREAD
7474 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7475 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7476 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7477 mono_mb_emit_i4 ((mb), (offset)); \
7482 * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
7483 * where the two are the same.
7486 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7487 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7488 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7489 mono_mb_emit_i4 ((mb), thread_info_key); \
7490 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7491 mono_mb_emit_byte ((mb), CEE_ADD); \
7492 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7495 #define EMIT_TLS_ACCESS(mb,member,dummy) do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
7500 #ifdef MANAGED_ALLOCATION
7501 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7502 * for each class. This is currently not easy to do, as it is hard to generate basic
7503 * blocks + branches, but it is easy with the linear IL codebase.
7505 * For this to work we'd need to solve the TLAB race, first. Now we
7506 * require the allocator to be in a few known methods to make sure
7507 * that they are executed atomically via the restart mechanism.
7510 create_allocator (int atype)
7512 int p_var, size_var;
7513 guint32 slowpath_branch, max_size_branch;
7514 MonoMethodBuilder *mb;
7516 MonoMethodSignature *csig;
7517 static gboolean registered = FALSE;
7518 int tlab_next_addr_var, new_next_var;
7520 const char *name = NULL;
7521 AllocatorWrapperInfo *info;
7523 #ifdef HAVE_KW_THREAD
7524 int tlab_next_addr_offset = -1;
7525 int tlab_temp_end_offset = -1;
7527 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7528 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7530 g_assert (tlab_next_addr_offset != -1);
7531 g_assert (tlab_temp_end_offset != -1);
7535 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7536 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7540 if (atype == ATYPE_SMALL) {
7542 name = "AllocSmall";
7543 } else if (atype == ATYPE_NORMAL) {
7546 } else if (atype == ATYPE_VECTOR) {
7548 name = "AllocVector";
7550 g_assert_not_reached ();
7553 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7554 csig->ret = &mono_defaults.object_class->byval_arg;
7555 for (i = 0; i < num_params; ++i)
7556 csig->params [i] = &mono_defaults.int_class->byval_arg;
7558 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7559 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7560 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7561 /* size = vtable->klass->instance_size; */
7562 mono_mb_emit_ldarg (mb, 0);
7563 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7564 mono_mb_emit_byte (mb, CEE_ADD);
7565 mono_mb_emit_byte (mb, CEE_LDIND_I);
7566 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7567 mono_mb_emit_byte (mb, CEE_ADD);
7568 /* FIXME: assert instance_size stays a 4 byte integer */
7569 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7570 mono_mb_emit_stloc (mb, size_var);
7571 } else if (atype == ATYPE_VECTOR) {
7572 MonoExceptionClause *clause;
7574 MonoClass *oom_exc_class;
7577 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7578 mono_mb_emit_ldarg (mb, 1);
7579 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7580 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7581 mono_mb_emit_exception (mb, "OverflowException", NULL);
7582 mono_mb_patch_short_branch (mb, pos);
7584 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7585 clause->try_offset = mono_mb_get_label (mb);
7587 /* vtable->klass->sizes.element_size */
7588 mono_mb_emit_ldarg (mb, 0);
7589 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7590 mono_mb_emit_byte (mb, CEE_ADD);
7591 mono_mb_emit_byte (mb, CEE_LDIND_I);
7592 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7593 mono_mb_emit_byte (mb, CEE_ADD);
7594 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7597 mono_mb_emit_ldarg (mb, 1);
7598 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7599 /* + sizeof (MonoArray) */
7600 mono_mb_emit_icon (mb, sizeof (MonoArray));
7601 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7602 mono_mb_emit_stloc (mb, size_var);
7604 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7607 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7608 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7609 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7610 "System", "OverflowException");
7611 g_assert (clause->data.catch_class);
7612 clause->handler_offset = mono_mb_get_label (mb);
7614 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7615 "System", "OutOfMemoryException");
7616 g_assert (oom_exc_class);
7617 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7620 mono_mb_emit_byte (mb, CEE_POP);
7621 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7622 mono_mb_emit_byte (mb, CEE_THROW);
7624 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7625 mono_mb_set_clauses (mb, 1, clause);
7626 mono_mb_patch_branch (mb, pos_leave);
7629 g_assert_not_reached ();
7632 /* size += ALLOC_ALIGN - 1; */
7633 mono_mb_emit_ldloc (mb, size_var);
7634 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7635 mono_mb_emit_byte (mb, CEE_ADD);
7636 /* size &= ~(ALLOC_ALIGN - 1); */
7637 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7638 mono_mb_emit_byte (mb, CEE_AND);
7639 mono_mb_emit_stloc (mb, size_var);
7641 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7642 if (atype != ATYPE_SMALL) {
7643 mono_mb_emit_ldloc (mb, size_var);
7644 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7645 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7649 * We need to modify tlab_next, but the JIT only supports reading, so we read
7650 * another tls var holding its address instead.
7653 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7654 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7655 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7656 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7658 /* p = (void**)tlab_next; */
7659 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7660 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7661 mono_mb_emit_byte (mb, CEE_LDIND_I);
7662 mono_mb_emit_stloc (mb, p_var);
7664 /* new_next = (char*)p + size; */
7665 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7666 mono_mb_emit_ldloc (mb, p_var);
7667 mono_mb_emit_ldloc (mb, size_var);
7668 mono_mb_emit_byte (mb, CEE_CONV_I);
7669 mono_mb_emit_byte (mb, CEE_ADD);
7670 mono_mb_emit_stloc (mb, new_next_var);
7672 /* tlab_next = new_next */
7673 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7674 mono_mb_emit_ldloc (mb, new_next_var);
7675 mono_mb_emit_byte (mb, CEE_STIND_I);
7677 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7678 mono_mb_emit_ldloc (mb, new_next_var);
7679 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7680 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7683 if (atype != ATYPE_SMALL)
7684 mono_mb_patch_short_branch (mb, max_size_branch);
7686 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7687 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7689 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7690 mono_mb_emit_ldarg (mb, 0);
7691 mono_mb_emit_ldloc (mb, size_var);
7692 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7693 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7694 } else if (atype == ATYPE_VECTOR) {
7695 mono_mb_emit_ldarg (mb, 1);
7696 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7698 g_assert_not_reached ();
7700 mono_mb_emit_byte (mb, CEE_RET);
7703 mono_mb_patch_short_branch (mb, slowpath_branch);
7705 /* FIXME: Memory barrier */
7708 mono_mb_emit_ldloc (mb, p_var);
7709 mono_mb_emit_ldarg (mb, 0);
7710 mono_mb_emit_byte (mb, CEE_STIND_I);
7712 if (atype == ATYPE_VECTOR) {
7713 /* arr->max_length = max_length; */
7714 mono_mb_emit_ldloc (mb, p_var);
7715 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7716 mono_mb_emit_ldarg (mb, 1);
7717 mono_mb_emit_byte (mb, CEE_STIND_I);
7721 mono_mb_emit_ldloc (mb, p_var);
7722 mono_mb_emit_byte (mb, CEE_RET);
7724 res = mono_mb_create_method (mb, csig, 8);
7726 mono_method_get_header (res)->init_locals = FALSE;
7728 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7729 info->gc_name = "sgen";
7730 info->alloc_type = atype;
7731 mono_marshal_set_wrapper_info (res, info);
7738 mono_gc_get_gc_name (void)
7743 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7744 static MonoMethod *write_barrier_method;
7747 mono_gc_is_critical_method (MonoMethod *method)
7750 if (method == write_barrier_method)
7753 for (i = 0; i < ATYPE_NUM; ++i)
7754 if (method == alloc_method_cache [i])
7761 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7765 if (!mono_thread_internal_current ())
7766 /* Happens during thread attach */
7771 ji = mono_jit_info_table_find (domain, ip);
7775 return mono_gc_is_critical_method (ji->method);
7779 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7780 * The signature of the called method is:
7781 * object allocate (MonoVTable *vtable)
7784 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7786 #ifdef MANAGED_ALLOCATION
7787 MonoClass *klass = vtable->klass;
7789 #ifdef HAVE_KW_THREAD
7790 int tlab_next_offset = -1;
7791 int tlab_temp_end_offset = -1;
7792 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7793 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7795 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7799 if (!mono_runtime_has_tls_get ())
7801 if (klass->instance_size > tlab_size)
7803 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7807 if (klass->byval_arg.type == MONO_TYPE_STRING)
7809 if (collect_before_allocs)
7812 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7813 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7815 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7822 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7824 #ifdef MANAGED_ALLOCATION
7825 MonoClass *klass = vtable->klass;
7827 #ifdef HAVE_KW_THREAD
7828 int tlab_next_offset = -1;
7829 int tlab_temp_end_offset = -1;
7830 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7831 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7833 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7839 if (!mono_runtime_has_tls_get ())
7841 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7843 if (collect_before_allocs)
7845 g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
7847 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7854 mono_gc_get_managed_allocator_by_type (int atype)
7856 #ifdef MANAGED_ALLOCATION
7859 if (!mono_runtime_has_tls_get ())
7862 mono_loader_lock ();
7863 res = alloc_method_cache [atype];
7865 res = alloc_method_cache [atype] = create_allocator (atype);
7866 mono_loader_unlock ();
7874 mono_gc_get_managed_allocator_types (void)
7881 mono_gc_get_write_barrier (void)
7884 MonoMethodBuilder *mb;
7885 MonoMethodSignature *sig;
7886 #ifdef MANAGED_WBARRIER
7887 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7888 #ifndef SGEN_ALIGN_NURSERY
7889 int label_continue_1, label_continue_2, label_no_wb_5;
7890 int dereferenced_var;
7892 int buffer_var, buffer_index_var, dummy_var;
7894 #ifdef HAVE_KW_THREAD
7895 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7896 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7898 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7899 g_assert (stack_end_offset != -1);
7900 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7901 g_assert (store_remset_buffer_offset != -1);
7902 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7903 g_assert (store_remset_buffer_index_offset != -1);
7904 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7905 g_assert (store_remset_buffer_index_addr_offset != -1);
7909 g_assert (!use_cardtable);
7911 // FIXME: Maybe create a separate version for ctors (the branch would be
7912 // correctly predicted more times)
7913 if (write_barrier_method)
7914 return write_barrier_method;
7916 /* Create the IL version of mono_gc_barrier_generic_store () */
7917 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7918 sig->ret = &mono_defaults.void_class->byval_arg;
7919 sig->params [0] = &mono_defaults.int_class->byval_arg;
7921 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7923 #ifdef MANAGED_WBARRIER
7924 if (mono_runtime_has_tls_get ()) {
7925 #ifdef SGEN_ALIGN_NURSERY
7926 // if (ptr_in_nursery (ptr)) return;
7928 * Masking out the bits might be faster, but we would have to use 64 bit
7929 * immediates, which might be slower.
7931 mono_mb_emit_ldarg (mb, 0);
7932 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7933 mono_mb_emit_byte (mb, CEE_SHR_UN);
7934 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7935 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7937 // if (!ptr_in_nursery (*ptr)) return;
7938 mono_mb_emit_ldarg (mb, 0);
7939 mono_mb_emit_byte (mb, CEE_LDIND_I);
7940 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7941 mono_mb_emit_byte (mb, CEE_SHR_UN);
7942 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7943 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7946 // if (ptr < (nursery_start)) goto continue;
7947 mono_mb_emit_ldarg (mb, 0);
7948 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7949 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7951 // if (ptr >= nursery_end)) goto continue;
7952 mono_mb_emit_ldarg (mb, 0);
7953 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7954 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7957 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7960 mono_mb_patch_branch (mb, label_continue_1);
7961 mono_mb_patch_branch (mb, label_continue_2);
7963 // Dereference and store in local var
7964 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7965 mono_mb_emit_ldarg (mb, 0);
7966 mono_mb_emit_byte (mb, CEE_LDIND_I);
7967 mono_mb_emit_stloc (mb, dereferenced_var);
7969 // if (*ptr < nursery_start) return;
7970 mono_mb_emit_ldloc (mb, dereferenced_var);
7971 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7972 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7974 // if (*ptr >= nursery_end) return;
7975 mono_mb_emit_ldloc (mb, dereferenced_var);
7976 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7977 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7980 // if (ptr >= stack_end) goto need_wb;
7981 mono_mb_emit_ldarg (mb, 0);
7982 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7983 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7985 // if (ptr >= stack_start) return;
7986 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7987 mono_mb_emit_ldarg (mb, 0);
7988 mono_mb_emit_ldloc_addr (mb, dummy_var);
7989 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7992 mono_mb_patch_branch (mb, label_need_wb);
7994 // buffer = STORE_REMSET_BUFFER;
7995 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7996 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7997 mono_mb_emit_stloc (mb, buffer_var);
7999 // buffer_index = STORE_REMSET_BUFFER_INDEX;
8000 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8001 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
8002 mono_mb_emit_stloc (mb, buffer_index_var);
8004 // if (buffer [buffer_index] == ptr) return;
8005 mono_mb_emit_ldloc (mb, buffer_var);
8006 mono_mb_emit_ldloc (mb, buffer_index_var);
8007 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8008 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8009 mono_mb_emit_byte (mb, CEE_SHL);
8010 mono_mb_emit_byte (mb, CEE_ADD);
8011 mono_mb_emit_byte (mb, CEE_LDIND_I);
8012 mono_mb_emit_ldarg (mb, 0);
8013 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
8016 mono_mb_emit_ldloc (mb, buffer_index_var);
8017 mono_mb_emit_icon (mb, 1);
8018 mono_mb_emit_byte (mb, CEE_ADD);
8019 mono_mb_emit_stloc (mb, buffer_index_var);
8021 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
8022 mono_mb_emit_ldloc (mb, buffer_index_var);
8023 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
8024 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
8026 // buffer [buffer_index] = ptr;
8027 mono_mb_emit_ldloc (mb, buffer_var);
8028 mono_mb_emit_ldloc (mb, buffer_index_var);
8029 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8030 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8031 mono_mb_emit_byte (mb, CEE_SHL);
8032 mono_mb_emit_byte (mb, CEE_ADD);
8033 mono_mb_emit_ldarg (mb, 0);
8034 mono_mb_emit_byte (mb, CEE_STIND_I);
8036 // STORE_REMSET_BUFFER_INDEX = buffer_index;
8037 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8038 mono_mb_emit_ldloc (mb, buffer_index_var);
8039 mono_mb_emit_byte (mb, CEE_STIND_I);
8042 mono_mb_patch_branch (mb, label_no_wb_1);
8043 mono_mb_patch_branch (mb, label_no_wb_2);
8044 mono_mb_patch_branch (mb, label_no_wb_3);
8045 mono_mb_patch_branch (mb, label_no_wb_4);
8046 #ifndef SGEN_ALIGN_NURSERY
8047 mono_mb_patch_branch (mb, label_no_wb_5);
8049 mono_mb_emit_byte (mb, CEE_RET);
8052 mono_mb_patch_branch (mb, label_slow_path);
8056 mono_mb_emit_ldarg (mb, 0);
8057 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
8058 mono_mb_emit_byte (mb, CEE_RET);
8060 res = mono_mb_create_method (mb, sig, 16);
8063 mono_loader_lock ();
8064 if (write_barrier_method) {
8065 /* Already created */
8066 mono_free_method (res);
8068 /* double-checked locking */
8069 mono_memory_barrier ();
8070 write_barrier_method = res;
8072 mono_loader_unlock ();
8074 return write_barrier_method;
8078 mono_gc_get_description (void)
8080 return g_strdup ("sgen");
8084 mono_gc_set_desktop_mode (void)
8089 mono_gc_is_moving (void)
8095 mono_gc_is_disabled (void)
8101 mono_sgen_debug_printf (int level, const char *format, ...)
8105 if (level > gc_debug_level)
8108 va_start (ap, format);
8109 vfprintf (gc_debug_file, format, ap);
8114 mono_sgen_get_logfile (void)
8116 return gc_debug_file;
8120 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
8126 #endif /* HAVE_SGEN_GC */