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
196 #include "metadata/metadata-internals.h"
197 #include "metadata/class-internals.h"
198 #include "metadata/gc-internal.h"
199 #include "metadata/object-internals.h"
200 #include "metadata/threads.h"
201 #include "metadata/sgen-gc.h"
202 #include "metadata/sgen-cardtable.h"
203 #include "metadata/sgen-protocol.h"
204 #include "metadata/sgen-archdep.h"
205 #include "metadata/sgen-bridge.h"
206 #include "metadata/mono-gc.h"
207 #include "metadata/method-builder.h"
208 #include "metadata/profiler-private.h"
209 #include "metadata/monitor.h"
210 #include "metadata/threadpool-internals.h"
211 #include "metadata/mempool-internals.h"
212 #include "metadata/marshal.h"
213 #include "utils/mono-mmap.h"
214 #include "utils/mono-time.h"
215 #include "utils/mono-semaphore.h"
216 #include "utils/mono-counters.h"
217 #include "utils/mono-proclib.h"
219 #include <mono/utils/memcheck.h>
221 #if defined(__MACH__)
222 #include "utils/mach-support.h"
225 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
229 #include "mono/cil/opcode.def"
235 #undef pthread_create
237 #undef pthread_detach
240 * ######################################################################
241 * ######## Types and constants used by the GC.
242 * ######################################################################
245 static int gc_initialized = 0;
246 /* If set, do a minor collection before every X allocation */
247 static guint32 collect_before_allocs = 0;
248 /* If set, do a heap consistency check before each minor collection */
249 static gboolean consistency_check_at_minor_collection = FALSE;
250 /* If set, check that there are no references to the domain left at domain unload */
251 static gboolean xdomain_checks = FALSE;
252 /* If not null, dump the heap after each collection into this file */
253 static FILE *heap_dump_file = NULL;
254 /* If set, mark stacks conservatively, even if precise marking is possible */
255 static gboolean conservative_stack_mark = FALSE;
256 /* If set, do a plausibility check on the scan_starts before and after
258 static gboolean do_scan_starts_check = FALSE;
260 #ifdef HEAVY_STATISTICS
261 static long long stat_objects_alloced = 0;
262 static long long stat_bytes_alloced = 0;
263 long long stat_objects_alloced_degraded = 0;
264 long long stat_bytes_alloced_degraded = 0;
265 static long long stat_bytes_alloced_los = 0;
267 long long stat_copy_object_called_nursery = 0;
268 long long stat_objects_copied_nursery = 0;
269 long long stat_copy_object_called_major = 0;
270 long long stat_objects_copied_major = 0;
272 long long stat_scan_object_called_nursery = 0;
273 long long stat_scan_object_called_major = 0;
275 long long stat_nursery_copy_object_failed_from_space = 0;
276 long long stat_nursery_copy_object_failed_forwarded = 0;
277 long long stat_nursery_copy_object_failed_pinned = 0;
279 static long long stat_store_remsets = 0;
280 static long long stat_store_remsets_unique = 0;
281 static long long stat_saved_remsets_1 = 0;
282 static long long stat_saved_remsets_2 = 0;
283 static long long stat_local_remsets_processed = 0;
284 static long long stat_global_remsets_added = 0;
285 static long long stat_global_remsets_readded = 0;
286 static long long stat_global_remsets_processed = 0;
287 static long long stat_global_remsets_discarded = 0;
289 static long long stat_wasted_fragments_used = 0;
290 static long long stat_wasted_fragments_bytes = 0;
292 static int stat_wbarrier_set_field = 0;
293 static int stat_wbarrier_set_arrayref = 0;
294 static int stat_wbarrier_arrayref_copy = 0;
295 static int stat_wbarrier_generic_store = 0;
296 static int stat_wbarrier_generic_store_remset = 0;
297 static int stat_wbarrier_set_root = 0;
298 static int stat_wbarrier_value_copy = 0;
299 static int stat_wbarrier_object_copy = 0;
302 static long long stat_pinned_objects = 0;
304 static long long time_minor_pre_collection_fragment_clear = 0;
305 static long long time_minor_pinning = 0;
306 static long long time_minor_scan_remsets = 0;
307 static long long time_minor_scan_card_table = 0;
308 static long long time_minor_scan_pinned = 0;
309 static long long time_minor_scan_registered_roots = 0;
310 static long long time_minor_scan_thread_data = 0;
311 static long long time_minor_finish_gray_stack = 0;
312 static long long time_minor_fragment_creation = 0;
314 static long long time_major_pre_collection_fragment_clear = 0;
315 static long long time_major_pinning = 0;
316 static long long time_major_scan_pinned = 0;
317 static long long time_major_scan_registered_roots = 0;
318 static long long time_major_scan_thread_data = 0;
319 static long long time_major_scan_alloc_pinned = 0;
320 static long long time_major_scan_finalized = 0;
321 static long long time_major_scan_big_objects = 0;
322 static long long time_major_finish_gray_stack = 0;
323 static long long time_major_free_bigobjs = 0;
324 static long long time_major_los_sweep = 0;
325 static long long time_major_sweep = 0;
326 static long long time_major_fragment_creation = 0;
328 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
330 int gc_debug_level = 0;
335 mono_gc_flush_info (void)
337 fflush (gc_debug_file);
342 * Define this to allow the user to change the nursery size by
343 * specifying its value in the MONO_GC_PARAMS environmental
344 * variable. See mono_gc_base_init for details.
346 #define USER_CONFIG 1
348 #define TV_DECLARE SGEN_TV_DECLARE
349 #define TV_GETTIME SGEN_TV_GETTIME
350 #define TV_ELAPSED SGEN_TV_ELAPSED
351 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
353 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
355 /* The method used to clear the nursery */
356 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
357 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
362 CLEAR_AT_TLAB_CREATION
363 } NurseryClearPolicy;
365 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
368 * The young generation is divided into fragments. This is because
369 * we can hand one fragments to a thread for lock-less fast alloc and
370 * because the young generation ends up fragmented anyway by pinned objects.
371 * Once a collection is done, a list of fragments is created. When doing
372 * thread local alloc we use smallish nurseries so we allow new threads to
373 * allocate memory from gen0 without triggering a collection. Threads that
374 * are found to allocate lots of memory are given bigger fragments. This
375 * should make the finalizer thread use little nursery memory after a while.
376 * We should start assigning threads very small fragments: if there are many
377 * threads the nursery will be full of reserved space that the threads may not
378 * use at all, slowing down allocation speed.
379 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
380 * Allocation Buffers (TLABs).
382 typedef struct _Fragment Fragment;
386 char *fragment_start;
387 char *fragment_limit; /* the current soft limit for allocation */
391 /* the runtime can register areas of memory as roots: we keep two lists of roots,
392 * a pinned root set for conservatively scanned roots and a normal one for
393 * precisely scanned roots (currently implemented as a single list).
395 typedef struct _RootRecord RootRecord;
404 * We're never actually using the first element. It's always set to
405 * NULL to simplify the elimination of consecutive duplicate
408 #define STORE_REMSET_BUFFER_SIZE 1024
410 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
411 struct _GenericStoreRememberedSet {
412 GenericStoreRememberedSet *next;
413 /* We need one entry less because the first entry of store
414 remset buffers is always a dummy and we don't copy it. */
415 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
418 /* we have 4 possible values in the low 2 bits */
420 REMSET_LOCATION, /* just a pointer to the exact location */
421 REMSET_RANGE, /* range of pointer fields */
422 REMSET_OBJECT, /* mark all the object for scanning */
423 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
424 REMSET_TYPE_MASK = 0x3
427 #ifdef HAVE_KW_THREAD
428 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
430 static pthread_key_t remembered_set_key;
431 static RememberedSet *global_remset;
432 static RememberedSet *freed_thread_remsets;
433 static GenericStoreRememberedSet *generic_store_remsets = NULL;
435 /*A two slots cache for recently inserted remsets */
436 static gpointer global_remset_cache [2];
438 /* FIXME: later choose a size that takes into account the RememberedSet struct
439 * and doesn't waste any alloc paddin space.
441 #define DEFAULT_REMSET_SIZE 1024
442 static RememberedSet* alloc_remset (int size, gpointer id);
443 static RememberedSet* alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id);
445 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
446 #define object_is_pinned SGEN_OBJECT_IS_PINNED
447 #define pin_object SGEN_PIN_OBJECT
448 #define unpin_object SGEN_UNPIN_OBJECT
450 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_real_end))
452 #define LOAD_VTABLE SGEN_LOAD_VTABLE
455 safe_name (void* obj)
457 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
458 return vt->klass->name;
461 #define safe_object_get_size mono_sgen_safe_object_get_size
464 mono_sgen_safe_name (void* obj)
466 return safe_name (obj);
470 * ######################################################################
471 * ######## Global data.
472 * ######################################################################
474 static LOCK_DECLARE (gc_mutex);
475 static int gc_disabled = 0;
476 static int num_minor_gcs = 0;
477 static int num_major_gcs = 0;
479 static gboolean use_cardtable;
483 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
484 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
485 static int default_nursery_size = (1 << 22);
486 #ifdef SGEN_ALIGN_NURSERY
487 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
488 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
489 static int default_nursery_bits = 22;
494 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
495 #ifdef SGEN_ALIGN_NURSERY
496 #define DEFAULT_NURSERY_BITS 22
501 #ifndef SGEN_ALIGN_NURSERY
502 #define DEFAULT_NURSERY_BITS -1
505 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
507 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
509 /* the minimum size of a fragment that we consider useful for allocation */
510 #define FRAGMENT_MIN_SIZE (512)
512 static mword pagesize = 4096;
513 static mword nursery_size;
514 static int degraded_mode = 0;
516 static mword total_alloc = 0;
517 /* use this to tune when to do a major/minor collection */
518 static mword memory_pressure = 0;
519 static mword minor_collection_allowance;
520 static int minor_collection_sections_alloced = 0;
522 static GCMemSection *nursery_section = NULL;
523 static mword lowest_heap_address = ~(mword)0;
524 static mword highest_heap_address = 0;
526 static LOCK_DECLARE (interruption_mutex);
527 static LOCK_DECLARE (global_remset_mutex);
528 static LOCK_DECLARE (pin_queue_mutex);
530 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
531 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
533 #define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
534 #define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
536 typedef struct _FinalizeEntry FinalizeEntry;
537 struct _FinalizeEntry {
542 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
543 struct _FinalizeEntryHashTable {
544 FinalizeEntry **table;
549 typedef struct _DisappearingLink DisappearingLink;
550 struct _DisappearingLink {
551 DisappearingLink *next;
555 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
556 struct _DisappearingLinkHashTable {
557 DisappearingLink **table;
562 typedef struct _EphemeronLinkNode EphemeronLinkNode;
564 struct _EphemeronLinkNode {
565 EphemeronLinkNode *next;
574 int current_collection_generation = -1;
577 * The link pointer is hidden by negating each bit. We use the lowest
578 * bit of the link (before negation) to store whether it needs
579 * resurrection tracking.
581 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
582 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
584 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
585 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
588 * The finalizable hash has the object as the key, the
589 * disappearing_link hash, has the link address as key.
591 static FinalizeEntryHashTable minor_finalizable_hash;
592 static FinalizeEntryHashTable major_finalizable_hash;
593 /* objects that are ready to be finalized */
594 static FinalizeEntry *fin_ready_list = NULL;
595 static FinalizeEntry *critical_fin_list = NULL;
597 static DisappearingLinkHashTable minor_disappearing_link_hash;
598 static DisappearingLinkHashTable major_disappearing_link_hash;
600 static EphemeronLinkNode *ephemeron_list;
602 static int num_ready_finalizers = 0;
603 static int no_finalize = 0;
606 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
607 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
608 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
612 /* registered roots: the key to the hash is the root start address */
614 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
616 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
617 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
618 static mword roots_size = 0; /* amount of memory in the root set */
619 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
621 #define GC_ROOT_NUM 32
624 void *objects [GC_ROOT_NUM];
625 int root_types [GC_ROOT_NUM];
626 uintptr_t extra_info [GC_ROOT_NUM];
630 notify_gc_roots (GCRootReport *report)
634 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
639 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
641 if (report->count == GC_ROOT_NUM)
642 notify_gc_roots (report);
643 report->objects [report->count] = object;
644 report->root_types [report->count] = rtype;
645 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
649 * The current allocation cursors
650 * We allocate objects in the nursery.
651 * The nursery is the area between nursery_start and nursery_real_end.
652 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
653 * from nursery fragments.
654 * tlab_next is the pointer to the space inside the TLAB where the next object will
656 * tlab_temp_end is the pointer to the end of the temporary space reserved for
657 * the allocation: it allows us to set the scan starts at reasonable intervals.
658 * tlab_real_end points to the end of the TLAB.
659 * nursery_frag_real_end points to the end of the currently used nursery fragment.
660 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
661 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
662 * At the next allocation, the area of the nursery where objects can be present is
663 * between MIN(nursery_first_pinned_start, first_fragment_start) and
664 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
666 static char *nursery_start = NULL;
668 #ifdef HAVE_KW_THREAD
669 #define TLAB_ACCESS_INIT
670 #define TLAB_START tlab_start
671 #define TLAB_NEXT tlab_next
672 #define TLAB_TEMP_END tlab_temp_end
673 #define TLAB_REAL_END tlab_real_end
674 #define REMEMBERED_SET remembered_set
675 #define STORE_REMSET_BUFFER store_remset_buffer
676 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
677 #define IN_CRITICAL_REGION thread_info->in_critical_region
679 static pthread_key_t thread_info_key;
680 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
681 #define TLAB_START (__thread_info__->tlab_start)
682 #define TLAB_NEXT (__thread_info__->tlab_next)
683 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
684 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
685 #define REMEMBERED_SET (__thread_info__->remset)
686 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
687 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
688 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
691 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
692 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
693 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
696 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
697 * variables for next+temp_end ?
699 #ifdef HAVE_KW_THREAD
700 static __thread SgenThreadInfo *thread_info;
701 static __thread char *tlab_start;
702 static __thread char *tlab_next;
703 static __thread char *tlab_temp_end;
704 static __thread char *tlab_real_end;
705 static __thread gpointer *store_remset_buffer;
706 static __thread long store_remset_buffer_index;
707 /* Used by the managed allocator/wbarrier */
708 static __thread char **tlab_next_addr;
709 static __thread char *stack_end;
710 static __thread long *store_remset_buffer_index_addr;
712 static char *nursery_next = NULL;
713 static char *nursery_frag_real_end = NULL;
714 static char *nursery_real_end = NULL;
715 static char *nursery_last_pinned_end = NULL;
717 /* The size of a TLAB */
718 /* The bigger the value, the less often we have to go to the slow path to allocate a new
719 * one, but the more space is wasted by threads not allocating much memory.
721 * FIXME: Make this self-tuning for each thread.
723 static guint32 tlab_size = (1024 * 4);
725 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
726 #define MAX_NURSERY_TLAB_WASTE 512
728 /* fragments that are free and ready to be used for allocation */
729 static Fragment *nursery_fragments = NULL;
730 /* freeelist of fragment structures */
731 static Fragment *fragment_freelist = NULL;
733 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
735 /* Functions supplied by the runtime to be called by the GC */
736 static MonoGCCallbacks gc_callbacks;
738 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
739 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
741 #define ALIGN_UP SGEN_ALIGN_UP
743 #define MOVED_OBJECTS_NUM 64
744 static void *moved_objects [MOVED_OBJECTS_NUM];
745 static int moved_objects_idx = 0;
747 /* Vtable of the objects used to fill out nursery fragments before a collection */
748 static MonoVTable *array_fill_vtable;
750 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
751 pthread_t main_gc_thread = NULL;
755 * ######################################################################
756 * ######## Heap size accounting
757 * ######################################################################
760 static mword max_heap_size = ((mword)0)- ((mword)1);
761 static mword allocated_heap;
763 /*Object was pinned during the current collection*/
764 static mword objects_pinned;
767 mono_sgen_release_space (mword size, int space)
769 allocated_heap -= size;
773 available_free_space (void)
775 return max_heap_size - MIN (allocated_heap, max_heap_size);
779 mono_sgen_try_alloc_space (mword size, int space)
781 if (available_free_space () < size)
784 allocated_heap += size;
789 init_heap_size_limits (glong max_heap)
794 if (max_heap < nursery_size * 4) {
795 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
798 max_heap_size = max_heap - nursery_size;
802 * ######################################################################
803 * ######## Macros and function declarations.
804 * ######################################################################
808 align_pointer (void *ptr)
810 mword p = (mword)ptr;
811 p += sizeof (gpointer) - 1;
812 p &= ~ (sizeof (gpointer) - 1);
816 typedef SgenGrayQueue GrayQueue;
818 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
819 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
821 /* forward declarations */
822 static int stop_world (int generation);
823 static int restart_world (int generation);
824 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
825 static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
826 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
827 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
828 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
829 static void report_finalizer_roots (void);
830 static void report_registered_roots (void);
831 static void find_pinning_ref_from_thread (char *obj, size_t size);
832 static void update_current_thread_stack (void *start);
833 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
834 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
835 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
836 static void null_links_for_domain (MonoDomain *domain, int generation);
837 static gboolean search_fragment_for_size (size_t size);
838 static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
839 static void clear_nursery_fragments (char *next);
840 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
841 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
842 static void optimize_pin_queue (int start_slot);
843 static void clear_remsets (void);
844 static void clear_tlabs (void);
845 static void sort_addresses (void **array, int size);
846 static gboolean drain_gray_stack (GrayQueue *queue, int max_objs);
847 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
848 static gboolean need_major_collection (mword space_needed);
849 static void major_collection (const char *reason);
851 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
853 void describe_ptr (char *ptr);
854 void check_object (char *start);
856 static void check_consistency (void);
857 static void check_major_refs (void);
858 static void check_scan_starts (void);
859 static void check_for_xdomain_refs (void);
860 static void dump_heap (const char *type, int num, const char *reason);
862 void mono_gc_scan_for_specific_ref (MonoObject *key);
864 static void init_stats (void);
866 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
867 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
868 static void null_ephemerons_for_domain (MonoDomain *domain);
870 SgenMajorCollector major_collector;
872 #include "sgen-pinning.c"
873 #include "sgen-pinning-stats.c"
874 #include "sgen-gray.c"
875 #include "sgen-workers.c"
876 #include "sgen-cardtable.c"
878 /* Root bitmap descriptors are simpler: the lower three bits describe the type
879 * and we either have 30/62 bitmap bits or nibble-based run-length,
880 * or a complex descriptor, or a user defined marker function.
883 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
888 ROOT_DESC_TYPE_MASK = 0x7,
889 ROOT_DESC_TYPE_SHIFT = 3,
892 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
894 #define MAX_USER_DESCRIPTORS 16
896 static gsize* complex_descriptors = NULL;
897 static int complex_descriptors_size = 0;
898 static int complex_descriptors_next = 0;
899 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
900 static int user_descriptors_next = 0;
903 alloc_complex_descriptor (gsize *bitmap, int numbits)
907 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
908 nwords = numbits / GC_BITS_PER_WORD + 1;
911 res = complex_descriptors_next;
912 /* linear search, so we don't have duplicates with domain load/unload
913 * this should not be performance critical or we'd have bigger issues
914 * (the number and size of complex descriptors should be small).
916 for (i = 0; i < complex_descriptors_next; ) {
917 if (complex_descriptors [i] == nwords) {
919 for (j = 0; j < nwords - 1; ++j) {
920 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
930 i += complex_descriptors [i];
932 if (complex_descriptors_next + nwords > complex_descriptors_size) {
933 int new_size = complex_descriptors_size * 2 + nwords;
934 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
935 complex_descriptors_size = new_size;
937 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
938 complex_descriptors_next += nwords;
939 complex_descriptors [res] = nwords;
940 for (i = 0; i < nwords - 1; ++i) {
941 complex_descriptors [res + 1 + i] = bitmap [i];
942 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
949 mono_sgen_get_complex_descriptor (GCVTable *vt)
951 return complex_descriptors + (vt->desc >> LOW_TYPE_BITS);
955 * Descriptor builders.
958 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
960 return (void*) DESC_TYPE_RUN_LENGTH;
964 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
966 int first_set = -1, num_set = 0, last_set = -1, i;
968 size_t stored_size = obj_size;
969 for (i = 0; i < numbits; ++i) {
970 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
978 * We don't encode the size of types that don't contain
979 * references because they might not be aligned, i.e. the
980 * bottom two bits might be set, which would clash with the
981 * bits we need to encode the descriptor type. Since we don't
982 * use the encoded size to skip objects, other than for
983 * processing remsets, in which case only the positions of
984 * references are relevant, this is not a problem.
987 return (void*)DESC_TYPE_RUN_LENGTH;
988 g_assert (!(stored_size & 0x3));
989 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
990 /* check run-length encoding first: one byte offset, one byte number of pointers
991 * on 64 bit archs, we can have 3 runs, just one on 32.
992 * It may be better to use nibbles.
995 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
996 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
998 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
999 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
1000 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));
1001 return (void*) desc;
1003 /* we know the 2-word header is ptr-free */
1004 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1005 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1006 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1007 return (void*) desc;
1010 /* we know the 2-word header is ptr-free */
1011 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1012 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1013 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1014 return (void*) desc;
1016 /* it's a complex object ... */
1017 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1018 return (void*) desc;
1021 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1023 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1025 int first_set = -1, num_set = 0, last_set = -1, i;
1026 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1027 for (i = 0; i < numbits; ++i) {
1028 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1035 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
1037 return (void*)DESC_TYPE_RUN_LENGTH;
1038 if (elem_size <= MAX_ELEMENT_SIZE) {
1039 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1041 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1043 /* Note: we also handle structs with just ref fields */
1044 if (num_set * sizeof (gpointer) == elem_size) {
1045 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1047 /* FIXME: try run-len first */
1048 /* Note: we can't skip the object header here, because it's not present */
1049 if (last_set <= SMALL_BITMAP_SIZE) {
1050 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1053 /* it's am array of complex structs ... */
1054 desc = DESC_TYPE_COMPLEX_ARR;
1055 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1056 return (void*) desc;
1059 /* Return the bitmap encoded by a descriptor */
1061 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1063 mword d = (mword)descr;
1067 case DESC_TYPE_RUN_LENGTH: {
1068 int first_set = (d >> 16) & 0xff;
1069 int num_set = (d >> 24) & 0xff;
1072 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1074 for (i = first_set; i < first_set + num_set; ++i)
1075 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1077 *numbits = first_set + num_set;
1081 case DESC_TYPE_SMALL_BITMAP:
1082 bitmap = g_new0 (gsize, 1);
1084 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1086 *numbits = GC_BITS_PER_WORD;
1090 g_assert_not_reached ();
1095 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1097 MonoObject *o = (MonoObject*)(obj);
1098 MonoObject *ref = (MonoObject*)*(ptr);
1099 int offset = (char*)(ptr) - (char*)o;
1101 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1103 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1105 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1106 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1108 /* Thread.cached_culture_info */
1109 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1110 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1111 !strcmp(o->vtable->klass->name_space, "System") &&
1112 !strcmp(o->vtable->klass->name, "Object[]"))
1115 * 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
1116 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1117 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1118 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1119 * 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
1120 * 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
1121 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1122 * 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
1123 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1125 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1126 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1127 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1128 !strcmp (o->vtable->klass->name, "MemoryStream"))
1130 /* append_job() in threadpool.c */
1131 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1132 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1133 !strcmp (o->vtable->klass->name_space, "System") &&
1134 !strcmp (o->vtable->klass->name, "Object[]") &&
1135 mono_thread_pool_is_queue_array ((MonoArray*) o))
1141 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1143 MonoObject *o = (MonoObject*)(obj);
1144 MonoObject *ref = (MonoObject*)*(ptr);
1145 int offset = (char*)(ptr) - (char*)o;
1147 MonoClassField *field;
1150 if (!ref || ref->vtable->domain == domain)
1152 if (is_xdomain_ref_allowed (ptr, obj, domain))
1156 for (class = o->vtable->klass; class; class = class->parent) {
1159 for (i = 0; i < class->field.count; ++i) {
1160 if (class->fields[i].offset == offset) {
1161 field = &class->fields[i];
1169 if (ref->vtable->klass == mono_defaults.string_class)
1170 str = mono_string_to_utf8 ((MonoString*)ref);
1173 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1174 o, o->vtable->klass->name_space, o->vtable->klass->name,
1175 offset, field ? field->name : "",
1176 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1177 mono_gc_scan_for_specific_ref (o);
1183 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1186 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1188 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1190 #include "sgen-scan-object.h"
1194 #define HANDLE_PTR(ptr,obj) do { \
1195 if ((MonoObject*)*(ptr) == key) { \
1196 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1197 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1202 scan_object_for_specific_ref (char *start, MonoObject *key)
1206 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1209 #include "sgen-scan-object.h"
1213 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1215 while (start < end) {
1219 if (!*(void**)start) {
1220 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1225 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1231 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1233 callback (obj, size, data);
1240 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1242 scan_object_for_specific_ref (obj, key);
1246 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1250 g_print ("found ref to %p in root record %p\n", key, root);
1253 static MonoObject *check_key = NULL;
1254 static RootRecord *check_root = NULL;
1257 check_root_obj_specific_ref_from_marker (void **obj)
1259 check_root_obj_specific_ref (check_root, check_key, *obj);
1263 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1268 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1269 for (root = roots_hash [root_type][i]; root; root = root->next) {
1270 void **start_root = (void**)root->start_root;
1271 mword desc = root->root_desc;
1275 switch (desc & ROOT_DESC_TYPE_MASK) {
1276 case ROOT_DESC_BITMAP:
1277 desc >>= ROOT_DESC_TYPE_SHIFT;
1280 check_root_obj_specific_ref (root, key, *start_root);
1285 case ROOT_DESC_COMPLEX: {
1286 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1287 int bwords = (*bitmap_data) - 1;
1288 void **start_run = start_root;
1290 while (bwords-- > 0) {
1291 gsize bmap = *bitmap_data++;
1292 void **objptr = start_run;
1295 check_root_obj_specific_ref (root, key, *objptr);
1299 start_run += GC_BITS_PER_WORD;
1303 case ROOT_DESC_USER: {
1304 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1305 marker (start_root, check_root_obj_specific_ref_from_marker);
1308 case ROOT_DESC_RUN_LEN:
1309 g_assert_not_reached ();
1311 g_assert_not_reached ();
1320 mono_gc_scan_for_specific_ref (MonoObject *key)
1325 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1326 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1328 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1330 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1332 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1333 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1335 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1336 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1337 void **ptr = (void**)root->start_root;
1339 while (ptr < (void**)root->end_root) {
1340 check_root_obj_specific_ref (root, *ptr, key);
1348 clear_current_nursery_fragment (char *next)
1350 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1351 g_assert (next <= nursery_frag_real_end);
1352 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", next, nursery_frag_real_end));
1353 memset (next, 0, nursery_frag_real_end - next);
1357 /* Clear all remaining nursery fragments */
1359 clear_nursery_fragments (char *next)
1362 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1363 clear_current_nursery_fragment (next);
1364 for (frag = nursery_fragments; frag; frag = frag->next) {
1365 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_start, frag->fragment_end));
1366 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1372 need_remove_object_for_domain (char *start, MonoDomain *domain)
1374 if (mono_object_domain (start) == domain) {
1375 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1376 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1383 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1385 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1386 if (vt->klass == mono_defaults.internal_thread_class)
1387 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1388 /* The object could be a proxy for an object in the domain
1390 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1391 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1393 /* The server could already have been zeroed out, so
1394 we need to check for that, too. */
1395 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1396 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1398 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1403 static MonoDomain *check_domain = NULL;
1406 check_obj_not_in_domain (void **o)
1408 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1412 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1416 check_domain = domain;
1417 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1418 for (root = roots_hash [root_type][i]; root; root = root->next) {
1419 void **start_root = (void**)root->start_root;
1420 mword desc = root->root_desc;
1422 /* The MonoDomain struct is allowed to hold
1423 references to objects in its own domain. */
1424 if (start_root == (void**)domain)
1427 switch (desc & ROOT_DESC_TYPE_MASK) {
1428 case ROOT_DESC_BITMAP:
1429 desc >>= ROOT_DESC_TYPE_SHIFT;
1431 if ((desc & 1) && *start_root)
1432 check_obj_not_in_domain (*start_root);
1437 case ROOT_DESC_COMPLEX: {
1438 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1439 int bwords = (*bitmap_data) - 1;
1440 void **start_run = start_root;
1442 while (bwords-- > 0) {
1443 gsize bmap = *bitmap_data++;
1444 void **objptr = start_run;
1446 if ((bmap & 1) && *objptr)
1447 check_obj_not_in_domain (*objptr);
1451 start_run += GC_BITS_PER_WORD;
1455 case ROOT_DESC_USER: {
1456 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1457 marker (start_root, check_obj_not_in_domain);
1460 case ROOT_DESC_RUN_LEN:
1461 g_assert_not_reached ();
1463 g_assert_not_reached ();
1467 check_domain = NULL;
1471 check_for_xdomain_refs (void)
1475 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1476 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1478 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1480 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1481 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1485 clear_domain_process_object (char *obj, MonoDomain *domain)
1489 process_object_for_domain_clearing (obj, domain);
1490 remove = need_remove_object_for_domain (obj, domain);
1492 if (remove && ((MonoObject*)obj)->synchronisation) {
1493 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1495 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1502 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1504 if (clear_domain_process_object (obj, domain))
1505 memset (obj, 0, size);
1509 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1511 clear_domain_process_object (obj, domain);
1515 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1517 if (need_remove_object_for_domain (obj, domain))
1518 major_collector.free_non_pinned_object (obj, size);
1522 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1524 if (need_remove_object_for_domain (obj, domain))
1525 major_collector.free_pinned_object (obj, size);
1529 * When appdomains are unloaded we can easily remove objects that have finalizers,
1530 * but all the others could still be present in random places on the heap.
1531 * We need a sweep to get rid of them even though it's going to be costly
1533 * The reason we need to remove them is because we access the vtable and class
1534 * structures to know the object size and the reference bitmap: once the domain is
1535 * unloaded the point to random memory.
1538 mono_gc_clear_domain (MonoDomain * domain)
1540 LOSObject *bigobj, *prev;
1545 clear_nursery_fragments (nursery_next);
1547 if (xdomain_checks && domain != mono_get_root_domain ()) {
1548 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1549 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1550 check_for_xdomain_refs ();
1553 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1554 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1556 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1557 to memory returned to the OS.*/
1558 null_ephemerons_for_domain (domain);
1560 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1561 null_links_for_domain (domain, i);
1563 /* We need two passes over major and large objects because
1564 freeing such objects might give their memory back to the OS
1565 (in the case of large objects) or obliterate its vtable
1566 (pinned objects with major-copying or pinned and non-pinned
1567 objects with major-mark&sweep), but we might need to
1568 dereference a pointer from an object to another object if
1569 the first object is a proxy. */
1570 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1571 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1572 clear_domain_process_object (bigobj->data, domain);
1575 for (bigobj = los_object_list; bigobj;) {
1576 if (need_remove_object_for_domain (bigobj->data, domain)) {
1577 LOSObject *to_free = bigobj;
1579 prev->next = bigobj->next;
1581 los_object_list = bigobj->next;
1582 bigobj = bigobj->next;
1583 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1585 mono_sgen_los_free_object (to_free);
1589 bigobj = bigobj->next;
1591 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1592 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1598 global_remset_cache_clear (void)
1600 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1604 * Tries to check if a given remset location was already added to the global remset.
1607 * A 2 entry, LRU cache of recently saw location remsets.
1609 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1611 * Returns TRUE is the element was added..
1614 global_remset_location_was_not_added (gpointer ptr)
1617 gpointer first = global_remset_cache [0], second;
1619 HEAVY_STAT (++stat_global_remsets_discarded);
1623 second = global_remset_cache [1];
1625 if (second == ptr) {
1626 /*Move the second to the front*/
1627 global_remset_cache [0] = second;
1628 global_remset_cache [1] = first;
1630 HEAVY_STAT (++stat_global_remsets_discarded);
1634 global_remset_cache [0] = second;
1635 global_remset_cache [1] = ptr;
1640 * mono_sgen_add_to_global_remset:
1642 * The global remset contains locations which point into newspace after
1643 * a minor collection. This can happen if the objects they point to are pinned.
1645 * LOCKING: If called from a parallel collector, the global remset
1646 * lock must be held. For serial collectors that is not necessary.
1649 mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr)
1652 gboolean lock = major_collector.is_parallel;
1654 if (use_cardtable) {
1655 sgen_card_table_mark_address ((mword)ptr);
1659 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1664 if (!global_remset_location_was_not_added (ptr))
1667 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1668 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1670 HEAVY_STAT (++stat_global_remsets_added);
1673 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1674 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1676 if (global_remset->store_next + 3 < global_remset->end_set) {
1677 *(global_remset->store_next++) = (mword)ptr;
1680 rs = alloc_global_remset (alc, global_remset->end_set - global_remset->data, NULL);
1681 rs->next = global_remset;
1683 *(global_remset->store_next++) = (mword)ptr;
1686 int global_rs_size = 0;
1688 for (rs = global_remset; rs; rs = rs->next) {
1689 global_rs_size += rs->store_next - rs->data;
1691 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1696 UNLOCK_GLOBAL_REMSET;
1702 * Scan objects in the gray stack until the stack is empty. This should be called
1703 * frequently after each object is copied, to achieve better locality and cache
1707 drain_gray_stack (GrayQueue *queue, int max_objs)
1711 if (current_collection_generation == GENERATION_NURSERY) {
1713 GRAY_OBJECT_DEQUEUE (queue, obj);
1716 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1717 major_collector.minor_scan_object (obj, queue);
1722 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1726 for (i = 0; i != max_objs; ++i) {
1727 GRAY_OBJECT_DEQUEUE (queue, obj);
1730 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1731 major_collector.major_scan_object (obj, queue);
1733 } while (max_objs < 0);
1739 * Addresses from start to end are already sorted. This function finds
1740 * the object header for each address and pins the object. The
1741 * addresses must be inside the passed section. The (start of the)
1742 * address array is overwritten with the addresses of the actually
1743 * pinned objects. Return the number of pinned objects.
1746 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1751 void *last_obj = NULL;
1752 size_t last_obj_size = 0;
1755 void **definitely_pinned = start;
1759 * The code below starts the search from an entry in scan_starts, which might point into a nursery
1760 * fragment containing random data. Clearing the nursery fragments takes a lot of time, and searching
1761 * though them too, so lay arrays at each location inside a fragment where a search can start:
1762 * - scan_locations[i]
1764 * - the start of each fragment (the last_obj + last_obj case)
1765 * The third encompasses the first two, since scan_locations [i] can't point inside a nursery fragment.
1767 for (frag = nursery_fragments; frag; frag = frag->next) {
1770 g_assert (frag->fragment_end - frag->fragment_start >= sizeof (MonoArray));
1771 o = (MonoArray*)frag->fragment_start;
1772 memset (o, 0, sizeof (MonoArray));
1773 g_assert (array_fill_vtable);
1774 o->obj.vtable = array_fill_vtable;
1775 /* Mark this as not a real object */
1776 o->obj.synchronisation = GINT_TO_POINTER (-1);
1777 o->max_length = (frag->fragment_end - frag->fragment_start) - sizeof (MonoArray);
1778 g_assert (frag->fragment_start + safe_object_get_size ((MonoObject*)o) == frag->fragment_end);
1781 while (start < end) {
1783 /* the range check should be reduntant */
1784 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1785 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1786 /* multiple pointers to the same object */
1787 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1791 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1792 g_assert (idx < section->num_scan_start);
1793 search_start = (void*)section->scan_starts [idx];
1794 if (!search_start || search_start > addr) {
1797 search_start = section->scan_starts [idx];
1798 if (search_start && search_start <= addr)
1801 if (!search_start || search_start > addr)
1802 search_start = start_nursery;
1804 if (search_start < last_obj)
1805 search_start = (char*)last_obj + last_obj_size;
1806 /* now addr should be in an object a short distance from search_start
1807 * Note that search_start must point to zeroed mem or point to an object.
1811 if (!*(void**)search_start) {
1812 /* Consistency check */
1814 for (frag = nursery_fragments; frag; frag = frag->next) {
1815 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1816 g_assert_not_reached ();
1820 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1823 last_obj = search_start;
1824 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1826 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1827 /* Marks the beginning of a nursery fragment, skip */
1829 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1830 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1831 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));
1832 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1833 pin_object (search_start);
1834 GRAY_OBJECT_ENQUEUE (queue, search_start);
1836 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1837 definitely_pinned [count] = search_start;
1842 /* skip to the next object */
1843 search_start = (void*)((char*)search_start + last_obj_size);
1844 } while (search_start <= addr);
1845 /* we either pinned the correct object or we ignored the addr because
1846 * it points to unused zeroed memory.
1852 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1853 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1854 GCRootReport report;
1856 for (idx = 0; idx < count; ++idx)
1857 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
1858 notify_gc_roots (&report);
1860 stat_pinned_objects += count;
1865 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1867 int num_entries = section->pin_queue_num_entries;
1869 void **start = section->pin_queue_start;
1871 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1872 section->data, section->next_data, queue);
1873 section->pin_queue_num_entries = reduced_to;
1875 section->pin_queue_start = NULL;
1881 mono_sgen_pin_object (void *object, GrayQueue *queue)
1883 if (major_collector.is_parallel) {
1885 /*object arrives pinned*/
1886 pin_stage_ptr (object);
1890 SGEN_PIN_OBJECT (object);
1891 pin_stage_ptr (object);
1894 GRAY_OBJECT_ENQUEUE (queue, object);
1897 /* Sort the addresses in array in increasing order.
1898 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1901 sort_addresses (void **array, int size)
1906 for (i = 1; i < size; ++i) {
1909 int parent = (child - 1) / 2;
1911 if (array [parent] >= array [child])
1914 tmp = array [parent];
1915 array [parent] = array [child];
1916 array [child] = tmp;
1922 for (i = size - 1; i > 0; --i) {
1925 array [i] = array [0];
1931 while (root * 2 + 1 <= end) {
1932 int child = root * 2 + 1;
1934 if (child < end && array [child] < array [child + 1])
1936 if (array [root] >= array [child])
1940 array [root] = array [child];
1941 array [child] = tmp;
1948 static G_GNUC_UNUSED void
1949 print_nursery_gaps (void* start_nursery, void *end_nursery)
1952 gpointer first = start_nursery;
1954 for (i = 0; i < next_pin_slot; ++i) {
1955 next = pin_queue [i];
1956 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1960 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1963 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1965 optimize_pin_queue (int start_slot)
1967 void **start, **cur, **end;
1968 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1969 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1970 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1971 if ((next_pin_slot - start_slot) > 1)
1972 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1973 start = cur = pin_queue + start_slot;
1974 end = pin_queue + next_pin_slot;
1977 while (*start == *cur && cur < end)
1981 next_pin_slot = start - pin_queue;
1982 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1983 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1988 * Scan the memory between start and end and queue values which could be pointers
1989 * to the area between start_nursery and end_nursery for later consideration.
1990 * Typically used for thread stacks.
1993 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1996 while (start < end) {
1997 if (*start >= start_nursery && *start < end_nursery) {
1999 * *start can point to the middle of an object
2000 * note: should we handle pointing at the end of an object?
2001 * pinning in C# code disallows pointing at the end of an object
2002 * but there is some small chance that an optimizing C compiler
2003 * may keep the only reference to an object by pointing
2004 * at the end of it. We ignore this small chance for now.
2005 * Pointers to the end of an object are indistinguishable
2006 * from pointers to the start of the next object in memory
2007 * so if we allow that we'd need to pin two objects...
2008 * We queue the pointer in an array, the
2009 * array will then be sorted and uniqued. This way
2010 * we can coalesce several pinning pointers and it should
2011 * be faster since we'd do a memory scan with increasing
2012 * addresses. Note: we can align the address to the allocation
2013 * alignment, so the unique process is more effective.
2015 mword addr = (mword)*start;
2016 addr &= ~(ALLOC_ALIGN - 1);
2017 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2018 pin_stage_ptr ((void*)addr);
2020 pin_stats_register_address ((char*)addr, pin_type);
2021 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
2026 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2030 * Debugging function: find in the conservative roots where @obj is being pinned.
2032 static G_GNUC_UNUSED void
2033 find_pinning_reference (char *obj, size_t size)
2037 char *endobj = obj + size;
2038 for (i = 0; i < roots_hash_size [0]; ++i) {
2039 for (root = roots_hash [0][i]; root; root = root->next) {
2040 /* if desc is non-null it has precise info */
2041 if (!root->root_desc) {
2042 char ** start = (char**)root->start_root;
2043 while (start < (char**)root->end_root) {
2044 if (*start >= obj && *start < endobj) {
2045 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));
2052 find_pinning_ref_from_thread (obj, size);
2056 * The first thing we do in a collection is to identify pinned objects.
2057 * This function considers all the areas of memory that need to be
2058 * conservatively scanned.
2061 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
2065 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]));
2066 /* objects pinned from the API are inside these roots */
2067 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2068 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2069 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2070 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2073 /* now deal with the thread stacks
2074 * in the future we should be able to conservatively scan only:
2075 * *) the cpu registers
2076 * *) the unmanaged stack frames
2077 * *) the _last_ managed stack frame
2078 * *) pointers slots in managed frames
2080 scan_thread_data (start_nursery, end_nursery, FALSE, queue);
2082 evacuate_pin_staging_area ();
2086 CopyOrMarkObjectFunc func;
2088 } UserCopyOrMarkData;
2090 static pthread_key_t user_copy_or_mark_key;
2093 init_user_copy_or_mark_key (void)
2095 pthread_key_create (&user_copy_or_mark_key, NULL);
2099 set_user_copy_or_mark_data (UserCopyOrMarkData *data)
2101 static pthread_once_t init_control = PTHREAD_ONCE_INIT;
2102 pthread_once (&init_control, init_user_copy_or_mark_key);
2104 pthread_setspecific (user_copy_or_mark_key, data);
2108 single_arg_user_copy_or_mark (void **obj)
2110 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
2112 data->func (obj, data->queue);
2116 * The memory area from start_root to end_root contains pointers to objects.
2117 * Their position is precisely described by @desc (this means that the pointer
2118 * can be either NULL or the pointer to the start of an object).
2119 * This functions copies them to to_space updates them.
2121 * This function is not thread-safe!
2124 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2126 switch (desc & ROOT_DESC_TYPE_MASK) {
2127 case ROOT_DESC_BITMAP:
2128 desc >>= ROOT_DESC_TYPE_SHIFT;
2130 if ((desc & 1) && *start_root) {
2131 copy_func (start_root, queue);
2132 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2133 drain_gray_stack (queue, -1);
2139 case ROOT_DESC_COMPLEX: {
2140 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2141 int bwords = (*bitmap_data) - 1;
2142 void **start_run = start_root;
2144 while (bwords-- > 0) {
2145 gsize bmap = *bitmap_data++;
2146 void **objptr = start_run;
2148 if ((bmap & 1) && *objptr) {
2149 copy_func (objptr, queue);
2150 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2151 drain_gray_stack (queue, -1);
2156 start_run += GC_BITS_PER_WORD;
2160 case ROOT_DESC_USER: {
2161 UserCopyOrMarkData data = { copy_func, queue };
2162 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2163 set_user_copy_or_mark_data (&data);
2164 marker (start_root, single_arg_user_copy_or_mark);
2165 set_user_copy_or_mark_data (NULL);
2168 case ROOT_DESC_RUN_LEN:
2169 g_assert_not_reached ();
2171 g_assert_not_reached ();
2176 reset_heap_boundaries (void)
2178 lowest_heap_address = ~(mword)0;
2179 highest_heap_address = 0;
2183 mono_sgen_update_heap_boundaries (mword low, mword high)
2188 old = lowest_heap_address;
2191 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2194 old = highest_heap_address;
2197 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2201 alloc_fragment (void)
2203 Fragment *frag = fragment_freelist;
2205 fragment_freelist = frag->next;
2209 frag = mono_sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
2214 /* size must be a power of 2 */
2216 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2218 /* Allocate twice the memory to be able to put the block on an aligned address */
2219 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2224 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2225 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2228 mono_sgen_free_os_memory (mem, aligned - mem);
2229 if (aligned + size < mem + size + alignment)
2230 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2236 * Allocate and setup the data structures needed to be able to allocate objects
2237 * in the nursery. The nursery is stored in nursery_section.
2240 alloc_nursery (void)
2242 GCMemSection *section;
2248 if (nursery_section)
2250 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2251 /* later we will alloc a larger area for the nursery but only activate
2252 * what we need. The rest will be used as expansion if we have too many pinned
2253 * objects in the existing nursery.
2255 /* FIXME: handle OOM */
2256 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2258 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2259 alloc_size = nursery_size;
2260 #ifdef SGEN_ALIGN_NURSERY
2261 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2263 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2265 nursery_start = data;
2266 nursery_real_end = nursery_start + nursery_size;
2267 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
2268 nursery_next = nursery_start;
2269 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));
2270 section->data = section->next_data = data;
2271 section->size = alloc_size;
2272 section->end_data = nursery_real_end;
2273 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2274 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2275 section->num_scan_start = scan_starts;
2276 section->block.role = MEMORY_ROLE_GEN0;
2277 section->block.next = NULL;
2279 nursery_section = section;
2281 /* Setup the single first large fragment */
2282 frag = alloc_fragment ();
2283 frag->fragment_start = nursery_start;
2284 frag->fragment_limit = nursery_start;
2285 frag->fragment_end = nursery_real_end;
2286 nursery_frag_real_end = nursery_real_end;
2287 /* FIXME: frag here is lost */
2291 mono_gc_get_nursery (int *shift_bits, size_t *size)
2293 *size = nursery_size;
2294 #ifdef SGEN_ALIGN_NURSERY
2295 *shift_bits = DEFAULT_NURSERY_BITS;
2299 return nursery_start;
2303 mono_gc_precise_stack_mark_enabled (void)
2305 return !conservative_stack_mark;
2309 mono_gc_get_logfile (void)
2311 return mono_sgen_get_logfile ();
2315 report_finalizer_roots_list (FinalizeEntry *list)
2317 GCRootReport report;
2321 for (fin = list; fin; fin = fin->next) {
2324 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2326 notify_gc_roots (&report);
2330 report_finalizer_roots (void)
2332 report_finalizer_roots_list (fin_ready_list);
2333 report_finalizer_roots_list (critical_fin_list);
2336 static GCRootReport *root_report;
2339 single_arg_report_root (void **obj)
2342 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2346 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2348 switch (desc & ROOT_DESC_TYPE_MASK) {
2349 case ROOT_DESC_BITMAP:
2350 desc >>= ROOT_DESC_TYPE_SHIFT;
2352 if ((desc & 1) && *start_root) {
2353 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2359 case ROOT_DESC_COMPLEX: {
2360 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2361 int bwords = (*bitmap_data) - 1;
2362 void **start_run = start_root;
2364 while (bwords-- > 0) {
2365 gsize bmap = *bitmap_data++;
2366 void **objptr = start_run;
2368 if ((bmap & 1) && *objptr) {
2369 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2374 start_run += GC_BITS_PER_WORD;
2378 case ROOT_DESC_USER: {
2379 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2380 root_report = report;
2381 marker (start_root, single_arg_report_root);
2384 case ROOT_DESC_RUN_LEN:
2385 g_assert_not_reached ();
2387 g_assert_not_reached ();
2392 report_registered_roots_by_type (int root_type)
2394 GCRootReport report;
2398 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2399 for (root = roots_hash [root_type][i]; root; root = root->next) {
2400 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2401 precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
2404 notify_gc_roots (&report);
2408 report_registered_roots (void)
2410 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2411 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2415 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2419 for (fin = list; fin; fin = fin->next) {
2422 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2423 copy_func (&fin->object, queue);
2427 static mword fragment_total = 0;
2429 * We found a fragment of free memory in the nursery: memzero it and if
2430 * it is big enough, add it to the list of fragments that can be used for
2434 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2437 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2438 binary_protocol_empty (frag_start, frag_size);
2439 /* Not worth dealing with smaller fragments: need to tune */
2440 if (frag_size >= FRAGMENT_MIN_SIZE) {
2441 /* memsetting just the first chunk start is bound to provide better cache locality */
2442 if (nursery_clear_policy == CLEAR_AT_GC)
2443 memset (frag_start, 0, frag_size);
2445 fragment = alloc_fragment ();
2446 fragment->fragment_start = frag_start;
2447 fragment->fragment_limit = frag_start;
2448 fragment->fragment_end = frag_end;
2449 fragment->next = nursery_fragments;
2450 nursery_fragments = fragment;
2451 fragment_total += frag_size;
2453 /* Clear unused fragments, pinning depends on this */
2454 /*TODO place an int[] here instead of the memset if size justify it*/
2455 memset (frag_start, 0, frag_size);
2460 generation_name (int generation)
2462 switch (generation) {
2463 case GENERATION_NURSERY: return "nursery";
2464 case GENERATION_OLD: return "old";
2465 default: g_assert_not_reached ();
2469 static DisappearingLinkHashTable*
2470 get_dislink_hash_table (int generation)
2472 switch (generation) {
2473 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2474 case GENERATION_OLD: return &major_disappearing_link_hash;
2475 default: g_assert_not_reached ();
2479 static FinalizeEntryHashTable*
2480 get_finalize_entry_hash_table (int generation)
2482 switch (generation) {
2483 case GENERATION_NURSERY: return &minor_finalizable_hash;
2484 case GENERATION_OLD: return &major_finalizable_hash;
2485 default: g_assert_not_reached ();
2489 static MonoObject **finalized_array = NULL;
2490 static int finalized_array_capacity = 0;
2491 static int finalized_array_entries = 0;
2494 bridge_register_finalized_object (MonoObject *object)
2496 if (!finalized_array)
2499 if (finalized_array_entries >= finalized_array_capacity) {
2500 MonoObject **new_array;
2501 g_assert (finalized_array_entries == finalized_array_capacity);
2502 finalized_array_capacity *= 2;
2503 new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2504 memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
2505 mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
2506 finalized_array = new_array;
2508 finalized_array [finalized_array_entries++] = object;
2512 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2517 int ephemeron_rounds = 0;
2519 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2522 * We copied all the reachable objects. Now it's the time to copy
2523 * the objects that were not referenced by the roots, but by the copied objects.
2524 * we built a stack of objects pointed to by gray_start: they are
2525 * additional roots and we may add more items as we go.
2526 * We loop until gray_start == gray_objects which means no more objects have
2527 * been added. Note this is iterative: no recursion is involved.
2528 * We need to walk the LO list as well in search of marked big objects
2529 * (use a flag since this is needed only on major collections). We need to loop
2530 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2531 * To achieve better cache locality and cache usage, we drain the gray stack
2532 * frequently, after each object is copied, and just finish the work here.
2534 drain_gray_stack (queue, -1);
2536 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2539 We must clear weak links that don't track resurrection before processing object ready for
2540 finalization so they can be cleared before that.
2542 null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
2543 if (generation == GENERATION_OLD)
2544 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
2546 if (finalized_array == NULL && mono_sgen_need_bridge_processing ()) {
2547 finalized_array_capacity = 32;
2548 finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2550 finalized_array_entries = 0;
2552 /* walk the finalization queue and move also the objects that need to be
2553 * finalized: use the finalized objects as new roots so the objects they depend
2554 * on are also not reclaimed. As with the roots above, only objects in the nursery
2555 * are marked/copied.
2556 * We need a loop here, since objects ready for finalizers may reference other objects
2557 * that are fin-ready. Speedup with a flag?
2562 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2563 * before processing finalizable objects to avoid finalizing reachable values.
2565 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2566 * while they are been finalized.
2568 int done_with_ephemerons = 0;
2570 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2571 drain_gray_stack (queue, -1);
2573 } while (!done_with_ephemerons);
2575 fin_ready = num_ready_finalizers;
2576 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2577 if (generation == GENERATION_OLD)
2578 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
2580 if (fin_ready != num_ready_finalizers) {
2582 if (finalized_array != NULL)
2583 mono_sgen_bridge_processing (finalized_array_entries, finalized_array);
2586 /* drain the new stack that might have been created */
2587 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2588 drain_gray_stack (queue, -1);
2589 } while (fin_ready != num_ready_finalizers);
2591 if (mono_sgen_need_bridge_processing ())
2592 g_assert (num_loops <= 1);
2595 * Clear ephemeron pairs with unreachable keys.
2596 * We pass the copy func so we can figure out if an array was promoted or not.
2598 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2601 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));
2604 * handle disappearing links
2605 * Note we do this after checking the finalization queue because if an object
2606 * survives (at least long enough to be finalized) we don't clear the link.
2607 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2608 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2611 g_assert (gray_object_queue_is_empty (queue));
2613 null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
2614 if (generation == GENERATION_OLD)
2615 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
2616 if (gray_object_queue_is_empty (queue))
2618 drain_gray_stack (queue, -1);
2621 g_assert (gray_object_queue_is_empty (queue));
2625 mono_sgen_check_section_scan_starts (GCMemSection *section)
2628 for (i = 0; i < section->num_scan_start; ++i) {
2629 if (section->scan_starts [i]) {
2630 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2631 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2637 check_scan_starts (void)
2639 if (!do_scan_starts_check)
2641 mono_sgen_check_section_scan_starts (nursery_section);
2642 major_collector.check_scan_starts ();
2645 static int last_num_pinned = 0;
2648 build_nursery_fragments (void **start, int num_entries)
2650 char *frag_start, *frag_end;
2654 while (nursery_fragments) {
2655 Fragment *next = nursery_fragments->next;
2656 nursery_fragments->next = fragment_freelist;
2657 fragment_freelist = nursery_fragments;
2658 nursery_fragments = next;
2660 frag_start = nursery_start;
2662 /* clear scan starts */
2663 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2664 for (i = 0; i < num_entries; ++i) {
2665 frag_end = start [i];
2666 /* remove the pin bit from pinned objects */
2667 unpin_object (frag_end);
2668 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2669 frag_size = frag_end - frag_start;
2671 add_nursery_frag (frag_size, frag_start, frag_end);
2672 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)start [i]));
2673 frag_start = (char*)start [i] + frag_size;
2675 nursery_last_pinned_end = frag_start;
2676 frag_end = nursery_real_end;
2677 frag_size = frag_end - frag_start;
2679 add_nursery_frag (frag_size, frag_start, frag_end);
2680 if (!nursery_fragments) {
2681 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
2682 for (i = 0; i < num_entries; ++i) {
2683 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])));
2688 nursery_next = nursery_frag_real_end = NULL;
2690 /* Clear TLABs for all threads */
2695 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2699 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2700 for (root = roots_hash [root_type][i]; root; root = root->next) {
2701 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2702 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2708 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2710 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2714 mono_sgen_dump_section (GCMemSection *section, const char *type)
2716 char *start = section->data;
2717 char *end = section->data + section->size;
2718 char *occ_start = NULL;
2720 char *old_start = NULL; /* just for debugging */
2722 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2724 while (start < end) {
2728 if (!*(void**)start) {
2730 mono_sgen_dump_occupied (occ_start, start, section->data);
2733 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2736 g_assert (start < section->next_data);
2741 vt = (GCVTable*)LOAD_VTABLE (start);
2744 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2747 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2748 start - section->data,
2749 vt->klass->name_space, vt->klass->name,
2757 mono_sgen_dump_occupied (occ_start, start, section->data);
2759 fprintf (heap_dump_file, "</section>\n");
2763 dump_object (MonoObject *obj, gboolean dump_location)
2765 static char class_name [1024];
2767 MonoClass *class = mono_object_class (obj);
2771 * Python's XML parser is too stupid to parse angle brackets
2772 * in strings, so we just ignore them;
2775 while (class->name [i] && j < sizeof (class_name) - 1) {
2776 if (!strchr ("<>\"", class->name [i]))
2777 class_name [j++] = class->name [i];
2780 g_assert (j < sizeof (class_name));
2783 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2784 class->name_space, class_name,
2785 safe_object_get_size (obj));
2786 if (dump_location) {
2787 const char *location;
2788 if (ptr_in_nursery (obj))
2789 location = "nursery";
2790 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2794 fprintf (heap_dump_file, " location=\"%s\"", location);
2796 fprintf (heap_dump_file, "/>\n");
2800 dump_heap (const char *type, int num, const char *reason)
2805 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2807 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2808 fprintf (heap_dump_file, ">\n");
2809 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2810 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2811 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2812 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2813 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2815 fprintf (heap_dump_file, "<pinned-objects>\n");
2816 for (list = pinned_objects; list; list = list->next)
2817 dump_object (list->obj, TRUE);
2818 fprintf (heap_dump_file, "</pinned-objects>\n");
2820 mono_sgen_dump_section (nursery_section, "nursery");
2822 major_collector.dump_heap (heap_dump_file);
2824 fprintf (heap_dump_file, "<los>\n");
2825 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2826 dump_object ((MonoObject*)bigobj->data, FALSE);
2827 fprintf (heap_dump_file, "</los>\n");
2829 fprintf (heap_dump_file, "</collection>\n");
2833 mono_sgen_register_moved_object (void *obj, void *destination)
2835 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2837 /* FIXME: handle this for parallel collector */
2838 g_assert (!major_collector.is_parallel);
2840 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2841 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2842 moved_objects_idx = 0;
2844 moved_objects [moved_objects_idx++] = obj;
2845 moved_objects [moved_objects_idx++] = destination;
2851 static gboolean inited = FALSE;
2856 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2857 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2858 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2859 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2860 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2861 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2862 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2863 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2864 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2866 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2867 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2868 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2869 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2870 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2871 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2872 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2873 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2874 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2875 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2876 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2877 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2878 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2880 mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2882 #ifdef HEAVY_STATISTICS
2883 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2884 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2885 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2886 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2887 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2888 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2889 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2890 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2892 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2893 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2894 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2895 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2896 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2898 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2899 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2900 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2901 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2903 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2904 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2906 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2907 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2908 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2910 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
2911 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
2913 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2914 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2915 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2916 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2917 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2918 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2919 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2920 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2921 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2927 static gboolean need_calculate_minor_collection_allowance;
2929 static int last_collection_old_num_major_sections;
2930 static mword last_collection_los_memory_usage = 0;
2931 static mword last_collection_old_los_memory_usage;
2932 static mword last_collection_los_memory_alloced;
2935 reset_minor_collection_allowance (void)
2937 need_calculate_minor_collection_allowance = TRUE;
2941 try_calculate_minor_collection_allowance (gboolean overwrite)
2943 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2944 mword los_memory_saved;
2947 g_assert (need_calculate_minor_collection_allowance);
2949 if (!need_calculate_minor_collection_allowance)
2952 if (!*major_collector.have_swept) {
2954 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2958 num_major_sections = major_collector.get_num_major_sections ();
2960 num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
2961 los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
2963 save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2966 * We aim to allow the allocation of as many sections as is
2967 * necessary to reclaim save_target sections in the next
2968 * collection. We assume the collection pattern won't change.
2969 * In the last cycle, we had num_major_sections_saved for
2970 * minor_collection_sections_alloced. Assuming things won't
2971 * change, this must be the same ratio as save_target for
2972 * allowance_target, i.e.
2974 * num_major_sections_saved save_target
2975 * --------------------------------- == ----------------
2976 * minor_collection_sections_alloced allowance_target
2980 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));
2982 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2984 if (major_collector.have_computed_minor_collection_allowance)
2985 major_collector.have_computed_minor_collection_allowance ();
2987 need_calculate_minor_collection_allowance = FALSE;
2991 need_major_collection (mword space_needed)
2993 mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
2994 return (space_needed > available_free_space ()) ||
2995 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
2999 mono_sgen_need_major_collection (mword space_needed)
3001 return need_major_collection (space_needed);
3005 job_gray_queue (WorkerData *worker_data)
3007 return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
3014 } ScanFromRemsetsJobData;
3017 job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
3019 ScanFromRemsetsJobData *job_data = job_data_untyped;
3021 scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
3025 * Collect objects in the nursery. Returns whether to trigger a major
3029 collect_nursery (size_t requested_size)
3031 gboolean needs_major;
3032 size_t max_garbage_amount;
3033 char *orig_nursery_next;
3034 ScanFromRemsetsJobData sfrjd;
3035 TV_DECLARE (all_atv);
3036 TV_DECLARE (all_btv);
3040 mono_perfcounters->gc_collections0++;
3042 current_collection_generation = GENERATION_NURSERY;
3044 binary_protocol_collection (GENERATION_NURSERY);
3045 check_scan_starts ();
3049 orig_nursery_next = nursery_next;
3050 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3051 /* FIXME: optimize later to use the higher address where an object can be present */
3052 nursery_next = MAX (nursery_next, nursery_real_end);
3054 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)));
3055 max_garbage_amount = nursery_next - nursery_start;
3056 g_assert (nursery_section->size >= max_garbage_amount);
3058 /* world must be stopped already */
3059 TV_GETTIME (all_atv);
3062 /* Pinning no longer depends on clearing all nursery fragments */
3063 clear_current_nursery_fragment (orig_nursery_next);
3066 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3069 check_for_xdomain_refs ();
3071 nursery_section->next_data = nursery_next;
3073 major_collector.start_nursery_collection ();
3075 try_calculate_minor_collection_allowance (FALSE);
3077 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3078 workers_init_distribute_gray_queue ();
3081 mono_stats.minor_gc_count ++;
3083 global_remset_cache_clear ();
3085 /* pin from pinned handles */
3087 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
3088 pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3089 /* identify pinned objects */
3090 optimize_pin_queue (0);
3091 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3092 nursery_section->pin_queue_start = pin_queue;
3093 nursery_section->pin_queue_num_entries = next_pin_slot;
3095 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3096 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3097 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3099 if (consistency_check_at_minor_collection)
3100 check_consistency ();
3102 workers_start_all_workers ();
3105 * Walk all the roots and copy the young objects to the old
3106 * generation, starting from to_space.
3108 * The global remsets must be processed before the workers start
3109 * marking because they might add global remsets.
3111 scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3113 workers_start_marking ();
3115 sfrjd.heap_start = nursery_start;
3116 sfrjd.heap_end = nursery_next;
3117 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_remsets, &sfrjd);
3119 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3121 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3122 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3124 if (use_cardtable) {
3126 card_tables_collect_stats (TRUE);
3127 scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3129 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
3132 if (!major_collector.is_parallel)
3133 drain_gray_stack (&gray_queue, -1);
3135 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3136 report_registered_roots ();
3137 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3138 report_finalizer_roots ();
3140 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3141 /* registered roots, this includes static fields */
3142 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3143 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3145 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3147 scan_thread_data (nursery_start, nursery_next, TRUE, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3149 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3152 if (major_collector.is_parallel) {
3153 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3154 workers_distribute_gray_queue_sections ();
3160 if (major_collector.is_parallel)
3161 g_assert (gray_object_queue_is_empty (&gray_queue));
3163 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3165 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3166 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3169 * The (single-threaded) finalization code might have done
3170 * some copying/marking so we can only reset the GC thread's
3171 * worker data here instead of earlier when we joined the
3174 if (major_collector.reset_worker_data)
3175 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3177 if (objects_pinned) {
3178 evacuate_pin_staging_area ();
3179 optimize_pin_queue (0);
3180 nursery_section->pin_queue_start = pin_queue;
3181 nursery_section->pin_queue_num_entries = next_pin_slot;
3184 /* walk the pin_queue, build up the fragment list of free memory, unmark
3185 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3188 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3189 build_nursery_fragments (pin_queue, next_pin_slot);
3190 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3192 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3193 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3195 if (consistency_check_at_minor_collection)
3196 check_major_refs ();
3198 major_collector.finish_nursery_collection ();
3200 TV_GETTIME (all_btv);
3201 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3204 dump_heap ("minor", num_minor_gcs - 1, NULL);
3206 /* prepare the pin queue for the next collection */
3207 last_num_pinned = next_pin_slot;
3209 if (fin_ready_list || critical_fin_list) {
3210 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3211 mono_gc_finalize_notify ();
3215 g_assert (gray_object_queue_is_empty (&gray_queue));
3218 card_tables_collect_stats (FALSE);
3220 check_scan_starts ();
3222 binary_protocol_flush_buffers (FALSE);
3224 /*objects are late pinned because of lack of memory, so a major is a good call*/
3225 needs_major = need_major_collection (0) || objects_pinned;
3226 current_collection_generation = -1;
3237 } ScanFromRegisteredRootsJobData;
3240 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
3242 ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
3244 scan_from_registered_roots (major_collector.copy_or_mark_object,
3245 job_data->heap_start, job_data->heap_end,
3246 job_data->root_type,
3247 job_gray_queue (worker_data));
3254 } ScanThreadDataJobData;
3257 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
3259 ScanThreadDataJobData *job_data = job_data_untyped;
3261 scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
3262 job_gray_queue (worker_data));
3267 FinalizeEntry *list;
3268 } ScanFinalizerEntriesJobData;
3271 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
3273 ScanFinalizerEntriesJobData *job_data = job_data_untyped;
3275 scan_finalizer_entries (major_collector.copy_or_mark_object,
3277 job_gray_queue (worker_data));
3281 major_do_collection (const char *reason)
3283 LOSObject *bigobj, *prevbo;
3284 TV_DECLARE (all_atv);
3285 TV_DECLARE (all_btv);
3288 /* FIXME: only use these values for the precise scan
3289 * note that to_space pointers should be excluded anyway...
3291 char *heap_start = NULL;
3292 char *heap_end = (char*)-1;
3293 int old_next_pin_slot;
3294 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3295 ScanThreadDataJobData stdjd;
3296 ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
3298 mono_perfcounters->gc_collections1++;
3300 last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3303 * A domain could have been freed, resulting in
3304 * los_memory_usage being less than last_collection_los_memory_usage.
3306 last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3307 last_collection_old_los_memory_usage = los_memory_usage;
3310 //count_ref_nonref_objs ();
3311 //consistency_check ();
3313 binary_protocol_collection (GENERATION_OLD);
3314 check_scan_starts ();
3315 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3316 workers_init_distribute_gray_queue ();
3319 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3321 mono_stats.major_gc_count ++;
3323 /* world must be stopped already */
3324 TV_GETTIME (all_atv);
3327 /* Pinning depends on this */
3328 clear_nursery_fragments (nursery_next);
3331 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3333 nursery_section->next_data = nursery_real_end;
3334 /* we should also coalesce scanning from sections close to each other
3335 * and deal with pointers outside of the sections later.
3338 if (major_collector.start_major_collection)
3339 major_collector.start_major_collection ();
3341 *major_collector.have_swept = FALSE;
3342 reset_minor_collection_allowance ();
3345 check_for_xdomain_refs ();
3347 /* The remsets are not useful for a major collection */
3349 global_remset_cache_clear ();
3351 card_table_clear ();
3355 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3356 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3357 optimize_pin_queue (0);
3360 * pin_queue now contains all candidate pointers, sorted and
3361 * uniqued. We must do two passes now to figure out which
3362 * objects are pinned.
3364 * The first is to find within the pin_queue the area for each
3365 * section. This requires that the pin_queue be sorted. We
3366 * also process the LOS objects and pinned chunks here.
3368 * The second, destructive, pass is to reduce the section
3369 * areas to pointers to the actually pinned objects.
3371 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3372 /* first pass for the sections */
3373 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3374 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3375 /* identify possible pointers to the insize of large objects */
3376 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3377 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3379 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3380 pin_object (bigobj->data);
3381 /* FIXME: only enqueue if object has references */
3382 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3384 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3385 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));
3388 /* second pass for the sections */
3389 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3390 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3391 old_next_pin_slot = next_pin_slot;
3394 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3395 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3396 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3398 major_collector.init_to_space ();
3400 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3401 main_gc_thread = pthread_self ();
3404 workers_start_all_workers ();
3405 workers_start_marking ();
3407 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3408 report_registered_roots ();
3410 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3412 /* registered roots, this includes static fields */
3413 scrrjd_normal.heap_start = heap_start;
3414 scrrjd_normal.heap_end = heap_end;
3415 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3416 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
3418 scrrjd_wbarrier.heap_start = heap_start;
3419 scrrjd_wbarrier.heap_end = heap_end;
3420 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3421 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
3424 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3427 stdjd.heap_start = heap_start;
3428 stdjd.heap_end = heap_end;
3429 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
3432 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3435 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3437 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3438 report_finalizer_roots ();
3440 /* scan the list of objects ready for finalization */
3441 sfejd_fin_ready.list = fin_ready_list;
3442 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_fin_ready);
3444 sfejd_critical_fin.list = critical_fin_list;
3445 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_critical_fin);
3448 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3449 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3452 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3454 if (major_collector.is_parallel) {
3455 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3456 workers_distribute_gray_queue_sections ();
3462 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3463 main_gc_thread = NULL;
3466 if (major_collector.is_parallel)
3467 g_assert (gray_object_queue_is_empty (&gray_queue));
3469 /* all the objects in the heap */
3470 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3472 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3475 * The (single-threaded) finalization code might have done
3476 * some copying/marking so we can only reset the GC thread's
3477 * worker data here instead of earlier when we joined the
3480 if (major_collector.reset_worker_data)
3481 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3483 if (objects_pinned) {
3484 /*This is slow, but we just OOM'd*/
3485 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3486 evacuate_pin_staging_area ();
3487 optimize_pin_queue (0);
3488 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3492 reset_heap_boundaries ();
3493 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
3495 /* sweep the big objects list */
3497 for (bigobj = los_object_list; bigobj;) {
3498 if (object_is_pinned (bigobj->data)) {
3499 unpin_object (bigobj->data);
3500 mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3503 /* not referenced anywhere, so we can free it */
3505 prevbo->next = bigobj->next;
3507 los_object_list = bigobj->next;
3509 bigobj = bigobj->next;
3510 mono_sgen_los_free_object (to_free);
3514 bigobj = bigobj->next;
3518 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3520 mono_sgen_los_sweep ();
3523 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3525 major_collector.sweep ();
3528 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3530 /* walk the pin_queue, build up the fragment list of free memory, unmark
3531 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3534 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
3537 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3539 TV_GETTIME (all_btv);
3540 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3543 dump_heap ("major", num_major_gcs - 1, reason);
3545 /* prepare the pin queue for the next collection */
3547 if (fin_ready_list || critical_fin_list) {
3548 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3549 mono_gc_finalize_notify ();
3553 g_assert (gray_object_queue_is_empty (&gray_queue));
3555 try_calculate_minor_collection_allowance (TRUE);
3557 minor_collection_sections_alloced = 0;
3558 last_collection_los_memory_usage = los_memory_usage;
3560 major_collector.finish_major_collection ();
3562 check_scan_starts ();
3564 binary_protocol_flush_buffers (FALSE);
3566 //consistency_check ();
3570 major_collection (const char *reason)
3572 if (g_getenv ("MONO_GC_NO_MAJOR")) {
3573 collect_nursery (0);
3577 current_collection_generation = GENERATION_OLD;
3578 major_do_collection (reason);
3579 current_collection_generation = -1;
3583 sgen_collect_major_no_lock (const char *reason)
3585 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3587 major_collection (reason);
3589 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3593 * When deciding if it's better to collect or to expand, keep track
3594 * of how much garbage was reclaimed with the last collection: if it's too
3596 * This is called when we could not allocate a small object.
3598 static void __attribute__((noinline))
3599 minor_collect_or_expand_inner (size_t size)
3601 int do_minor_collection = 1;
3603 g_assert (nursery_section);
3604 if (do_minor_collection) {
3605 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3607 if (collect_nursery (size)) {
3608 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3609 major_collection ("minor overflow");
3610 /* keep events symmetric */
3611 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3613 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3615 /* this also sets the proper pointers for the next allocation */
3616 if (!search_fragment_for_size (size)) {
3618 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3619 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3620 for (i = 0; i < last_num_pinned; ++i) {
3621 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])));
3625 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3627 //report_internal_mem_usage ();
3631 * ######################################################################
3632 * ######## Memory allocation from the OS
3633 * ######################################################################
3634 * This section of code deals with getting memory from the OS and
3635 * allocating memory for GC-internal data structures.
3636 * Internal memory can be handled with a freelist for small objects.
3642 G_GNUC_UNUSED static void
3643 report_internal_mem_usage (void)
3645 printf ("Internal memory usage:\n");
3646 mono_sgen_report_internal_mem_usage ();
3647 printf ("Pinned memory usage:\n");
3648 major_collector.report_pinned_memory_usage ();
3652 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3653 * This must not require any lock.
3656 mono_sgen_alloc_os_memory (size_t size, int activate)
3659 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3661 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3662 size += pagesize - 1;
3663 size &= ~(pagesize - 1);
3664 ptr = mono_valloc (0, size, prot_flags);
3666 total_alloc += size;
3671 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3674 mono_sgen_free_os_memory (void *addr, size_t size)
3676 mono_vfree (addr, size);
3678 size += pagesize - 1;
3679 size &= ~(pagesize - 1);
3681 total_alloc -= size;
3685 * ######################################################################
3686 * ######## Object allocation
3687 * ######################################################################
3688 * This section of code deals with allocating memory for objects.
3689 * There are several ways:
3690 * *) allocate large objects
3691 * *) allocate normal objects
3692 * *) fast lock-free allocation
3693 * *) allocation of pinned objects
3697 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3699 /* remove from the list */
3701 prev->next = frag->next;
3703 nursery_fragments = frag->next;
3704 nursery_next = frag->fragment_start;
3705 nursery_frag_real_end = frag->fragment_end;
3707 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));
3708 frag->next = fragment_freelist;
3709 fragment_freelist = frag;
3712 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3713 * an object of size @size
3714 * Return FALSE if not found (which means we need a collection)
3717 search_fragment_for_size (size_t size)
3719 Fragment *frag, *prev;
3720 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3722 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3723 /* Clear the remaining space, pinning depends on this */
3724 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3728 for (frag = nursery_fragments; frag; frag = frag->next) {
3729 if (size <= (frag->fragment_end - frag->fragment_start)) {
3730 setup_fragment (frag, prev, size);
3739 * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3740 * This improves nursery usage.
3743 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3745 Fragment *frag, *prev, *min_prev;
3746 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));
3748 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3749 /* Clear the remaining space, pinning depends on this */
3750 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3753 min_prev = GINT_TO_POINTER (-1);
3756 for (frag = nursery_fragments; frag; frag = frag->next) {
3757 int frag_size = frag->fragment_end - frag->fragment_start;
3758 if (desired_size <= frag_size) {
3759 setup_fragment (frag, prev, desired_size);
3760 return desired_size;
3762 if (minimum_size <= frag_size)
3768 if (min_prev != GINT_TO_POINTER (-1)) {
3771 frag = min_prev->next;
3773 frag = nursery_fragments;
3775 frag_size = frag->fragment_end - frag->fragment_start;
3776 HEAVY_STAT (++stat_wasted_fragments_used);
3777 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3779 setup_fragment (frag, min_prev, minimum_size);
3787 alloc_degraded (MonoVTable *vtable, size_t size)
3789 if (need_major_collection (0)) {
3790 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3792 major_collection ("degraded overflow");
3794 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3797 return major_collector.alloc_degraded (vtable, size);
3801 * Provide a variant that takes just the vtable for small fixed-size objects.
3802 * The aligned size is already computed and stored in vt->gc_descr.
3803 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3804 * processing. We can keep track of where objects start, for example,
3805 * so when we scan the thread stacks for pinned objects, we can start
3806 * a search for the pinned object in SCAN_START_SIZE chunks.
3809 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3811 /* FIXME: handle OOM */
3816 HEAVY_STAT (++stat_objects_alloced);
3817 if (size <= MAX_SMALL_OBJ_SIZE)
3818 HEAVY_STAT (stat_bytes_alloced += size);
3820 HEAVY_STAT (stat_bytes_alloced_los += size);
3822 size = ALIGN_UP (size);
3824 g_assert (vtable->gc_descr);
3826 if (G_UNLIKELY (collect_before_allocs)) {
3827 static int alloc_count;
3829 InterlockedIncrement (&alloc_count);
3830 if (((alloc_count % collect_before_allocs) == 0) && nursery_section) {
3831 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3833 collect_nursery (0);
3835 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3836 if (!degraded_mode && !search_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
3838 g_assert_not_reached ();
3844 * We must already have the lock here instead of after the
3845 * fast path because we might be interrupted in the fast path
3846 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3847 * and we'll end up allocating an object in a fragment which
3848 * no longer belongs to us.
3850 * The managed allocator does not do this, but it's treated
3851 * specially by the world-stopping code.
3854 if (size > MAX_SMALL_OBJ_SIZE) {
3855 p = mono_sgen_los_alloc_large_inner (vtable, size);
3857 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3859 p = (void**)TLAB_NEXT;
3860 /* FIXME: handle overflow */
3861 new_next = (char*)p + size;
3862 TLAB_NEXT = new_next;
3864 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3868 * FIXME: We might need a memory barrier here so the change to tlab_next is
3869 * visible before the vtable store.
3872 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3873 binary_protocol_alloc (p , vtable, size);
3874 g_assert (*p == NULL);
3877 g_assert (TLAB_NEXT == new_next);
3884 /* there are two cases: the object is too big or we run out of space in the TLAB */
3885 /* we also reach here when the thread does its first allocation after a minor
3886 * collection, since the tlab_ variables are initialized to NULL.
3887 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3888 * objects that need finalizers can have the high bit set in their size
3889 * so the above check fails and we can readily add the object to the queue.
3890 * This avoids taking again the GC lock when registering, but this is moot when
3891 * doing thread-local allocation, so it may not be a good idea.
3893 g_assert (TLAB_NEXT == new_next);
3894 if (TLAB_NEXT >= TLAB_REAL_END) {
3896 * Run out of space in the TLAB. When this happens, some amount of space
3897 * remains in the TLAB, but not enough to satisfy the current allocation
3898 * request. Currently, we retire the TLAB in all cases, later we could
3899 * keep it if the remaining space is above a treshold, and satisfy the
3900 * allocation directly from the nursery.
3903 /* when running in degraded mode, we continue allocing that way
3904 * for a while, to decrease the number of useless nursery collections.
3906 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3907 p = alloc_degraded (vtable, size);
3908 binary_protocol_alloc_degraded (p, vtable, size);
3912 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3913 if (size > tlab_size) {
3914 /* Allocate directly from the nursery */
3915 if (nursery_next + size >= nursery_frag_real_end) {
3916 if (!search_fragment_for_size (size)) {
3917 minor_collect_or_expand_inner (size);
3918 if (degraded_mode) {
3919 p = alloc_degraded (vtable, size);
3920 binary_protocol_alloc_degraded (p, vtable, size);
3926 p = (void*)nursery_next;
3927 nursery_next += size;
3928 if (nursery_next > nursery_frag_real_end) {
3933 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3934 memset (p, 0, size);
3937 int alloc_size = tlab_size;
3938 int available_in_nursery = nursery_frag_real_end - nursery_next;
3940 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3942 if (alloc_size >= available_in_nursery) {
3943 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3944 alloc_size = available_in_nursery;
3946 alloc_size = search_fragment_for_size_range (tlab_size, size);
3948 alloc_size = tlab_size;
3949 minor_collect_or_expand_inner (tlab_size);
3950 if (degraded_mode) {
3951 p = alloc_degraded (vtable, size);
3952 binary_protocol_alloc_degraded (p, vtable, size);
3959 /* Allocate a new TLAB from the current nursery fragment */
3960 TLAB_START = nursery_next;
3961 nursery_next += alloc_size;
3962 TLAB_NEXT = TLAB_START;
3963 TLAB_REAL_END = TLAB_START + alloc_size;
3964 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3966 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3967 memset (TLAB_START, 0, alloc_size);
3970 /* Allocate from the TLAB */
3971 p = (void*)TLAB_NEXT;
3973 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3975 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3978 /* Reached tlab_temp_end */
3980 /* record the scan start so we can find pinned objects more easily */
3981 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3982 /* we just bump tlab_temp_end as well */
3983 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3984 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3989 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3990 binary_protocol_alloc (p, vtable, size);
3998 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4004 size = ALIGN_UP (size);
4006 g_assert (vtable->gc_descr);
4007 if (size <= MAX_SMALL_OBJ_SIZE) {
4008 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4010 p = (void**)TLAB_NEXT;
4011 /* FIXME: handle overflow */
4012 new_next = (char*)p + size;
4013 TLAB_NEXT = new_next;
4015 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4019 * FIXME: We might need a memory barrier here so the change to tlab_next is
4020 * visible before the vtable store.
4023 HEAVY_STAT (++stat_objects_alloced);
4024 HEAVY_STAT (stat_bytes_alloced += size);
4026 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4027 binary_protocol_alloc (p, vtable, size);
4028 g_assert (*p == NULL);
4031 g_assert (TLAB_NEXT == new_next);
4040 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4043 #ifndef DISABLE_CRITICAL_REGION
4045 ENTER_CRITICAL_REGION;
4046 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4048 EXIT_CRITICAL_REGION;
4051 EXIT_CRITICAL_REGION;
4054 res = mono_gc_alloc_obj_nolock (vtable, size);
4056 if (G_UNLIKELY (!res))
4057 return mono_gc_out_of_memory (size);
4062 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4065 #ifndef DISABLE_CRITICAL_REGION
4067 ENTER_CRITICAL_REGION;
4068 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4070 arr->max_length = max_length;
4071 EXIT_CRITICAL_REGION;
4074 EXIT_CRITICAL_REGION;
4079 arr = mono_gc_alloc_obj_nolock (vtable, size);
4080 if (G_UNLIKELY (!arr)) {
4082 return mono_gc_out_of_memory (size);
4085 arr->max_length = max_length;
4093 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4096 MonoArrayBounds *bounds;
4100 arr = mono_gc_alloc_obj_nolock (vtable, size);
4101 if (G_UNLIKELY (!arr)) {
4103 return mono_gc_out_of_memory (size);
4106 arr->max_length = max_length;
4108 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4109 arr->bounds = bounds;
4117 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4120 #ifndef DISABLE_CRITICAL_REGION
4122 ENTER_CRITICAL_REGION;
4123 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4126 EXIT_CRITICAL_REGION;
4129 EXIT_CRITICAL_REGION;
4134 str = mono_gc_alloc_obj_nolock (vtable, size);
4135 if (G_UNLIKELY (!str)) {
4137 return mono_gc_out_of_memory (size);
4148 * To be used for interned strings and possibly MonoThread, reflection handles.
4149 * We may want to explicitly free these objects.
4152 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4155 size = ALIGN_UP (size);
4158 if (size > MAX_SMALL_OBJ_SIZE) {
4159 /* large objects are always pinned anyway */
4160 p = mono_sgen_los_alloc_large_inner (vtable, size);
4162 DEBUG (9, g_assert (vtable->klass->inited));
4163 p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
4166 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4167 binary_protocol_alloc_pinned (p, vtable, size);
4175 mono_gc_alloc_mature (MonoVTable *vtable)
4178 size_t size = ALIGN_UP (vtable->klass->instance_size);
4180 res = alloc_degraded (vtable, size);
4183 if (G_UNLIKELY (vtable->klass->has_finalize))
4184 mono_object_register_finalizer ((MonoObject*)res);
4190 * ######################################################################
4191 * ######## Finalization support
4192 * ######################################################################
4196 * this is valid for the nursery: if the object has been forwarded it means it's
4197 * still refrenced from a root. If it is pinned it's still alive as well.
4198 * Return TRUE if @obj is ready to be finalized.
4200 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4203 is_critical_finalizer (FinalizeEntry *entry)
4208 if (!mono_defaults.critical_finalizer_object)
4211 obj = entry->object;
4212 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4214 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4218 queue_finalization_entry (FinalizeEntry *entry) {
4219 if (is_critical_finalizer (entry)) {
4220 entry->next = critical_fin_list;
4221 critical_fin_list = entry;
4223 entry->next = fin_ready_list;
4224 fin_ready_list = entry;
4228 /* LOCKING: requires that the GC lock is held */
4230 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4232 FinalizeEntry **finalizable_hash = hash_table->table;
4233 mword finalizable_hash_size = hash_table->size;
4236 FinalizeEntry **new_hash;
4237 FinalizeEntry *entry, *next;
4238 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4240 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4241 for (i = 0; i < finalizable_hash_size; ++i) {
4242 for (entry = finalizable_hash [i]; entry; entry = next) {
4243 hash = mono_object_hash (entry->object) % new_size;
4245 entry->next = new_hash [hash];
4246 new_hash [hash] = entry;
4249 mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4250 hash_table->table = new_hash;
4251 hash_table->size = new_size;
4254 /* LOCKING: requires that the GC lock is held */
4256 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4258 if (hash_table->num_registered >= hash_table->size * 2)
4259 rehash_fin_table (hash_table);
4262 /* LOCKING: requires that the GC lock is held */
4264 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
4266 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4267 FinalizeEntry *entry, *prev;
4269 FinalizeEntry **finalizable_hash = hash_table->table;
4270 mword finalizable_hash_size = hash_table->size;
4274 for (i = 0; i < finalizable_hash_size; ++i) {
4276 for (entry = finalizable_hash [i]; entry;) {
4277 if ((char*)entry->object >= start && (char*)entry->object < end && !major_collector.is_object_live (entry->object)) {
4278 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4279 char *copy = entry->object;
4280 copy_func ((void**)©, queue);
4283 FinalizeEntry *next;
4284 /* remove and put in fin_ready_list */
4286 prev->next = entry->next;
4288 finalizable_hash [i] = entry->next;
4290 num_ready_finalizers++;
4291 hash_table->num_registered--;
4292 queue_finalization_entry (entry);
4293 bridge_register_finalized_object ((MonoObject*)copy);
4294 /* Make it survive */
4295 from = entry->object;
4296 entry->object = copy;
4297 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));
4301 char *from = entry->object;
4302 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4303 FinalizeEntry *next = entry->next;
4304 unsigned int major_hash;
4305 /* remove from the list */
4307 prev->next = entry->next;
4309 finalizable_hash [i] = entry->next;
4310 hash_table->num_registered--;
4312 entry->object = copy;
4314 /* insert it into the major hash */
4315 rehash_fin_table_if_necessary (&major_finalizable_hash);
4316 major_hash = mono_object_hash ((MonoObject*) copy) %
4317 major_finalizable_hash.size;
4318 entry->next = major_finalizable_hash.table [major_hash];
4319 major_finalizable_hash.table [major_hash] = entry;
4320 major_finalizable_hash.num_registered++;
4322 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4327 /* update pointer */
4328 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4329 entry->object = copy;
4334 entry = entry->next;
4340 object_is_reachable (char *object, char *start, char *end)
4342 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4343 if (object < start || object >= end)
4345 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4349 mono_sgen_object_is_live (void *obj)
4351 if (ptr_in_nursery (obj))
4352 return object_is_pinned (obj);
4353 if (current_collection_generation == GENERATION_NURSERY)
4355 return major_collector.is_object_live (obj);
4358 /* LOCKING: requires that the GC lock is held */
4360 null_ephemerons_for_domain (MonoDomain *domain)
4362 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4365 MonoObject *object = (MonoObject*)current->array;
4367 if (object && !object->vtable) {
4368 EphemeronLinkNode *tmp = current;
4371 prev->next = current->next;
4373 ephemeron_list = current->next;
4375 current = current->next;
4376 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4379 current = current->next;
4384 /* LOCKING: requires that the GC lock is held */
4386 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4388 int was_in_nursery, was_promoted;
4389 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4391 Ephemeron *cur, *array_end;
4395 char *object = current->array;
4397 if (!object_is_reachable (object, start, end)) {
4398 EphemeronLinkNode *tmp = current;
4400 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4403 prev->next = current->next;
4405 ephemeron_list = current->next;
4407 current = current->next;
4408 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4413 was_in_nursery = ptr_in_nursery (object);
4414 copy_func ((void**)&object, queue);
4415 current->array = object;
4417 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4418 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4420 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4422 array = (MonoArray*)object;
4423 cur = mono_array_addr (array, Ephemeron, 0);
4424 array_end = cur + mono_array_length_fast (array);
4425 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4427 for (; cur < array_end; ++cur) {
4428 char *key = (char*)cur->key;
4430 if (!key || key == tombstone)
4433 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4434 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4435 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4437 if (!object_is_reachable (key, start, end)) {
4438 cur->key = tombstone;
4444 if (ptr_in_nursery (key)) {/*key was not promoted*/
4445 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4446 mono_sgen_add_to_global_remset (queue->allocator, &cur->key);
4448 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4449 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4450 mono_sgen_add_to_global_remset (queue->allocator, &cur->value);
4455 current = current->next;
4459 /* LOCKING: requires that the GC lock is held */
4461 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4463 int nothing_marked = 1;
4464 EphemeronLinkNode *current = ephemeron_list;
4466 Ephemeron *cur, *array_end;
4469 for (current = ephemeron_list; current; current = current->next) {
4470 char *object = current->array;
4471 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4473 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4474 if (object < start || object >= end)
4477 /*It has to be alive*/
4478 if (!object_is_reachable (object, start, end)) {
4479 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4483 copy_func ((void**)&object, queue);
4485 array = (MonoArray*)object;
4486 cur = mono_array_addr (array, Ephemeron, 0);
4487 array_end = cur + mono_array_length_fast (array);
4488 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4490 for (; cur < array_end; ++cur) {
4491 char *key = cur->key;
4493 if (!key || key == tombstone)
4496 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4497 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4498 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4500 if (object_is_reachable (key, start, end)) {
4501 char *value = cur->value;
4503 copy_func ((void**)&cur->key, queue);
4505 if (!object_is_reachable (value, start, end))
4507 copy_func ((void**)&cur->value, queue);
4513 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4514 return nothing_marked;
4517 /* LOCKING: requires that the GC lock is held */
4519 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
4521 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4522 DisappearingLink **disappearing_link_hash = hash->table;
4523 int disappearing_link_hash_size = hash->size;
4524 DisappearingLink *entry, *prev;
4526 if (!hash->num_links)
4528 for (i = 0; i < disappearing_link_hash_size; ++i) {
4530 for (entry = disappearing_link_hash [i]; entry;) {
4532 gboolean track = DISLINK_TRACK (entry);
4535 * Tracked references are processed after
4536 * finalization handling whereas standard weak
4537 * references are processed before. If an
4538 * object is still not marked after finalization
4539 * handling it means that it either doesn't have
4540 * a finalizer or the finalizer has already run,
4541 * so we must null a tracking reference.
4543 if (track == before_finalization) {
4545 entry = entry->next;
4549 object = DISLINK_OBJECT (entry);
4551 if (object >= start && object < end && !major_collector.is_object_live (object)) {
4552 if (object_is_fin_ready (object)) {
4553 void **p = entry->link;
4554 DisappearingLink *old;
4556 /* remove from list */
4558 prev->next = entry->next;
4560 disappearing_link_hash [i] = entry->next;
4561 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4563 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4568 char *copy = object;
4569 copy_func ((void**)©, queue);
4571 /* Update pointer if it's moved. If the object
4572 * has been moved out of the nursery, we need to
4573 * remove the link from the minor hash table to
4576 * FIXME: what if an object is moved earlier?
4579 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4580 void **link = entry->link;
4581 DisappearingLink *old;
4582 /* remove from list */
4584 prev->next = entry->next;
4586 disappearing_link_hash [i] = entry->next;
4588 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4592 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4593 track, GENERATION_OLD);
4595 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4599 *entry->link = HIDE_POINTER (copy, track);
4600 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4605 entry = entry->next;
4610 /* LOCKING: requires that the GC lock is held */
4612 null_links_for_domain (MonoDomain *domain, int generation)
4614 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4615 DisappearingLink **disappearing_link_hash = hash->table;
4616 int disappearing_link_hash_size = hash->size;
4617 DisappearingLink *entry, *prev;
4619 for (i = 0; i < disappearing_link_hash_size; ++i) {
4621 for (entry = disappearing_link_hash [i]; entry; ) {
4622 char *object = DISLINK_OBJECT (entry);
4623 if (object && !((MonoObject*)object)->vtable) {
4624 DisappearingLink *next = entry->next;
4629 disappearing_link_hash [i] = next;
4631 if (*(entry->link)) {
4632 *(entry->link) = NULL;
4633 g_warning ("Disappearing link %p not freed", entry->link);
4635 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4642 entry = entry->next;
4647 /* LOCKING: requires that the GC lock is held */
4649 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4650 FinalizeEntryHashTable *hash_table)
4652 FinalizeEntry **finalizable_hash = hash_table->table;
4653 mword finalizable_hash_size = hash_table->size;
4654 FinalizeEntry *entry, *prev;
4657 if (no_finalize || !out_size || !out_array)
4660 for (i = 0; i < finalizable_hash_size; ++i) {
4662 for (entry = finalizable_hash [i]; entry;) {
4663 if (mono_object_domain (entry->object) == domain) {
4664 FinalizeEntry *next;
4665 /* remove and put in out_array */
4667 prev->next = entry->next;
4669 finalizable_hash [i] = entry->next;
4671 hash_table->num_registered--;
4672 out_array [count ++] = entry->object;
4673 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));
4675 if (count == out_size)
4680 entry = entry->next;
4687 * mono_gc_finalizers_for_domain:
4688 * @domain: the unloading appdomain
4689 * @out_array: output array
4690 * @out_size: size of output array
4692 * Store inside @out_array up to @out_size objects that belong to the unloading
4693 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4694 * until it returns 0.
4695 * The items are removed from the finalizer data structure, so the caller is supposed
4697 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4700 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4705 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4706 if (result < out_size) {
4707 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4708 &major_finalizable_hash);
4716 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4718 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4719 FinalizeEntry **finalizable_hash;
4720 mword finalizable_hash_size;
4721 FinalizeEntry *entry, *prev;
4725 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4726 hash = mono_object_hash (obj);
4728 rehash_fin_table_if_necessary (hash_table);
4729 finalizable_hash = hash_table->table;
4730 finalizable_hash_size = hash_table->size;
4731 hash %= finalizable_hash_size;
4733 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4734 if (entry->object == obj) {
4736 /* remove from the list */
4738 prev->next = entry->next;
4740 finalizable_hash [hash] = entry->next;
4741 hash_table->num_registered--;
4742 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));
4743 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4751 /* request to deregister, but already out of the list */
4755 entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4756 entry->object = obj;
4757 entry->next = finalizable_hash [hash];
4758 finalizable_hash [hash] = entry;
4759 hash_table->num_registered++;
4760 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)));
4765 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4767 if (ptr_in_nursery (obj))
4768 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4770 register_for_finalization (obj, user_data, GENERATION_OLD);
4774 rehash_dislink (DisappearingLinkHashTable *hash_table)
4776 DisappearingLink **disappearing_link_hash = hash_table->table;
4777 int disappearing_link_hash_size = hash_table->size;
4780 DisappearingLink **new_hash;
4781 DisappearingLink *entry, *next;
4782 int new_size = g_spaced_primes_closest (hash_table->num_links);
4784 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4785 for (i = 0; i < disappearing_link_hash_size; ++i) {
4786 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4787 hash = mono_aligned_addr_hash (entry->link) % new_size;
4789 entry->next = new_hash [hash];
4790 new_hash [hash] = entry;
4793 mono_sgen_free_internal_dynamic (disappearing_link_hash,
4794 disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4795 hash_table->table = new_hash;
4796 hash_table->size = new_size;
4799 /* LOCKING: assumes the GC lock is held */
4801 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4803 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4804 DisappearingLink *entry, *prev;
4806 DisappearingLink **disappearing_link_hash = hash_table->table;
4807 int disappearing_link_hash_size = hash_table->size;
4809 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4810 rehash_dislink (hash_table);
4811 disappearing_link_hash = hash_table->table;
4812 disappearing_link_hash_size = hash_table->size;
4814 /* FIXME: add check that link is not in the heap */
4815 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4816 entry = disappearing_link_hash [hash];
4818 for (; entry; entry = entry->next) {
4819 /* link already added */
4820 if (link == entry->link) {
4821 /* NULL obj means remove */
4824 prev->next = entry->next;
4826 disappearing_link_hash [hash] = entry->next;
4827 hash_table->num_links--;
4828 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4829 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4832 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4840 entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4841 *link = HIDE_POINTER (obj, track);
4843 entry->next = disappearing_link_hash [hash];
4844 disappearing_link_hash [hash] = entry;
4845 hash_table->num_links++;
4846 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)));
4849 /* LOCKING: assumes the GC lock is held */
4851 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4853 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4854 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4856 if (ptr_in_nursery (obj))
4857 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4859 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4864 mono_gc_invoke_finalizers (void)
4866 FinalizeEntry *entry = NULL;
4867 gboolean entry_is_critical = FALSE;
4870 /* FIXME: batch to reduce lock contention */
4871 while (fin_ready_list || critical_fin_list) {
4875 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4877 /* We have finalized entry in the last
4878 interation, now we need to remove it from
4881 *list = entry->next;
4883 FinalizeEntry *e = *list;
4884 while (e->next != entry)
4886 e->next = entry->next;
4888 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4892 /* Now look for the first non-null entry. */
4893 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4896 entry_is_critical = FALSE;
4898 entry_is_critical = TRUE;
4899 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4904 g_assert (entry->object);
4905 num_ready_finalizers--;
4906 obj = entry->object;
4907 entry->object = NULL;
4908 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4916 g_assert (entry->object == NULL);
4918 /* the object is on the stack so it is pinned */
4919 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4920 mono_gc_run_finalize (obj, NULL);
4927 mono_gc_pending_finalizers (void)
4929 return fin_ready_list || critical_fin_list;
4932 /* Negative value to remove */
4934 mono_gc_add_memory_pressure (gint64 value)
4936 /* FIXME: Use interlocked functions */
4938 memory_pressure += value;
4943 mono_sgen_register_major_sections_alloced (int num_sections)
4945 minor_collection_sections_alloced += num_sections;
4949 mono_sgen_get_minor_collection_allowance (void)
4951 return minor_collection_allowance;
4955 * ######################################################################
4956 * ######## registered roots support
4957 * ######################################################################
4961 rehash_roots (gboolean pinned)
4965 RootRecord **new_hash;
4966 RootRecord *entry, *next;
4969 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4970 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4971 for (i = 0; i < roots_hash_size [pinned]; ++i) {
4972 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4973 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4975 entry->next = new_hash [hash];
4976 new_hash [hash] = entry;
4979 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4980 roots_hash [pinned] = new_hash;
4981 roots_hash_size [pinned] = new_size;
4985 find_root (int root_type, char *start, guint32 addr_hash)
4987 RootRecord *new_root;
4989 guint32 hash = addr_hash % roots_hash_size [root_type];
4990 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4991 /* we allow changing the size and the descriptor (for thread statics etc) */
4992 if (new_root->start_root == start) {
5001 * We do not coalesce roots.
5004 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5006 RootRecord *new_root;
5007 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5010 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5011 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5014 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5015 new_root = find_root (i, start, addr_hash);
5016 /* we allow changing the size and the descriptor (for thread statics etc) */
5018 size_t old_size = new_root->end_root - new_root->start_root;
5019 new_root->end_root = new_root->start_root + size;
5020 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5021 ((new_root->root_desc == 0) && (descr == NULL)));
5022 new_root->root_desc = (mword)descr;
5024 roots_size -= old_size;
5029 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
5031 new_root->start_root = start;
5032 new_root->end_root = new_root->start_root + size;
5033 new_root->root_desc = (mword)descr;
5035 hash = addr_hash % roots_hash_size [root_type];
5036 num_roots_entries [root_type]++;
5037 new_root->next = roots_hash [root_type] [hash];
5038 roots_hash [root_type][hash] = new_root;
5039 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));
5049 mono_gc_register_root (char *start, size_t size, void *descr)
5051 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5055 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5057 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5061 mono_gc_deregister_root (char* addr)
5063 RootRecord *tmp, *prev;
5064 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5068 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5069 hash = addr_hash % roots_hash_size [root_type];
5070 tmp = roots_hash [root_type][hash];
5073 if (tmp->start_root == (char*)addr) {
5075 prev->next = tmp->next;
5077 roots_hash [root_type][hash] = tmp->next;
5078 roots_size -= (tmp->end_root - tmp->start_root);
5079 num_roots_entries [root_type]--;
5080 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5081 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
5092 * ######################################################################
5093 * ######## Thread handling (stop/start code)
5094 * ######################################################################
5097 /* FIXME: handle large/small config */
5098 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5100 SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5102 #if USE_SIGNAL_BASED_START_STOP_WORLD
5104 static MonoSemType suspend_ack_semaphore;
5105 static MonoSemType *suspend_ack_semaphore_ptr;
5106 static unsigned int global_stop_count = 0;
5108 static sigset_t suspend_signal_mask;
5111 static MonoContext cur_thread_ctx = {0};
5113 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5118 mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
5120 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5121 SgenThreadInfo *info;
5123 info = thread_table [hash];
5124 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5131 update_current_thread_stack (void *start)
5133 int stack_guard = 0;
5137 void *ptr = cur_thread_regs;
5139 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5141 info->stack_start = align_pointer (&stack_guard);
5142 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5144 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
5145 info->monoctx = &cur_thread_ctx;
5147 ARCH_STORE_REGS (ptr);
5148 info->stopped_regs = ptr;
5150 if (gc_callbacks.thread_suspend_func)
5151 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5155 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5156 * have cross-domain checks in the write barrier.
5158 //#define XDOMAIN_CHECKS_IN_WBARRIER
5160 #ifndef SGEN_BINARY_PROTOCOL
5161 #ifndef HEAVY_STATISTICS
5162 #define MANAGED_ALLOCATION
5163 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5164 #define MANAGED_WBARRIER
5170 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5173 mono_sgen_wait_for_suspend_ack (int count)
5177 for (i = 0; i < count; ++i) {
5178 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5179 if (errno != EINTR) {
5180 g_error ("sem_wait ()");
5187 restart_threads_until_none_in_managed_allocator (void)
5189 SgenThreadInfo *info;
5190 int result, num_threads_died = 0;
5191 int sleep_duration = -1;
5194 int restart_count = 0, restarted_count = 0;
5195 /* restart all threads that stopped in the
5197 FOREACH_THREAD (info) {
5200 if (!info->stack_start || info->in_critical_region ||
5201 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5202 binary_protocol_thread_restart ((gpointer)info->id);
5203 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5204 result = thread_resume (pthread_mach_thread_np (info->id));
5206 result = pthread_kill (info->id, restart_signal_num);
5214 /* we set the stopped_ip to
5215 NULL for threads which
5216 we're not restarting so
5217 that we can easily identify
5219 info->stopped_ip = NULL;
5220 info->stopped_domain = NULL;
5222 } END_FOREACH_THREAD
5223 /* if no threads were restarted, we're done */
5224 if (restart_count == 0)
5227 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5228 /* mach thread_resume is synchronous so we dont need to wait for them */
5230 /* wait for the threads to signal their restart */
5231 mono_sgen_wait_for_suspend_ack (restart_count);
5234 if (sleep_duration < 0) {
5238 g_usleep (sleep_duration);
5239 sleep_duration += 10;
5242 /* stop them again */
5243 FOREACH_THREAD (info) {
5244 if (info->skip || info->stopped_ip == NULL)
5246 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5247 result = thread_suspend (pthread_mach_thread_np (info->id));
5249 result = pthread_kill (info->id, suspend_signal_num);
5256 } END_FOREACH_THREAD
5257 /* some threads might have died */
5258 num_threads_died += restart_count - restarted_count;
5259 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5260 /* mach thread_resume is synchronous so we dont need to wait for them */
5262 /* wait for the threads to signal their suspension
5264 mono_sgen_wait_for_suspend_ack (restart_count);
5268 return num_threads_died;
5271 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5273 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5275 SgenThreadInfo *info;
5278 int old_errno = errno;
5280 MonoContext monoctx;
5282 gpointer regs [ARCH_NUM_REGS];
5284 gpointer stack_start;
5286 id = pthread_self ();
5287 info = mono_sgen_thread_info_lookup (id);
5288 info->stopped_domain = mono_domain_get ();
5289 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5290 stop_count = global_stop_count;
5291 /* duplicate signal */
5292 if (0 && info->stop_count == stop_count) {
5296 #ifdef HAVE_KW_THREAD
5297 /* update the remset info in the thread data structure */
5298 info->remset = remembered_set;
5300 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5301 /* If stack_start is not within the limits, then don't set it
5302 in info and we will be restarted. */
5303 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5304 info->stack_start = stack_start;
5307 mono_sigctx_to_monoctx (context, &monoctx);
5308 info->monoctx = &monoctx;
5310 ARCH_COPY_SIGCTX_REGS (regs, context);
5311 info->stopped_regs = regs;
5314 g_assert (!info->stack_start);
5317 /* Notify the JIT */
5318 if (gc_callbacks.thread_suspend_func)
5319 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5321 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5322 /* notify the waiting thread */
5323 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5324 info->stop_count = stop_count;
5326 /* wait until we receive the restart signal */
5329 sigsuspend (&suspend_signal_mask);
5330 } while (info->signal != restart_signal_num);
5332 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5333 /* notify the waiting thread */
5334 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5340 restart_handler (int sig)
5342 SgenThreadInfo *info;
5343 int old_errno = errno;
5345 info = mono_sgen_thread_info_lookup (pthread_self ());
5346 info->signal = restart_signal_num;
5347 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5353 acquire_gc_locks (void)
5359 release_gc_locks (void)
5361 UNLOCK_INTERRUPTION;
5364 static TV_DECLARE (stop_world_time);
5365 static unsigned long max_pause_usec = 0;
5367 /* LOCKING: assumes the GC lock is held */
5369 stop_world (int generation)
5373 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
5374 acquire_gc_locks ();
5376 update_current_thread_stack (&count);
5378 global_stop_count++;
5379 DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()), (gpointer)ARCH_GET_THREAD ()));
5380 TV_GETTIME (stop_world_time);
5381 count = mono_sgen_thread_handshake (suspend_signal_num);
5382 count -= restart_threads_until_none_in_managed_allocator ();
5383 g_assert (count >= 0);
5384 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5385 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
5389 /* LOCKING: assumes the GC lock is held */
5391 restart_world (int generation)
5394 SgenThreadInfo *info;
5395 TV_DECLARE (end_sw);
5398 /* notify the profiler of the leftovers */
5399 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5400 if (moved_objects_idx) {
5401 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5402 moved_objects_idx = 0;
5405 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
5406 FOREACH_THREAD (info) {
5407 info->stack_start = NULL;
5409 info->monoctx = NULL;
5411 info->stopped_regs = NULL;
5413 } END_FOREACH_THREAD
5415 release_gc_locks ();
5417 count = mono_sgen_thread_handshake (restart_signal_num);
5418 TV_GETTIME (end_sw);
5419 usec = TV_ELAPSED (stop_world_time, end_sw);
5420 max_pause_usec = MAX (usec, max_pause_usec);
5421 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5422 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
5426 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5429 mono_sgen_get_current_collection_generation (void)
5431 return current_collection_generation;
5435 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5437 gc_callbacks = *callbacks;
5441 mono_gc_get_gc_callbacks ()
5443 return &gc_callbacks;
5446 /* Variables holding start/end nursery so it won't have to be passed at every call */
5447 static void *scan_area_arg_start, *scan_area_arg_end;
5450 mono_gc_conservatively_scan_area (void *start, void *end)
5452 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5456 mono_gc_scan_object (void *obj)
5458 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
5460 if (current_collection_generation == GENERATION_NURSERY)
5461 major_collector.copy_object (&obj, data->queue);
5463 major_collector.copy_or_mark_object (&obj, data->queue);
5468 * Mark from thread stacks and registers.
5471 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
5473 SgenThreadInfo *info;
5475 scan_area_arg_start = start_nursery;
5476 scan_area_arg_end = end_nursery;
5478 FOREACH_THREAD (info) {
5480 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));
5483 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));
5484 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
5485 UserCopyOrMarkData data = { NULL, queue };
5486 set_user_copy_or_mark_data (&data);
5487 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5488 set_user_copy_or_mark_data (NULL);
5489 } else if (!precise) {
5490 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5495 conservatively_pin_objects_from ((void**)info->monoctx, (void**)info->monoctx + ARCH_NUM_REGS,
5496 start_nursery, end_nursery, PIN_TYPE_STACK);
5499 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5500 start_nursery, end_nursery, PIN_TYPE_STACK);
5502 } END_FOREACH_THREAD
5506 find_pinning_ref_from_thread (char *obj, size_t size)
5509 SgenThreadInfo *info;
5510 char *endobj = obj + size;
5512 FOREACH_THREAD (info) {
5513 char **start = (char**)info->stack_start;
5516 while (start < (char**)info->stack_end) {
5517 if (*start >= obj && *start < endobj) {
5518 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)info->id, start, info->stack_start, info->stack_end));
5523 for (j = 0; j < ARCH_NUM_REGS; ++j) {
5525 mword w = ((mword*)info->monoctx) [j];
5527 mword w = (mword)info->stopped_regs [j];
5530 if (w >= (mword)obj && w < (mword)obj + size)
5531 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)info->id));
5532 } END_FOREACH_THREAD
5537 ptr_on_stack (void *ptr)
5539 gpointer stack_start = &stack_start;
5540 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5542 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5548 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
5555 HEAVY_STAT (++stat_global_remsets_processed);
5557 HEAVY_STAT (++stat_local_remsets_processed);
5559 /* FIXME: exclude stack locations */
5560 switch ((*p) & REMSET_TYPE_MASK) {
5561 case REMSET_LOCATION:
5563 //__builtin_prefetch (ptr);
5564 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5565 gpointer old = *ptr;
5566 major_collector.copy_object (ptr, queue);
5567 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5569 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5570 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5572 * If the object is pinned, each reference to it from nonpinned objects
5573 * becomes part of the global remset, which can grow very large.
5575 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5576 mono_sgen_add_to_global_remset (queue->allocator, ptr);
5579 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5583 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5584 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5587 while (count-- > 0) {
5588 major_collector.copy_object (ptr, queue);
5589 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5590 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5591 mono_sgen_add_to_global_remset (queue->allocator, ptr);
5596 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5597 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5599 major_collector.minor_scan_object ((char*)ptr, queue);
5601 case REMSET_VTYPE: {
5602 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5603 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5608 ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
5612 g_assert_not_reached ();
5617 #ifdef HEAVY_STATISTICS
5619 collect_store_remsets (RememberedSet *remset, mword *bumper)
5621 mword *p = remset->data;
5626 while (p < remset->store_next) {
5627 switch ((*p) & REMSET_TYPE_MASK) {
5628 case REMSET_LOCATION:
5631 ++stat_saved_remsets_1;
5633 if (*p == last1 || *p == last2) {
5634 ++stat_saved_remsets_2;
5651 g_assert_not_reached ();
5661 RememberedSet *remset;
5663 SgenThreadInfo *info;
5665 mword *addresses, *bumper, *p, *r;
5667 FOREACH_THREAD (info) {
5668 for (remset = info->remset; remset; remset = remset->next)
5669 size += remset->store_next - remset->data;
5670 } END_FOREACH_THREAD
5671 for (remset = freed_thread_remsets; remset; remset = remset->next)
5672 size += remset->store_next - remset->data;
5673 for (remset = global_remset; remset; remset = remset->next)
5674 size += remset->store_next - remset->data;
5676 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5678 FOREACH_THREAD (info) {
5679 for (remset = info->remset; remset; remset = remset->next)
5680 bumper = collect_store_remsets (remset, bumper);
5681 } END_FOREACH_THREAD
5682 for (remset = global_remset; remset; remset = remset->next)
5683 bumper = collect_store_remsets (remset, bumper);
5684 for (remset = freed_thread_remsets; remset; remset = remset->next)
5685 bumper = collect_store_remsets (remset, bumper);
5687 g_assert (bumper <= addresses + size);
5689 stat_store_remsets += bumper - addresses;
5691 sort_addresses ((void**)addresses, bumper - addresses);
5694 while (r < bumper) {
5700 stat_store_remsets_unique += p - addresses;
5702 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5707 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5709 *info->store_remset_buffer_index_addr = 0;
5710 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5714 remset_byte_size (RememberedSet *remset)
5716 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5720 scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5722 RememberedSet *remset;
5723 mword *p, *next_p, *store_pos;
5725 /* the global one */
5726 for (remset = global_remset; remset; remset = remset->next) {
5727 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));
5728 store_pos = remset->data;
5729 for (p = remset->data; p < remset->store_next; p = next_p) {
5730 void **ptr = (void**)p [0];
5732 /*Ignore previously processed remset.*/
5733 if (!global_remset_location_was_not_added (ptr)) {
5738 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5741 * Clear global remsets of locations which no longer point to the
5742 * nursery. Otherwise, they could grow indefinitely between major
5745 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5747 if (ptr_in_nursery (*ptr)) {
5748 *store_pos ++ = p [0];
5749 HEAVY_STAT (++stat_global_remsets_readded);
5753 /* Truncate the remset */
5754 remset->store_next = store_pos;
5759 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5762 SgenThreadInfo *info;
5763 RememberedSet *remset;
5764 GenericStoreRememberedSet *store_remset;
5767 #ifdef HEAVY_STATISTICS
5771 /* the generic store ones */
5772 store_remset = generic_store_remsets;
5773 while (store_remset) {
5774 GenericStoreRememberedSet *next = store_remset->next;
5776 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5777 gpointer addr = store_remset->data [i];
5779 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5782 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5784 store_remset = next;
5786 generic_store_remsets = NULL;
5788 /* the per-thread ones */
5789 FOREACH_THREAD (info) {
5790 RememberedSet *next;
5792 for (remset = info->remset; remset; remset = next) {
5793 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));
5794 for (p = remset->data; p < remset->store_next;)
5795 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5796 remset->store_next = remset->data;
5797 next = remset->next;
5798 remset->next = NULL;
5799 if (remset != info->remset) {
5800 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5801 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5804 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5805 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5806 clear_thread_store_remset_buffer (info);
5807 } END_FOREACH_THREAD
5809 /* the freed thread ones */
5810 while (freed_thread_remsets) {
5811 RememberedSet *next;
5812 remset = freed_thread_remsets;
5813 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));
5814 for (p = remset->data; p < remset->store_next;)
5815 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5816 next = remset->next;
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);
5819 freed_thread_remsets = next;
5824 * Clear the info in the remembered sets: we're doing a major collection, so
5825 * the per-thread ones are not needed and the global ones will be reconstructed
5829 clear_remsets (void)
5831 SgenThreadInfo *info;
5832 RememberedSet *remset, *next;
5834 /* the global list */
5835 for (remset = global_remset; remset; remset = next) {
5836 remset->store_next = remset->data;
5837 next = remset->next;
5838 remset->next = NULL;
5839 if (remset != global_remset) {
5840 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5841 mono_sgen_free_internal_dynamic_delayed (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET,
5842 mono_sgen_get_unmanaged_allocator ());
5845 /* the generic store ones */
5846 while (generic_store_remsets) {
5847 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5848 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5849 generic_store_remsets = gs_next;
5851 /* the per-thread ones */
5852 FOREACH_THREAD (info) {
5853 for (remset = info->remset; remset; remset = next) {
5854 remset->store_next = remset->data;
5855 next = remset->next;
5856 remset->next = NULL;
5857 if (remset != info->remset) {
5858 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5859 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5862 clear_thread_store_remset_buffer (info);
5863 } END_FOREACH_THREAD
5865 /* the freed thread ones */
5866 while (freed_thread_remsets) {
5867 next = freed_thread_remsets->next;
5868 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5869 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5870 freed_thread_remsets = next;
5875 * Clear the thread local TLAB variables for all threads.
5880 SgenThreadInfo *info;
5882 FOREACH_THREAD (info) {
5883 /* A new TLAB will be allocated when the thread does its first allocation */
5884 *info->tlab_start_addr = NULL;
5885 *info->tlab_next_addr = NULL;
5886 *info->tlab_temp_end_addr = NULL;
5887 *info->tlab_real_end_addr = NULL;
5888 } END_FOREACH_THREAD
5891 /* LOCKING: assumes the GC lock is held */
5892 static SgenThreadInfo*
5893 gc_register_current_thread (void *addr)
5896 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5897 #ifndef HAVE_KW_THREAD
5898 SgenThreadInfo *__thread_info__ = info;
5904 memset (info, 0, sizeof (SgenThreadInfo));
5905 #ifndef HAVE_KW_THREAD
5906 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5908 g_assert (!pthread_getspecific (thread_info_key));
5909 pthread_setspecific (thread_info_key, info);
5914 info->id = ARCH_GET_THREAD ();
5915 info->stop_count = -1;
5918 info->stack_start = NULL;
5919 info->tlab_start_addr = &TLAB_START;
5920 info->tlab_next_addr = &TLAB_NEXT;
5921 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5922 info->tlab_real_end_addr = &TLAB_REAL_END;
5923 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5924 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5925 info->stopped_ip = NULL;
5926 info->stopped_domain = NULL;
5928 info->monoctx = NULL;
5930 info->stopped_regs = NULL;
5933 binary_protocol_thread_register ((gpointer)info->id);
5935 #ifdef HAVE_KW_THREAD
5936 tlab_next_addr = &tlab_next;
5937 store_remset_buffer_index_addr = &store_remset_buffer_index;
5940 /* try to get it with attributes first */
5941 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5945 pthread_attr_t attr;
5946 pthread_getattr_np (pthread_self (), &attr);
5947 pthread_attr_getstack (&attr, &sstart, &size);
5948 info->stack_start_limit = sstart;
5949 info->stack_end = (char*)sstart + size;
5950 pthread_attr_destroy (&attr);
5952 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5953 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5954 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5957 /* FIXME: we assume the stack grows down */
5958 gsize stack_bottom = (gsize)addr;
5959 stack_bottom += 4095;
5960 stack_bottom &= ~4095;
5961 info->stack_end = (char*)stack_bottom;
5965 #ifdef HAVE_KW_THREAD
5966 stack_end = info->stack_end;
5969 /* hash into the table */
5970 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
5971 info->next = thread_table [hash];
5972 thread_table [hash] = info;
5974 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
5975 pthread_setspecific (remembered_set_key, info->remset);
5976 #ifdef HAVE_KW_THREAD
5977 remembered_set = info->remset;
5980 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5981 STORE_REMSET_BUFFER_INDEX = 0;
5983 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
5985 if (gc_callbacks.thread_attach_func)
5986 info->runtime_data = gc_callbacks.thread_attach_func ();
5992 add_generic_store_remset_from_buffer (gpointer *buffer)
5994 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5995 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5996 remset->next = generic_store_remsets;
5997 generic_store_remsets = remset;
6001 unregister_current_thread (void)
6004 SgenThreadInfo *prev = NULL;
6006 RememberedSet *rset;
6007 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6009 binary_protocol_thread_unregister ((gpointer)id);
6011 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6012 p = thread_table [hash];
6014 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6015 while (!ARCH_THREAD_EQUALS (p->id, id)) {
6020 thread_table [hash] = p->next;
6022 prev->next = p->next;
6025 if (gc_callbacks.thread_detach_func) {
6026 gc_callbacks.thread_detach_func (p->runtime_data);
6027 p->runtime_data = NULL;
6031 if (freed_thread_remsets) {
6032 for (rset = p->remset; rset->next; rset = rset->next)
6034 rset->next = freed_thread_remsets;
6035 freed_thread_remsets = p->remset;
6037 freed_thread_remsets = p->remset;
6040 if (*p->store_remset_buffer_index_addr)
6041 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6042 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6047 unregister_thread (void *k)
6049 /* If a delegate is passed to native code and invoked on a thread we dont
6050 * know about, the jit will register it with mono_jit_thead_attach, but
6051 * we have no way of knowing when that thread goes away. SGen has a TSD
6052 * so we assume that if the domain is still registered, we can detach
6055 if (mono_domain_get ())
6056 mono_thread_detach (mono_thread_current ());
6059 unregister_current_thread ();
6064 mono_gc_register_thread (void *baseptr)
6066 SgenThreadInfo *info;
6070 info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
6072 info = gc_register_current_thread (baseptr);
6074 /* The main thread might get registered before callbacks are set */
6075 if (gc_callbacks.thread_attach_func && !info->runtime_data)
6076 info->runtime_data = gc_callbacks.thread_attach_func ();
6080 /* Need a better place to initialize this */
6081 if (!array_fill_vtable && mono_get_root_domain ()) {
6082 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
6085 return info != NULL;
6089 * mono_gc_set_stack_end:
6091 * Set the end of the current threads stack to STACK_END. The stack space between
6092 * STACK_END and the real end of the threads stack will not be scanned during collections.
6095 mono_gc_set_stack_end (void *stack_end)
6097 SgenThreadInfo *info;
6100 info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
6102 g_assert (stack_end < info->stack_end);
6103 info->stack_end = stack_end;
6108 #if USE_PTHREAD_INTERCEPT
6111 void *(*start_routine) (void *);
6114 MonoSemType registered;
6115 } SgenThreadStartInfo;
6118 gc_start_thread (void *arg)
6120 SgenThreadStartInfo *start_info = arg;
6121 SgenThreadInfo* info;
6122 void *t_arg = start_info->arg;
6123 void *(*start_func) (void*) = start_info->start_routine;
6128 info = gc_register_current_thread (&result);
6130 post_result = MONO_SEM_POST (&(start_info->registered));
6131 g_assert (!post_result);
6132 result = start_func (t_arg);
6133 g_assert (!mono_domain_get ());
6135 * this is done by the pthread key dtor
6137 unregister_current_thread ();
6145 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6147 SgenThreadStartInfo *start_info;
6150 start_info = malloc (sizeof (SgenThreadStartInfo));
6153 MONO_SEM_INIT (&(start_info->registered), 0);
6154 start_info->arg = arg;
6155 start_info->start_routine = start_routine;
6157 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6159 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6160 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6163 MONO_SEM_DESTROY (&(start_info->registered));
6169 mono_gc_pthread_join (pthread_t thread, void **retval)
6171 return pthread_join (thread, retval);
6175 mono_gc_pthread_detach (pthread_t thread)
6177 return pthread_detach (thread);
6180 #endif /* USE_PTHREAD_INTERCEPT */
6183 * ######################################################################
6184 * ######## Write barriers
6185 * ######################################################################
6189 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
6192 dummy_use (gpointer v) {
6193 __asm__ volatile ("" : "=r"(v) : "r"(v));
6197 static RememberedSet*
6198 alloc_remset (int size, gpointer id) {
6199 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6200 res->store_next = res->data;
6201 res->end_set = res->data + size;
6203 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6207 static RememberedSet*
6208 alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id)
6210 RememberedSet* res = mono_sgen_alloc_internal_full (alc, sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6211 res->store_next = res->data;
6212 res->end_set = res->data + size;
6214 DEBUG (4, fprintf (gc_debug_file, "Allocated global remset size %d at %p for %p\n", size, res->data, id));
6219 * Note: the write barriers first do the needed GC work and then do the actual store:
6220 * this way the value is visible to the conservative GC scan after the write barrier
6221 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6222 * the conservative scan, otherwise by the remembered set scan.
6225 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6227 HEAVY_STAT (++stat_wbarrier_set_field);
6228 if (ptr_in_nursery (field_ptr)) {
6229 *(void**)field_ptr = value;
6232 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6233 if (use_cardtable) {
6234 *(void**)field_ptr = value;
6235 if (ptr_in_nursery (value))
6236 sgen_card_table_mark_address ((mword)field_ptr);
6243 rs = REMEMBERED_SET;
6244 if (rs->store_next < rs->end_set) {
6245 *(rs->store_next++) = (mword)field_ptr;
6246 *(void**)field_ptr = value;
6250 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6251 rs->next = REMEMBERED_SET;
6252 REMEMBERED_SET = rs;
6253 #ifdef HAVE_KW_THREAD
6254 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6256 *(rs->store_next++) = (mword)field_ptr;
6257 *(void**)field_ptr = value;
6263 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6265 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6266 if (ptr_in_nursery (slot_ptr)) {
6267 *(void**)slot_ptr = value;
6270 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6271 if (use_cardtable) {
6272 *(void**)slot_ptr = value;
6273 if (ptr_in_nursery (value))
6274 sgen_card_table_mark_address ((mword)slot_ptr);
6281 rs = REMEMBERED_SET;
6282 if (rs->store_next < rs->end_set) {
6283 *(rs->store_next++) = (mword)slot_ptr;
6284 *(void**)slot_ptr = value;
6288 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6289 rs->next = REMEMBERED_SET;
6290 REMEMBERED_SET = rs;
6291 #ifdef HAVE_KW_THREAD
6292 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6294 *(rs->store_next++) = (mword)slot_ptr;
6295 *(void**)slot_ptr = value;
6301 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6303 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6304 /*This check can be done without taking a lock since dest_ptr array is pinned*/
6305 if (ptr_in_nursery (dest_ptr) || count <= 0) {
6306 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6310 if (use_cardtable) {
6311 gpointer *dest = dest_ptr;
6312 gpointer *src = src_ptr;
6314 /*overlapping that required backward copying*/
6315 if (src < dest && (src + count) > dest) {
6316 gpointer *start = dest;
6320 for (; dest >= start; --src, --dest) {
6321 gpointer value = *src;
6323 if (ptr_in_nursery (value))
6324 sgen_card_table_mark_address ((mword)dest);
6328 gpointer *end = dest + count;
6329 for (; dest < end; ++src, ++dest) {
6330 gpointer value = *src;
6332 if (ptr_in_nursery (value))
6333 sgen_card_table_mark_address ((mword)dest);
6341 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6343 rs = REMEMBERED_SET;
6344 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6345 if (rs->store_next + 1 < rs->end_set) {
6346 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6347 *(rs->store_next++) = count;
6351 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6352 rs->next = REMEMBERED_SET;
6353 REMEMBERED_SET = rs;
6354 #ifdef HAVE_KW_THREAD
6355 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6357 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6358 *(rs->store_next++) = count;
6364 static char *found_obj;
6367 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
6369 char *ptr = user_data;
6371 if (ptr >= obj && ptr < obj + size) {
6372 g_assert (!found_obj);
6377 /* for use in the debugger */
6378 char* find_object_for_ptr (char *ptr);
6380 find_object_for_ptr (char *ptr)
6382 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6384 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6385 find_object_for_ptr_callback, ptr, TRUE);
6391 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
6396 * Very inefficient, but this is debugging code, supposed to
6397 * be called from gdb, so we don't care.
6400 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
6405 evacuate_remset_buffer (void)
6410 buffer = STORE_REMSET_BUFFER;
6412 add_generic_store_remset_from_buffer (buffer);
6413 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6415 STORE_REMSET_BUFFER_INDEX = 0;
6419 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6425 HEAVY_STAT (++stat_wbarrier_generic_store);
6427 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6428 /* FIXME: ptr_in_heap must be called with the GC lock held */
6429 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6430 char *start = find_object_for_ptr (ptr);
6431 MonoObject *value = *(MonoObject**)ptr;
6435 MonoObject *obj = (MonoObject*)start;
6436 if (obj->vtable->domain != value->vtable->domain)
6437 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6443 if (*(gpointer*)ptr)
6444 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6446 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6447 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6451 if (use_cardtable) {
6452 if (ptr_in_nursery(*(gpointer*)ptr))
6453 sgen_card_table_mark_address ((mword)ptr);
6459 buffer = STORE_REMSET_BUFFER;
6460 index = STORE_REMSET_BUFFER_INDEX;
6461 /* This simple optimization eliminates a sizable portion of
6462 entries. Comparing it to the last but one entry as well
6463 doesn't eliminate significantly more entries. */
6464 if (buffer [index] == ptr) {
6469 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6470 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6473 if (index >= STORE_REMSET_BUFFER_SIZE) {
6474 evacuate_remset_buffer ();
6475 index = STORE_REMSET_BUFFER_INDEX;
6476 g_assert (index == 0);
6479 buffer [index] = ptr;
6480 STORE_REMSET_BUFFER_INDEX = index;
6486 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6488 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6489 *(void**)ptr = value;
6490 if (ptr_in_nursery (value))
6491 mono_gc_wbarrier_generic_nostore (ptr);
6495 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
6497 mword *dest = _dest;
6502 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
6507 size -= SIZEOF_VOID_P;
6514 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6517 size_t size = count * mono_class_value_size (klass, NULL);
6519 HEAVY_STAT (++stat_wbarrier_value_copy);
6520 g_assert (klass->valuetype);
6522 memmove (dest, src, size);
6523 if (use_cardtable) {
6524 sgen_card_table_mark_range ((mword)dest, size);
6526 rs = REMEMBERED_SET;
6527 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
6531 g_assert (klass->gc_descr_inited);
6532 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));
6534 if (rs->store_next + 3 < rs->end_set) {
6535 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6536 *(rs->store_next++) = (mword)klass->gc_descr;
6537 *(rs->store_next++) = (mword)count;
6541 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6542 rs->next = REMEMBERED_SET;
6543 REMEMBERED_SET = rs;
6544 #ifdef HAVE_KW_THREAD
6545 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6547 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6548 *(rs->store_next++) = (mword)klass->gc_descr;
6549 *(rs->store_next++) = (mword)count;
6555 * mono_gc_wbarrier_object_copy:
6557 * Write barrier to call when obj is the result of a clone or copy of an object.
6560 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6566 HEAVY_STAT (++stat_wbarrier_object_copy);
6567 rs = REMEMBERED_SET;
6568 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6569 size = mono_object_class (obj)->instance_size;
6571 /* do not copy the sync state */
6572 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6573 size - sizeof (MonoObject));
6574 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6578 if (rs->store_next < rs->end_set) {
6579 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6583 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6584 rs->next = REMEMBERED_SET;
6585 REMEMBERED_SET = rs;
6586 #ifdef HAVE_KW_THREAD
6587 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6589 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6594 * ######################################################################
6595 * ######## Collector debugging
6596 * ######################################################################
6599 const char*descriptor_types [] = {
6611 describe_ptr (char *ptr)
6618 if (ptr_in_nursery (ptr)) {
6619 printf ("Pointer inside nursery.\n");
6621 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
6623 printf ("Pointer is the start of object %p in LOS space.\n", start);
6625 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
6627 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
6628 printf ("Pointer inside oldspace.\n");
6629 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
6630 printf ("Pointer is inside a pinned chunk.\n");
6632 printf ("Pointer unknown.\n");
6637 if (object_is_pinned (ptr))
6638 printf ("Object is pinned.\n");
6640 if (object_is_forwarded (ptr))
6641 printf ("Object is forwared.\n");
6643 // FIXME: Handle pointers to the inside of objects
6644 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6646 printf ("VTable: %p\n", vtable);
6647 if (vtable == NULL) {
6648 printf ("VTable is invalid (empty).\n");
6651 if (ptr_in_nursery (vtable)) {
6652 printf ("VTable is invalid (points inside nursery).\n");
6655 printf ("Class: %s\n", vtable->klass->name);
6657 desc = ((GCVTable*)vtable)->desc;
6658 printf ("Descriptor: %lx\n", (long)desc);
6661 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6665 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6671 switch ((*p) & REMSET_TYPE_MASK) {
6672 case REMSET_LOCATION:
6673 if (*p == (mword)addr)
6677 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6679 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6683 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6684 count = safe_object_get_size ((MonoObject*)ptr);
6685 count = ALIGN_UP (count);
6686 count /= sizeof (mword);
6687 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6691 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6695 switch (desc & 0x7) {
6696 case DESC_TYPE_RUN_LENGTH:
6697 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6699 case DESC_TYPE_SMALL_BITMAP:
6700 OBJ_BITMAP_SIZE (skip_size, desc, start);
6704 g_assert_not_reached ();
6707 /* The descriptor includes the size of MonoObject */
6708 skip_size -= sizeof (MonoObject);
6710 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6715 g_assert_not_reached ();
6721 * Return whenever ADDR occurs in the remembered sets
6724 find_in_remsets (char *addr)
6727 SgenThreadInfo *info;
6728 RememberedSet *remset;
6729 GenericStoreRememberedSet *store_remset;
6731 gboolean found = FALSE;
6733 /* the global one */
6734 for (remset = global_remset; remset; remset = remset->next) {
6735 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));
6736 for (p = remset->data; p < remset->store_next;) {
6737 p = find_in_remset_loc (p, addr, &found);
6743 /* the generic store ones */
6744 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6745 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6746 if (store_remset->data [i] == addr)
6751 /* the per-thread ones */
6752 FOREACH_THREAD (info) {
6754 for (remset = info->remset; remset; remset = remset->next) {
6755 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));
6756 for (p = remset->data; p < remset->store_next;) {
6757 p = find_in_remset_loc (p, addr, &found);
6762 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6763 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6766 } END_FOREACH_THREAD
6768 /* the freed thread ones */
6769 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6770 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));
6771 for (p = remset->data; p < remset->store_next;) {
6772 p = find_in_remset_loc (p, addr, &found);
6781 static gboolean missing_remsets;
6784 * We let a missing remset slide if the target object is pinned,
6785 * because the store might have happened but the remset not yet added,
6786 * but in that case the target must be pinned. We might theoretically
6787 * miss some missing remsets this way, but it's very unlikely.
6790 #define HANDLE_PTR(ptr,obj) do { \
6791 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6792 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6793 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); \
6794 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6795 if (!object_is_pinned (*(ptr))) \
6796 missing_remsets = TRUE; \
6802 * Check that each object reference which points into the nursery can
6803 * be found in the remembered sets.
6806 check_consistency_callback (char *start, size_t size, void *dummy)
6808 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6809 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6811 #define SCAN_OBJECT_ACTION
6812 #include "sgen-scan-object.h"
6816 * Perform consistency check of the heap.
6818 * Assumes the world is stopped.
6821 check_consistency (void)
6823 // Need to add more checks
6825 missing_remsets = FALSE;
6827 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6829 // Check that oldspace->newspace pointers are registered with the collector
6830 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6832 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6834 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6836 if (!binary_protocol_is_enabled ())
6837 g_assert (!missing_remsets);
6842 #define HANDLE_PTR(ptr,obj) do { \
6843 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6844 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6848 check_major_refs_callback (char *start, size_t size, void *dummy)
6850 #define SCAN_OBJECT_ACTION
6851 #include "sgen-scan-object.h"
6855 check_major_refs (void)
6857 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6858 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6861 /* Check that the reference is valid */
6863 #define HANDLE_PTR(ptr,obj) do { \
6865 g_assert (safe_name (*(ptr)) != NULL); \
6872 * Perform consistency check on an object. Currently we only check that the
6873 * reference fields are valid.
6876 check_object (char *start)
6881 #include "sgen-scan-object.h"
6885 * ######################################################################
6886 * ######## Other mono public interface functions.
6887 * ######################################################################
6890 #define REFS_SIZE 128
6893 MonoGCReferences callback;
6897 MonoObject *refs [REFS_SIZE];
6898 uintptr_t offsets [REFS_SIZE];
6902 #define HANDLE_PTR(ptr,obj) do { \
6904 if (hwi->count == REFS_SIZE) { \
6905 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
6909 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
6910 hwi->refs [hwi->count++] = *(ptr); \
6915 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6917 #include "sgen-scan-object.h"
6921 walk_references (char *start, size_t size, void *data)
6923 HeapWalkInfo *hwi = data;
6926 collect_references (hwi, start, size);
6927 if (hwi->count || !hwi->called)
6928 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6932 * mono_gc_walk_heap:
6933 * @flags: flags for future use
6934 * @callback: a function pointer called for each object in the heap
6935 * @data: a user data pointer that is passed to callback
6937 * This function can be used to iterate over all the live objects in the heap:
6938 * for each object, @callback is invoked, providing info about the object's
6939 * location in memory, its class, its size and the objects it references.
6940 * For each referenced object it's offset from the object address is
6941 * reported in the offsets array.
6942 * The object references may be buffered, so the callback may be invoked
6943 * multiple times for the same object: in all but the first call, the size
6944 * argument will be zero.
6945 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6946 * profiler event handler.
6948 * Returns: a non-zero value if the GC doesn't support heap walking
6951 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6956 hwi.callback = callback;
6959 clear_nursery_fragments (nursery_next);
6960 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
6962 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
6963 mono_sgen_los_iterate_objects (walk_references, &hwi);
6969 mono_gc_collect (int generation)
6974 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6975 stop_world (generation);
6976 if (generation == 0) {
6977 collect_nursery (0);
6979 major_collection ("user request");
6981 restart_world (generation);
6982 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6987 mono_gc_max_generation (void)
6993 mono_gc_collection_count (int generation)
6995 if (generation == 0)
6996 return num_minor_gcs;
6997 return num_major_gcs;
7001 mono_gc_get_used_size (void)
7005 tot = los_memory_usage;
7006 tot += nursery_section->next_data - nursery_section->data;
7007 tot += major_collector.get_used_size ();
7008 /* FIXME: account for pinned objects */
7014 mono_gc_get_heap_size (void)
7020 mono_gc_disable (void)
7028 mono_gc_enable (void)
7036 mono_gc_get_los_limit (void)
7038 return MAX_SMALL_OBJ_SIZE;
7042 mono_object_is_alive (MonoObject* o)
7048 mono_gc_get_generation (MonoObject *obj)
7050 if (ptr_in_nursery (obj))
7056 mono_gc_enable_events (void)
7061 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7064 mono_gc_register_disappearing_link (obj, link_addr, track);
7069 mono_gc_weak_link_remove (void **link_addr)
7072 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7077 mono_gc_weak_link_get (void **link_addr)
7081 return (MonoObject*) REVEAL_POINTER (*link_addr);
7085 mono_gc_ephemeron_array_add (MonoObject *obj)
7087 EphemeronLinkNode *node;
7091 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
7096 node->array = (char*)obj;
7097 node->next = ephemeron_list;
7098 ephemeron_list = node;
7100 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
7107 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7110 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
7111 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7112 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7114 mword complex = alloc_complex_descriptor (bitmap, numbits);
7115 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7119 static void *all_ref_root_descrs [32];
7122 mono_gc_make_root_descr_all_refs (int numbits)
7127 if (numbits < 32 && all_ref_root_descrs [numbits])
7128 return all_ref_root_descrs [numbits];
7130 gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
7131 memset (gc_bitmap, 0xff, numbits / 8);
7133 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
7134 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
7138 all_ref_root_descrs [numbits] = descr;
7144 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7148 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7149 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7150 user_descriptors [user_descriptors_next ++] = marker;
7156 mono_gc_alloc_fixed (size_t size, void *descr)
7158 /* FIXME: do a single allocation */
7159 void *res = calloc (1, size);
7162 if (!mono_gc_register_root (res, size, descr)) {
7170 mono_gc_free_fixed (void* addr)
7172 mono_gc_deregister_root (addr);
7177 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7181 result = func (data);
7182 UNLOCK_INTERRUPTION;
7187 mono_gc_is_gc_thread (void)
7191 result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
7197 mono_gc_base_init (void)
7201 char *major_collector_opt = NULL;
7202 struct sigaction sinfo;
7206 /* the gc_initialized guard seems to imply this method is
7207 idempotent, but LOCK_INIT(gc_mutex) might not be. It's
7208 defined in sgen-gc.h as nothing, so there's no danger at
7210 LOCK_INIT (gc_mutex);
7212 if (gc_initialized) {
7216 pagesize = mono_pagesize ();
7217 gc_debug_file = stdout;
7219 LOCK_INIT (interruption_mutex);
7220 LOCK_INIT (global_remset_mutex);
7221 LOCK_INIT (pin_queue_mutex);
7223 if ((env = getenv ("MONO_GC_PARAMS"))) {
7224 opts = g_strsplit (env, ",", -1);
7225 for (ptr = opts; *ptr; ++ptr) {
7227 if (g_str_has_prefix (opt, "major=")) {
7228 opt = strchr (opt, '=') + 1;
7229 major_collector_opt = g_strdup (opt);
7237 mono_sgen_init_internal_allocator ();
7239 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
7240 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
7241 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
7242 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
7243 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
7244 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
7245 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
7246 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
7247 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
7249 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
7250 mono_sgen_marksweep_init (&major_collector);
7251 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
7252 mono_sgen_marksweep_fixed_init (&major_collector);
7253 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
7254 mono_sgen_marksweep_par_init (&major_collector);
7255 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
7256 mono_sgen_marksweep_fixed_par_init (&major_collector);
7257 } else if (!strcmp (major_collector_opt, "copying")) {
7258 mono_sgen_copying_init (&major_collector);
7260 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
7264 #ifdef SGEN_HAVE_CARDTABLE
7265 use_cardtable = major_collector.supports_cardtable;
7267 use_cardtable = FALSE;
7270 num_workers = mono_cpu_count ();
7271 g_assert (num_workers > 0);
7272 if (num_workers > 16)
7275 /* Keep this the default for now */
7276 conservative_stack_mark = TRUE;
7279 for (ptr = opts; *ptr; ++ptr) {
7281 if (g_str_has_prefix (opt, "major="))
7283 if (g_str_has_prefix (opt, "wbarrier=")) {
7284 opt = strchr (opt, '=') + 1;
7285 if (strcmp (opt, "remset") == 0) {
7286 use_cardtable = FALSE;
7287 } else if (strcmp (opt, "cardtable") == 0) {
7288 if (!use_cardtable) {
7289 if (major_collector.supports_cardtable)
7290 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
7292 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
7298 if (g_str_has_prefix (opt, "max-heap-size=")) {
7299 opt = strchr (opt, '=') + 1;
7300 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
7301 if ((max_heap & (mono_pagesize () - 1))) {
7302 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
7306 fprintf (stderr, "max-heap-size must be an integer.\n");
7311 if (g_str_has_prefix (opt, "workers=")) {
7314 if (!major_collector.is_parallel) {
7315 fprintf (stderr, "The workers= option can only be used for parallel collectors.");
7318 opt = strchr (opt, '=') + 1;
7319 val = strtol (opt, &endptr, 10);
7320 if (!*opt || *endptr) {
7321 fprintf (stderr, "Cannot parse the workers= option value.");
7324 if (val <= 0 || val > 16) {
7325 fprintf (stderr, "The number of workers must be in the range 1 to 16.");
7328 num_workers = (int)val;
7331 if (g_str_has_prefix (opt, "stack-mark=")) {
7332 opt = strchr (opt, '=') + 1;
7333 if (!strcmp (opt, "precise")) {
7334 conservative_stack_mark = FALSE;
7335 } else if (!strcmp (opt, "conservative")) {
7336 conservative_stack_mark = TRUE;
7338 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
7344 if (g_str_has_prefix (opt, "nursery-size=")) {
7346 opt = strchr (opt, '=') + 1;
7347 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
7348 default_nursery_size = val;
7349 #ifdef SGEN_ALIGN_NURSERY
7350 if ((val & (val - 1))) {
7351 fprintf (stderr, "The nursery size must be a power of two.\n");
7355 default_nursery_bits = 0;
7356 while (1 << (++ default_nursery_bits) != default_nursery_size)
7360 fprintf (stderr, "nursery-size must be an integer.\n");
7366 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
7367 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
7368 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7369 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7370 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
7371 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
7372 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
7373 if (major_collector.print_gc_param_usage)
7374 major_collector.print_gc_param_usage ();
7381 if (major_collector.is_parallel)
7382 workers_init (num_workers);
7384 if (major_collector_opt)
7385 g_free (major_collector_opt);
7387 nursery_size = DEFAULT_NURSERY_SIZE;
7388 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7389 init_heap_size_limits (max_heap);
7393 if ((env = getenv ("MONO_GC_DEBUG"))) {
7394 opts = g_strsplit (env, ",", -1);
7395 for (ptr = opts; ptr && *ptr; ptr ++) {
7397 if (opt [0] >= '0' && opt [0] <= '9') {
7398 gc_debug_level = atoi (opt);
7403 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7404 gc_debug_file = fopen (rf, "wb");
7406 gc_debug_file = stderr;
7409 } else if (!strcmp (opt, "collect-before-allocs")) {
7410 collect_before_allocs = 1;
7411 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
7412 char *arg = strchr (opt, '=') + 1;
7413 collect_before_allocs = atoi (arg);
7414 } else if (!strcmp (opt, "check-at-minor-collections")) {
7415 consistency_check_at_minor_collection = TRUE;
7416 nursery_clear_policy = CLEAR_AT_GC;
7417 } else if (!strcmp (opt, "xdomain-checks")) {
7418 xdomain_checks = TRUE;
7419 } else if (!strcmp (opt, "clear-at-gc")) {
7420 nursery_clear_policy = CLEAR_AT_GC;
7421 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
7422 nursery_clear_policy = CLEAR_AT_GC;
7423 } else if (!strcmp (opt, "check-scan-starts")) {
7424 do_scan_starts_check = TRUE;
7425 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7426 char *filename = strchr (opt, '=') + 1;
7427 nursery_clear_policy = CLEAR_AT_GC;
7428 heap_dump_file = fopen (filename, "w");
7430 fprintf (heap_dump_file, "<sgen-dump>\n");
7431 #ifdef SGEN_BINARY_PROTOCOL
7432 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7433 char *filename = strchr (opt, '=') + 1;
7434 binary_protocol_init (filename);
7437 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7438 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7439 fprintf (stderr, "Valid options are: collect-before-allocs[=<n>], check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
7446 if (major_collector.post_param_init)
7447 major_collector.post_param_init ();
7449 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7450 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7452 sigfillset (&sinfo.sa_mask);
7453 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7454 sinfo.sa_sigaction = suspend_handler;
7455 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7456 g_error ("failed sigaction");
7459 sinfo.sa_handler = restart_handler;
7460 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7461 g_error ("failed sigaction");
7464 sigfillset (&suspend_signal_mask);
7465 sigdelset (&suspend_signal_mask, restart_signal_num);
7467 global_remset = alloc_remset (1024, NULL);
7468 global_remset->next = NULL;
7470 pthread_key_create (&remembered_set_key, unregister_thread);
7472 #ifndef HAVE_KW_THREAD
7473 pthread_key_create (&thread_info_key, NULL);
7479 gc_initialized = TRUE;
7481 mono_gc_register_thread (&sinfo);
7485 mono_gc_get_suspend_signal (void)
7487 return suspend_signal_num;
7497 #ifdef HAVE_KW_THREAD
7498 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7499 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7500 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7501 mono_mb_emit_i4 ((mb), (offset)); \
7506 * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
7507 * where the two are the same.
7510 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7511 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7512 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7513 mono_mb_emit_i4 ((mb), thread_info_key); \
7514 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7515 mono_mb_emit_byte ((mb), CEE_ADD); \
7516 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7519 #define EMIT_TLS_ACCESS(mb,member,dummy) do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
7524 #ifdef MANAGED_ALLOCATION
7525 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7526 * for each class. This is currently not easy to do, as it is hard to generate basic
7527 * blocks + branches, but it is easy with the linear IL codebase.
7529 * For this to work we'd need to solve the TLAB race, first. Now we
7530 * require the allocator to be in a few known methods to make sure
7531 * that they are executed atomically via the restart mechanism.
7534 create_allocator (int atype)
7536 int p_var, size_var;
7537 guint32 slowpath_branch, max_size_branch;
7538 MonoMethodBuilder *mb;
7540 MonoMethodSignature *csig;
7541 static gboolean registered = FALSE;
7542 int tlab_next_addr_var, new_next_var;
7544 const char *name = NULL;
7545 AllocatorWrapperInfo *info;
7547 #ifdef HAVE_KW_THREAD
7548 int tlab_next_addr_offset = -1;
7549 int tlab_temp_end_offset = -1;
7551 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7552 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7554 g_assert (tlab_next_addr_offset != -1);
7555 g_assert (tlab_temp_end_offset != -1);
7559 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7560 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7564 if (atype == ATYPE_SMALL) {
7566 name = "AllocSmall";
7567 } else if (atype == ATYPE_NORMAL) {
7570 } else if (atype == ATYPE_VECTOR) {
7572 name = "AllocVector";
7574 g_assert_not_reached ();
7577 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7578 csig->ret = &mono_defaults.object_class->byval_arg;
7579 for (i = 0; i < num_params; ++i)
7580 csig->params [i] = &mono_defaults.int_class->byval_arg;
7582 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7583 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7584 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7585 /* size = vtable->klass->instance_size; */
7586 mono_mb_emit_ldarg (mb, 0);
7587 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7588 mono_mb_emit_byte (mb, CEE_ADD);
7589 mono_mb_emit_byte (mb, CEE_LDIND_I);
7590 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7591 mono_mb_emit_byte (mb, CEE_ADD);
7592 /* FIXME: assert instance_size stays a 4 byte integer */
7593 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7594 mono_mb_emit_stloc (mb, size_var);
7595 } else if (atype == ATYPE_VECTOR) {
7596 MonoExceptionClause *clause;
7598 MonoClass *oom_exc_class;
7601 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7602 mono_mb_emit_ldarg (mb, 1);
7603 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7604 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7605 mono_mb_emit_exception (mb, "OverflowException", NULL);
7606 mono_mb_patch_short_branch (mb, pos);
7608 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7609 clause->try_offset = mono_mb_get_label (mb);
7611 /* vtable->klass->sizes.element_size */
7612 mono_mb_emit_ldarg (mb, 0);
7613 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7614 mono_mb_emit_byte (mb, CEE_ADD);
7615 mono_mb_emit_byte (mb, CEE_LDIND_I);
7616 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7617 mono_mb_emit_byte (mb, CEE_ADD);
7618 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7621 mono_mb_emit_ldarg (mb, 1);
7622 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7623 /* + sizeof (MonoArray) */
7624 mono_mb_emit_icon (mb, sizeof (MonoArray));
7625 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7626 mono_mb_emit_stloc (mb, size_var);
7628 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7631 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7632 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7633 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7634 "System", "OverflowException");
7635 g_assert (clause->data.catch_class);
7636 clause->handler_offset = mono_mb_get_label (mb);
7638 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7639 "System", "OutOfMemoryException");
7640 g_assert (oom_exc_class);
7641 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7644 mono_mb_emit_byte (mb, CEE_POP);
7645 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7646 mono_mb_emit_byte (mb, CEE_THROW);
7648 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7649 mono_mb_set_clauses (mb, 1, clause);
7650 mono_mb_patch_branch (mb, pos_leave);
7653 g_assert_not_reached ();
7656 /* size += ALLOC_ALIGN - 1; */
7657 mono_mb_emit_ldloc (mb, size_var);
7658 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7659 mono_mb_emit_byte (mb, CEE_ADD);
7660 /* size &= ~(ALLOC_ALIGN - 1); */
7661 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7662 mono_mb_emit_byte (mb, CEE_AND);
7663 mono_mb_emit_stloc (mb, size_var);
7665 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7666 if (atype != ATYPE_SMALL) {
7667 mono_mb_emit_ldloc (mb, size_var);
7668 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7669 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7673 * We need to modify tlab_next, but the JIT only supports reading, so we read
7674 * another tls var holding its address instead.
7677 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7678 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7679 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7680 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7682 /* p = (void**)tlab_next; */
7683 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7684 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7685 mono_mb_emit_byte (mb, CEE_LDIND_I);
7686 mono_mb_emit_stloc (mb, p_var);
7688 /* new_next = (char*)p + size; */
7689 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7690 mono_mb_emit_ldloc (mb, p_var);
7691 mono_mb_emit_ldloc (mb, size_var);
7692 mono_mb_emit_byte (mb, CEE_CONV_I);
7693 mono_mb_emit_byte (mb, CEE_ADD);
7694 mono_mb_emit_stloc (mb, new_next_var);
7696 /* tlab_next = new_next */
7697 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7698 mono_mb_emit_ldloc (mb, new_next_var);
7699 mono_mb_emit_byte (mb, CEE_STIND_I);
7701 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7702 mono_mb_emit_ldloc (mb, new_next_var);
7703 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7704 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7707 if (atype != ATYPE_SMALL)
7708 mono_mb_patch_short_branch (mb, max_size_branch);
7710 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7711 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7713 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7714 mono_mb_emit_ldarg (mb, 0);
7715 mono_mb_emit_ldloc (mb, size_var);
7716 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7717 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7718 } else if (atype == ATYPE_VECTOR) {
7719 mono_mb_emit_ldarg (mb, 1);
7720 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7722 g_assert_not_reached ();
7724 mono_mb_emit_byte (mb, CEE_RET);
7727 mono_mb_patch_short_branch (mb, slowpath_branch);
7729 /* FIXME: Memory barrier */
7732 mono_mb_emit_ldloc (mb, p_var);
7733 mono_mb_emit_ldarg (mb, 0);
7734 mono_mb_emit_byte (mb, CEE_STIND_I);
7736 if (atype == ATYPE_VECTOR) {
7737 /* arr->max_length = max_length; */
7738 mono_mb_emit_ldloc (mb, p_var);
7739 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7740 mono_mb_emit_ldarg (mb, 1);
7741 mono_mb_emit_byte (mb, CEE_STIND_I);
7745 mono_mb_emit_ldloc (mb, p_var);
7746 mono_mb_emit_byte (mb, CEE_RET);
7748 res = mono_mb_create_method (mb, csig, 8);
7750 mono_method_get_header (res)->init_locals = FALSE;
7752 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7753 info->gc_name = "sgen";
7754 info->alloc_type = atype;
7755 mono_marshal_set_wrapper_info (res, info);
7762 mono_gc_get_gc_name (void)
7767 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7768 static MonoMethod *write_barrier_method;
7771 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7777 if (!mono_thread_internal_current ())
7778 /* Happens during thread attach */
7783 ji = mono_jit_info_table_find (domain, ip);
7786 method = ji->method;
7788 if (method == write_barrier_method)
7790 for (i = 0; i < ATYPE_NUM; ++i)
7791 if (method == alloc_method_cache [i])
7797 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7798 * The signature of the called method is:
7799 * object allocate (MonoVTable *vtable)
7802 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7804 #ifdef MANAGED_ALLOCATION
7805 MonoClass *klass = vtable->klass;
7807 #ifdef HAVE_KW_THREAD
7808 int tlab_next_offset = -1;
7809 int tlab_temp_end_offset = -1;
7810 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7811 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7813 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7817 if (!mono_runtime_has_tls_get ())
7819 if (klass->instance_size > tlab_size)
7821 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7825 if (klass->byval_arg.type == MONO_TYPE_STRING)
7827 if (collect_before_allocs)
7830 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7831 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7833 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7840 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7842 #ifdef MANAGED_ALLOCATION
7843 MonoClass *klass = vtable->klass;
7845 #ifdef HAVE_KW_THREAD
7846 int tlab_next_offset = -1;
7847 int tlab_temp_end_offset = -1;
7848 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7849 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7851 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7857 if (!mono_runtime_has_tls_get ())
7859 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7861 if (collect_before_allocs)
7863 g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
7865 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7872 mono_gc_get_managed_allocator_by_type (int atype)
7874 #ifdef MANAGED_ALLOCATION
7877 if (!mono_runtime_has_tls_get ())
7880 mono_loader_lock ();
7881 res = alloc_method_cache [atype];
7883 res = alloc_method_cache [atype] = create_allocator (atype);
7884 mono_loader_unlock ();
7892 mono_gc_get_managed_allocator_types (void)
7899 mono_gc_get_write_barrier (void)
7902 MonoMethodBuilder *mb;
7903 MonoMethodSignature *sig;
7904 #ifdef MANAGED_WBARRIER
7905 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7906 #ifndef SGEN_ALIGN_NURSERY
7907 int label_continue_1, label_continue_2, label_no_wb_5;
7908 int dereferenced_var;
7910 int buffer_var, buffer_index_var, dummy_var;
7912 #ifdef HAVE_KW_THREAD
7913 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7914 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7916 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7917 g_assert (stack_end_offset != -1);
7918 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7919 g_assert (store_remset_buffer_offset != -1);
7920 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7921 g_assert (store_remset_buffer_index_offset != -1);
7922 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7923 g_assert (store_remset_buffer_index_addr_offset != -1);
7927 g_assert (!use_cardtable);
7929 // FIXME: Maybe create a separate version for ctors (the branch would be
7930 // correctly predicted more times)
7931 if (write_barrier_method)
7932 return write_barrier_method;
7934 /* Create the IL version of mono_gc_barrier_generic_store () */
7935 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7936 sig->ret = &mono_defaults.void_class->byval_arg;
7937 sig->params [0] = &mono_defaults.int_class->byval_arg;
7939 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7941 #ifdef MANAGED_WBARRIER
7942 if (mono_runtime_has_tls_get ()) {
7943 #ifdef SGEN_ALIGN_NURSERY
7944 // if (ptr_in_nursery (ptr)) return;
7946 * Masking out the bits might be faster, but we would have to use 64 bit
7947 * immediates, which might be slower.
7949 mono_mb_emit_ldarg (mb, 0);
7950 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7951 mono_mb_emit_byte (mb, CEE_SHR_UN);
7952 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7953 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7955 // if (!ptr_in_nursery (*ptr)) return;
7956 mono_mb_emit_ldarg (mb, 0);
7957 mono_mb_emit_byte (mb, CEE_LDIND_I);
7958 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7959 mono_mb_emit_byte (mb, CEE_SHR_UN);
7960 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7961 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7964 // if (ptr < (nursery_start)) goto continue;
7965 mono_mb_emit_ldarg (mb, 0);
7966 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7967 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7969 // if (ptr >= nursery_real_end)) goto continue;
7970 mono_mb_emit_ldarg (mb, 0);
7971 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7972 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7975 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7978 mono_mb_patch_branch (mb, label_continue_1);
7979 mono_mb_patch_branch (mb, label_continue_2);
7981 // Dereference and store in local var
7982 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7983 mono_mb_emit_ldarg (mb, 0);
7984 mono_mb_emit_byte (mb, CEE_LDIND_I);
7985 mono_mb_emit_stloc (mb, dereferenced_var);
7987 // if (*ptr < nursery_start) return;
7988 mono_mb_emit_ldloc (mb, dereferenced_var);
7989 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7990 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7992 // if (*ptr >= nursery_end) return;
7993 mono_mb_emit_ldloc (mb, dereferenced_var);
7994 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7995 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7998 // if (ptr >= stack_end) goto need_wb;
7999 mono_mb_emit_ldarg (mb, 0);
8000 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
8001 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
8003 // if (ptr >= stack_start) return;
8004 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8005 mono_mb_emit_ldarg (mb, 0);
8006 mono_mb_emit_ldloc_addr (mb, dummy_var);
8007 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
8010 mono_mb_patch_branch (mb, label_need_wb);
8012 // buffer = STORE_REMSET_BUFFER;
8013 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8014 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
8015 mono_mb_emit_stloc (mb, buffer_var);
8017 // buffer_index = STORE_REMSET_BUFFER_INDEX;
8018 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8019 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
8020 mono_mb_emit_stloc (mb, buffer_index_var);
8022 // if (buffer [buffer_index] == ptr) return;
8023 mono_mb_emit_ldloc (mb, buffer_var);
8024 mono_mb_emit_ldloc (mb, buffer_index_var);
8025 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8026 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8027 mono_mb_emit_byte (mb, CEE_SHL);
8028 mono_mb_emit_byte (mb, CEE_ADD);
8029 mono_mb_emit_byte (mb, CEE_LDIND_I);
8030 mono_mb_emit_ldarg (mb, 0);
8031 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
8034 mono_mb_emit_ldloc (mb, buffer_index_var);
8035 mono_mb_emit_icon (mb, 1);
8036 mono_mb_emit_byte (mb, CEE_ADD);
8037 mono_mb_emit_stloc (mb, buffer_index_var);
8039 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
8040 mono_mb_emit_ldloc (mb, buffer_index_var);
8041 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
8042 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
8044 // buffer [buffer_index] = ptr;
8045 mono_mb_emit_ldloc (mb, buffer_var);
8046 mono_mb_emit_ldloc (mb, buffer_index_var);
8047 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8048 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8049 mono_mb_emit_byte (mb, CEE_SHL);
8050 mono_mb_emit_byte (mb, CEE_ADD);
8051 mono_mb_emit_ldarg (mb, 0);
8052 mono_mb_emit_byte (mb, CEE_STIND_I);
8054 // STORE_REMSET_BUFFER_INDEX = buffer_index;
8055 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8056 mono_mb_emit_ldloc (mb, buffer_index_var);
8057 mono_mb_emit_byte (mb, CEE_STIND_I);
8060 mono_mb_patch_branch (mb, label_no_wb_1);
8061 mono_mb_patch_branch (mb, label_no_wb_2);
8062 mono_mb_patch_branch (mb, label_no_wb_3);
8063 mono_mb_patch_branch (mb, label_no_wb_4);
8064 #ifndef SGEN_ALIGN_NURSERY
8065 mono_mb_patch_branch (mb, label_no_wb_5);
8067 mono_mb_emit_byte (mb, CEE_RET);
8070 mono_mb_patch_branch (mb, label_slow_path);
8074 mono_mb_emit_ldarg (mb, 0);
8075 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
8076 mono_mb_emit_byte (mb, CEE_RET);
8078 res = mono_mb_create_method (mb, sig, 16);
8081 mono_loader_lock ();
8082 if (write_barrier_method) {
8083 /* Already created */
8084 mono_free_method (res);
8086 /* double-checked locking */
8087 mono_memory_barrier ();
8088 write_barrier_method = res;
8090 mono_loader_unlock ();
8092 return write_barrier_method;
8096 mono_gc_get_description (void)
8098 return g_strdup ("sgen");
8102 mono_gc_set_desktop_mode (void)
8107 mono_gc_is_moving (void)
8113 mono_gc_is_disabled (void)
8119 mono_sgen_debug_printf (int level, const char *format, ...)
8123 if (level > gc_debug_level)
8126 va_start (ap, format);
8127 vfprintf (gc_debug_file, format, ap);
8132 mono_sgen_get_logfile (void)
8134 return gc_debug_file;
8138 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
8144 #endif /* HAVE_SGEN_GC */