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-archdep.h"
204 #include "metadata/mono-gc.h"
205 #include "metadata/method-builder.h"
206 #include "metadata/profiler-private.h"
207 #include "metadata/monitor.h"
208 #include "metadata/threadpool-internals.h"
209 #include "metadata/mempool-internals.h"
210 #include "metadata/marshal.h"
211 #include "utils/mono-mmap.h"
212 #include "utils/mono-time.h"
213 #include "utils/mono-semaphore.h"
214 #include "utils/mono-counters.h"
215 #include "utils/mono-proclib.h"
217 #include <mono/utils/memcheck.h>
219 #if defined(__MACH__)
220 #include "utils/mach-support.h"
223 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
227 #include "mono/cil/opcode.def"
233 #undef pthread_create
235 #undef pthread_detach
238 * ######################################################################
239 * ######## Types and constants used by the GC.
240 * ######################################################################
243 static int gc_initialized = 0;
244 /* If set, do a minor collection before every allocation */
245 static gboolean collect_before_allocs = FALSE;
246 /* If set, do a heap consistency check before each minor collection */
247 static gboolean consistency_check_at_minor_collection = FALSE;
248 /* If set, check that there are no references to the domain left at domain unload */
249 static gboolean xdomain_checks = FALSE;
250 /* If not null, dump the heap after each collection into this file */
251 static FILE *heap_dump_file = NULL;
252 /* If set, mark stacks conservatively, even if precise marking is possible */
253 static gboolean conservative_stack_mark = TRUE;
254 /* If set, do a plausibility check on the scan_starts before and after
256 static gboolean do_scan_starts_check = FALSE;
258 #ifdef HEAVY_STATISTICS
259 static long long stat_objects_alloced = 0;
260 static long long stat_bytes_alloced = 0;
261 long long stat_objects_alloced_degraded = 0;
262 long long stat_bytes_alloced_degraded = 0;
263 static long long stat_bytes_alloced_los = 0;
265 long long stat_copy_object_called_nursery = 0;
266 long long stat_objects_copied_nursery = 0;
267 long long stat_copy_object_called_major = 0;
268 long long stat_objects_copied_major = 0;
270 long long stat_scan_object_called_nursery = 0;
271 long long stat_scan_object_called_major = 0;
273 long long stat_nursery_copy_object_failed_from_space = 0;
274 long long stat_nursery_copy_object_failed_forwarded = 0;
275 long long stat_nursery_copy_object_failed_pinned = 0;
277 static long long stat_store_remsets = 0;
278 static long long stat_store_remsets_unique = 0;
279 static long long stat_saved_remsets_1 = 0;
280 static long long stat_saved_remsets_2 = 0;
281 static long long stat_local_remsets_processed = 0;
282 static long long stat_global_remsets_added = 0;
283 static long long stat_global_remsets_readded = 0;
284 static long long stat_global_remsets_processed = 0;
285 static long long stat_global_remsets_discarded = 0;
287 static long long stat_wasted_fragments_used = 0;
288 static long long stat_wasted_fragments_bytes = 0;
290 static int stat_wbarrier_set_field = 0;
291 static int stat_wbarrier_set_arrayref = 0;
292 static int stat_wbarrier_arrayref_copy = 0;
293 static int stat_wbarrier_generic_store = 0;
294 static int stat_wbarrier_generic_store_remset = 0;
295 static int stat_wbarrier_set_root = 0;
296 static int stat_wbarrier_value_copy = 0;
297 static int stat_wbarrier_object_copy = 0;
300 static long long time_minor_pre_collection_fragment_clear = 0;
301 static long long time_minor_pinning = 0;
302 static long long time_minor_scan_remsets = 0;
303 static long long time_minor_scan_card_table = 0;
304 static long long time_minor_scan_pinned = 0;
305 static long long time_minor_scan_registered_roots = 0;
306 static long long time_minor_scan_thread_data = 0;
307 static long long time_minor_finish_gray_stack = 0;
308 static long long time_minor_fragment_creation = 0;
310 static long long time_major_pre_collection_fragment_clear = 0;
311 static long long time_major_pinning = 0;
312 static long long time_major_scan_pinned = 0;
313 static long long time_major_scan_registered_roots = 0;
314 static long long time_major_scan_thread_data = 0;
315 static long long time_major_scan_alloc_pinned = 0;
316 static long long time_major_scan_finalized = 0;
317 static long long time_major_scan_big_objects = 0;
318 static long long time_major_finish_gray_stack = 0;
319 static long long time_major_free_bigobjs = 0;
320 static long long time_major_los_sweep = 0;
321 static long long time_major_sweep = 0;
322 static long long time_major_fragment_creation = 0;
324 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
326 static int gc_debug_level = 0;
327 static FILE* gc_debug_file;
331 mono_gc_flush_info (void)
333 fflush (gc_debug_file);
338 * Define this to allow the user to change the nursery size by
339 * specifying its value in the MONO_GC_PARAMS environmental
340 * variable. See mono_gc_base_init for details.
342 #define USER_CONFIG 1
344 #define TV_DECLARE(name) gint64 name
345 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
346 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
347 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
349 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
351 /* The method used to clear the nursery */
352 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
353 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
358 CLEAR_AT_TLAB_CREATION
359 } NurseryClearPolicy;
361 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
364 * The young generation is divided into fragments. This is because
365 * we can hand one fragments to a thread for lock-less fast alloc and
366 * because the young generation ends up fragmented anyway by pinned objects.
367 * Once a collection is done, a list of fragments is created. When doing
368 * thread local alloc we use smallish nurseries so we allow new threads to
369 * allocate memory from gen0 without triggering a collection. Threads that
370 * are found to allocate lots of memory are given bigger fragments. This
371 * should make the finalizer thread use little nursery memory after a while.
372 * We should start assigning threads very small fragments: if there are many
373 * threads the nursery will be full of reserved space that the threads may not
374 * use at all, slowing down allocation speed.
375 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
376 * Allocation Buffers (TLABs).
378 typedef struct _Fragment Fragment;
382 char *fragment_start;
383 char *fragment_limit; /* the current soft limit for allocation */
387 /* the runtime can register areas of memory as roots: we keep two lists of roots,
388 * a pinned root set for conservatively scanned roots and a normal one for
389 * precisely scanned roots (currently implemented as a single list).
391 typedef struct _RootRecord RootRecord;
400 * We're never actually using the first element. It's always set to
401 * NULL to simplify the elimination of consecutive duplicate
404 #define STORE_REMSET_BUFFER_SIZE 1024
406 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
407 struct _GenericStoreRememberedSet {
408 GenericStoreRememberedSet *next;
409 /* We need one entry less because the first entry of store
410 remset buffers is always a dummy and we don't copy it. */
411 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
414 /* we have 4 possible values in the low 2 bits */
416 REMSET_LOCATION, /* just a pointer to the exact location */
417 REMSET_RANGE, /* range of pointer fields */
418 REMSET_OBJECT, /* mark all the object for scanning */
419 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
420 REMSET_TYPE_MASK = 0x3
423 #ifdef HAVE_KW_THREAD
424 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
426 static pthread_key_t remembered_set_key;
427 static RememberedSet *global_remset;
428 static RememberedSet *freed_thread_remsets;
429 static GenericStoreRememberedSet *generic_store_remsets = NULL;
431 /*A two slots cache for recently inserted remsets */
432 static gpointer global_remset_cache [2];
434 /* FIXME: later choose a size that takes into account the RememberedSet struct
435 * and doesn't waste any alloc paddin space.
437 #define DEFAULT_REMSET_SIZE 1024
438 static RememberedSet* alloc_remset (int size, gpointer id);
440 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
441 #define object_is_pinned SGEN_OBJECT_IS_PINNED
442 #define pin_object SGEN_PIN_OBJECT
443 #define unpin_object SGEN_UNPIN_OBJECT
445 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_real_end))
447 #define LOAD_VTABLE SGEN_LOAD_VTABLE
450 safe_name (void* obj)
452 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
453 return vt->klass->name;
456 #define safe_object_get_size mono_sgen_safe_object_get_size
459 * ######################################################################
460 * ######## Global data.
461 * ######################################################################
463 static LOCK_DECLARE (gc_mutex);
464 static int gc_disabled = 0;
465 static int num_minor_gcs = 0;
466 static int num_major_gcs = 0;
468 static gboolean use_cardtable;
472 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
473 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
474 static int default_nursery_size = (1 << 22);
475 #ifdef SGEN_ALIGN_NURSERY
476 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
477 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
478 static int default_nursery_bits = 22;
483 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
484 #ifdef SGEN_ALIGN_NURSERY
485 #define DEFAULT_NURSERY_BITS 22
490 #ifndef SGEN_ALIGN_NURSERY
491 #define DEFAULT_NURSERY_BITS -1
494 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
496 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
498 /* the minimum size of a fragment that we consider useful for allocation */
499 #define FRAGMENT_MIN_SIZE (512)
501 static mword pagesize = 4096;
502 static mword nursery_size;
503 static int degraded_mode = 0;
505 static mword total_alloc = 0;
506 /* use this to tune when to do a major/minor collection */
507 static mword memory_pressure = 0;
508 static mword minor_collection_allowance;
509 static int minor_collection_sections_alloced = 0;
511 static GCMemSection *nursery_section = NULL;
512 static mword lowest_heap_address = ~(mword)0;
513 static mword highest_heap_address = 0;
515 static LOCK_DECLARE (interruption_mutex);
516 static LOCK_DECLARE (global_remset_mutex);
518 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
519 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
521 typedef struct _FinalizeEntry FinalizeEntry;
522 struct _FinalizeEntry {
527 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
528 struct _FinalizeEntryHashTable {
529 FinalizeEntry **table;
534 typedef struct _DisappearingLink DisappearingLink;
535 struct _DisappearingLink {
536 DisappearingLink *next;
540 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
541 struct _DisappearingLinkHashTable {
542 DisappearingLink **table;
547 typedef struct _EphemeronLinkNode EphemeronLinkNode;
549 struct _EphemeronLinkNode {
550 EphemeronLinkNode *next;
565 int current_collection_generation = -1;
568 * The link pointer is hidden by negating each bit. We use the lowest
569 * bit of the link (before negation) to store whether it needs
570 * resurrection tracking.
572 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
573 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
575 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
576 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
579 * The finalizable hash has the object as the key, the
580 * disappearing_link hash, has the link address as key.
582 static FinalizeEntryHashTable minor_finalizable_hash;
583 static FinalizeEntryHashTable major_finalizable_hash;
584 /* objects that are ready to be finalized */
585 static FinalizeEntry *fin_ready_list = NULL;
586 static FinalizeEntry *critical_fin_list = NULL;
588 static DisappearingLinkHashTable minor_disappearing_link_hash;
589 static DisappearingLinkHashTable major_disappearing_link_hash;
591 static EphemeronLinkNode *ephemeron_list;
593 static int num_ready_finalizers = 0;
594 static int no_finalize = 0;
597 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
598 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
599 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
603 /* registered roots: the key to the hash is the root start address */
605 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
607 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
608 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
609 static mword roots_size = 0; /* amount of memory in the root set */
610 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
613 * The current allocation cursors
614 * We allocate objects in the nursery.
615 * The nursery is the area between nursery_start and nursery_real_end.
616 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
617 * from nursery fragments.
618 * tlab_next is the pointer to the space inside the TLAB where the next object will
620 * tlab_temp_end is the pointer to the end of the temporary space reserved for
621 * the allocation: it allows us to set the scan starts at reasonable intervals.
622 * tlab_real_end points to the end of the TLAB.
623 * nursery_frag_real_end points to the end of the currently used nursery fragment.
624 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
625 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
626 * At the next allocation, the area of the nursery where objects can be present is
627 * between MIN(nursery_first_pinned_start, first_fragment_start) and
628 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
630 static char *nursery_start = NULL;
632 #ifdef HAVE_KW_THREAD
633 #define TLAB_ACCESS_INIT
634 #define TLAB_START tlab_start
635 #define TLAB_NEXT tlab_next
636 #define TLAB_TEMP_END tlab_temp_end
637 #define TLAB_REAL_END tlab_real_end
638 #define REMEMBERED_SET remembered_set
639 #define STORE_REMSET_BUFFER store_remset_buffer
640 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
641 #define IN_CRITICAL_REGION thread_info->in_critical_region
643 static pthread_key_t thread_info_key;
644 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
645 #define TLAB_START (__thread_info__->tlab_start)
646 #define TLAB_NEXT (__thread_info__->tlab_next)
647 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
648 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
649 #define REMEMBERED_SET (__thread_info__->remset)
650 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
651 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
652 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
655 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
656 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
657 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
660 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
661 * variables for next+temp_end ?
663 #ifdef HAVE_KW_THREAD
664 static __thread SgenThreadInfo *thread_info;
665 static __thread char *tlab_start;
666 static __thread char *tlab_next;
667 static __thread char *tlab_temp_end;
668 static __thread char *tlab_real_end;
669 static __thread gpointer *store_remset_buffer;
670 static __thread long store_remset_buffer_index;
671 /* Used by the managed allocator/wbarrier */
672 static __thread char **tlab_next_addr;
673 static __thread char *stack_end;
674 static __thread long *store_remset_buffer_index_addr;
676 static char *nursery_next = NULL;
677 static char *nursery_frag_real_end = NULL;
678 static char *nursery_real_end = NULL;
679 static char *nursery_last_pinned_end = NULL;
681 /* The size of a TLAB */
682 /* The bigger the value, the less often we have to go to the slow path to allocate a new
683 * one, but the more space is wasted by threads not allocating much memory.
685 * FIXME: Make this self-tuning for each thread.
687 static guint32 tlab_size = (1024 * 4);
689 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
690 #define MAX_NURSERY_TLAB_WASTE 512
692 /* fragments that are free and ready to be used for allocation */
693 static Fragment *nursery_fragments = NULL;
694 /* freeelist of fragment structures */
695 static Fragment *fragment_freelist = NULL;
697 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
699 /* Functions supplied by the runtime to be called by the GC */
700 static MonoGCCallbacks gc_callbacks;
702 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
703 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
705 #define ALIGN_UP SGEN_ALIGN_UP
707 #define MOVED_OBJECTS_NUM 64
708 static void *moved_objects [MOVED_OBJECTS_NUM];
709 static int moved_objects_idx = 0;
712 * ######################################################################
713 * ######## Macros and function declarations.
714 * ######################################################################
717 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
720 align_pointer (void *ptr)
722 mword p = (mword)ptr;
723 p += sizeof (gpointer) - 1;
724 p &= ~ (sizeof (gpointer) - 1);
728 typedef SgenGrayQueue GrayQueue;
730 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
731 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
733 /* forward declarations */
734 static int stop_world (int generation);
735 static int restart_world (int generation);
736 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
737 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
738 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
739 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
740 static void find_pinning_ref_from_thread (char *obj, size_t size);
741 static void update_current_thread_stack (void *start);
742 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
743 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
744 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
745 static void null_links_for_domain (MonoDomain *domain, int generation);
746 static gboolean search_fragment_for_size (size_t size);
747 static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
748 static void clear_nursery_fragments (char *next);
749 static void pin_from_roots (void *start_nursery, void *end_nursery);
750 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
751 static void optimize_pin_queue (int start_slot);
752 static void clear_remsets (void);
753 static void clear_tlabs (void);
754 static void sort_addresses (void **array, int size);
755 static void drain_gray_stack (GrayQueue *queue);
756 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
757 static gboolean need_major_collection (void);
758 static void major_collection (const char *reason);
760 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
762 void describe_ptr (char *ptr);
763 void check_object (char *start);
765 static void check_consistency (void);
766 static void check_major_refs (void);
767 static void check_scan_starts (void);
768 static void check_for_xdomain_refs (void);
769 static void dump_heap (const char *type, int num, const char *reason);
771 void mono_gc_scan_for_specific_ref (MonoObject *key);
773 static void init_stats (void);
775 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
776 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
777 static void null_ephemerons_for_domain (MonoDomain *domain);
779 SgenMajorCollector major_collector;
781 #include "sgen-protocol.c"
782 #include "sgen-pinning.c"
783 #include "sgen-pinning-stats.c"
784 #include "sgen-gray.c"
785 #include "sgen-workers.c"
786 #include "sgen-los.c"
787 #include "sgen-cardtable.c"
789 /* Root bitmap descriptors are simpler: the lower three bits describe the type
790 * and we either have 30/62 bitmap bits or nibble-based run-length,
791 * or a complex descriptor, or a user defined marker function.
794 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
799 ROOT_DESC_TYPE_MASK = 0x7,
800 ROOT_DESC_TYPE_SHIFT = 3,
803 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
805 #define MAX_USER_DESCRIPTORS 16
807 static gsize* complex_descriptors = NULL;
808 static int complex_descriptors_size = 0;
809 static int complex_descriptors_next = 0;
810 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
811 static int user_descriptors_next = 0;
814 alloc_complex_descriptor (gsize *bitmap, int numbits)
818 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
819 nwords = numbits / GC_BITS_PER_WORD + 1;
822 res = complex_descriptors_next;
823 /* linear search, so we don't have duplicates with domain load/unload
824 * this should not be performance critical or we'd have bigger issues
825 * (the number and size of complex descriptors should be small).
827 for (i = 0; i < complex_descriptors_next; ) {
828 if (complex_descriptors [i] == nwords) {
830 for (j = 0; j < nwords - 1; ++j) {
831 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
841 i += complex_descriptors [i];
843 if (complex_descriptors_next + nwords > complex_descriptors_size) {
844 int new_size = complex_descriptors_size * 2 + nwords;
845 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
846 complex_descriptors_size = new_size;
848 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
849 complex_descriptors_next += nwords;
850 complex_descriptors [res] = nwords;
851 for (i = 0; i < nwords - 1; ++i) {
852 complex_descriptors [res + 1 + i] = bitmap [i];
853 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
860 mono_sgen_get_complex_descriptor (GCVTable *vt)
862 return complex_descriptors + (vt->desc >> LOW_TYPE_BITS);
866 * Descriptor builders.
869 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
871 return (void*) DESC_TYPE_RUN_LENGTH;
875 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
877 int first_set = -1, num_set = 0, last_set = -1, i;
879 size_t stored_size = obj_size;
880 for (i = 0; i < numbits; ++i) {
881 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
889 * We don't encode the size of types that don't contain
890 * references because they might not be aligned, i.e. the
891 * bottom two bits might be set, which would clash with the
892 * bits we need to encode the descriptor type. Since we don't
893 * use the encoded size to skip objects, other than for
894 * processing remsets, in which case only the positions of
895 * references are relevant, this is not a problem.
898 return (void*)DESC_TYPE_RUN_LENGTH;
899 g_assert (!(stored_size & 0x3));
900 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
901 /* check run-length encoding first: one byte offset, one byte number of pointers
902 * on 64 bit archs, we can have 3 runs, just one on 32.
903 * It may be better to use nibbles.
906 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
907 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
909 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
910 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
911 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));
914 /* we know the 2-word header is ptr-free */
915 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
916 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
917 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
921 /* we know the 2-word header is ptr-free */
922 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
923 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
924 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
927 /* it's a complex object ... */
928 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
932 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
934 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
936 int first_set = -1, num_set = 0, last_set = -1, i;
937 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
938 for (i = 0; i < numbits; ++i) {
939 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
946 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
948 return (void*)DESC_TYPE_RUN_LENGTH;
949 if (elem_size <= MAX_ELEMENT_SIZE) {
950 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
952 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
954 /* Note: we also handle structs with just ref fields */
955 if (num_set * sizeof (gpointer) == elem_size) {
956 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
958 /* FIXME: try run-len first */
959 /* Note: we can't skip the object header here, because it's not present */
960 if (last_set <= SMALL_BITMAP_SIZE) {
961 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
964 /* it's am array of complex structs ... */
965 desc = DESC_TYPE_COMPLEX_ARR;
966 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
970 /* Return the bitmap encoded by a descriptor */
972 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
974 mword d = (mword)descr;
978 case DESC_TYPE_RUN_LENGTH: {
979 int first_set = (d >> 16) & 0xff;
980 int num_set = (d >> 24) & 0xff;
983 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
985 for (i = first_set; i < first_set + num_set; ++i)
986 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
988 *numbits = first_set + num_set;
992 case DESC_TYPE_SMALL_BITMAP:
993 bitmap = g_new0 (gsize, 1);
995 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
997 *numbits = GC_BITS_PER_WORD;
1001 g_assert_not_reached ();
1006 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1008 MonoObject *o = (MonoObject*)(obj);
1009 MonoObject *ref = (MonoObject*)*(ptr);
1010 int offset = (char*)(ptr) - (char*)o;
1012 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1014 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1016 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1017 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1019 /* Thread.cached_culture_info */
1020 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1021 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1022 !strcmp(o->vtable->klass->name_space, "System") &&
1023 !strcmp(o->vtable->klass->name, "Object[]"))
1026 * 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
1027 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1028 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1029 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1030 * 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
1031 * 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
1032 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1033 * 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
1034 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1036 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1037 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1038 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1039 !strcmp (o->vtable->klass->name, "MemoryStream"))
1041 /* append_job() in threadpool.c */
1042 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1043 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1044 !strcmp (o->vtable->klass->name_space, "System") &&
1045 !strcmp (o->vtable->klass->name, "Object[]") &&
1046 mono_thread_pool_is_queue_array ((MonoArray*) o))
1052 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1054 MonoObject *o = (MonoObject*)(obj);
1055 MonoObject *ref = (MonoObject*)*(ptr);
1056 int offset = (char*)(ptr) - (char*)o;
1058 MonoClassField *field;
1061 if (!ref || ref->vtable->domain == domain)
1063 if (is_xdomain_ref_allowed (ptr, obj, domain))
1067 for (class = o->vtable->klass; class; class = class->parent) {
1070 for (i = 0; i < class->field.count; ++i) {
1071 if (class->fields[i].offset == offset) {
1072 field = &class->fields[i];
1080 if (ref->vtable->klass == mono_defaults.string_class)
1081 str = mono_string_to_utf8 ((MonoString*)ref);
1084 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1085 o, o->vtable->klass->name_space, o->vtable->klass->name,
1086 offset, field ? field->name : "",
1087 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1088 mono_gc_scan_for_specific_ref (o);
1094 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1097 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1099 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1101 #include "sgen-scan-object.h"
1105 #define HANDLE_PTR(ptr,obj) do { \
1106 if ((MonoObject*)*(ptr) == key) { \
1107 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1108 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1113 scan_object_for_specific_ref (char *start, MonoObject *key)
1115 #include "sgen-scan-object.h"
1119 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data)
1121 while (start < end) {
1123 if (!*(void**)start) {
1124 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1128 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
1130 callback (start, size, data);
1137 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1139 scan_object_for_specific_ref (obj, key);
1143 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1147 g_print ("found ref to %p in root record %p\n", key, root);
1150 static MonoObject *check_key = NULL;
1151 static RootRecord *check_root = NULL;
1154 check_root_obj_specific_ref_from_marker (void **obj)
1156 check_root_obj_specific_ref (check_root, check_key, *obj);
1160 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1165 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1166 for (root = roots_hash [root_type][i]; root; root = root->next) {
1167 void **start_root = (void**)root->start_root;
1168 mword desc = root->root_desc;
1172 switch (desc & ROOT_DESC_TYPE_MASK) {
1173 case ROOT_DESC_BITMAP:
1174 desc >>= ROOT_DESC_TYPE_SHIFT;
1177 check_root_obj_specific_ref (root, key, *start_root);
1182 case ROOT_DESC_COMPLEX: {
1183 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1184 int bwords = (*bitmap_data) - 1;
1185 void **start_run = start_root;
1187 while (bwords-- > 0) {
1188 gsize bmap = *bitmap_data++;
1189 void **objptr = start_run;
1192 check_root_obj_specific_ref (root, key, *objptr);
1196 start_run += GC_BITS_PER_WORD;
1200 case ROOT_DESC_USER: {
1201 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1202 marker (start_root, check_root_obj_specific_ref_from_marker);
1205 case ROOT_DESC_RUN_LEN:
1206 g_assert_not_reached ();
1208 g_assert_not_reached ();
1217 mono_gc_scan_for_specific_ref (MonoObject *key)
1223 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1224 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1226 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1228 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1229 scan_object_for_specific_ref (bigobj->data, key);
1231 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1232 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1234 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1235 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1236 void **ptr = (void**)root->start_root;
1238 while (ptr < (void**)root->end_root) {
1239 check_root_obj_specific_ref (root, *ptr, key);
1246 /* Clear all remaining nursery fragments */
1248 clear_nursery_fragments (char *next)
1251 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1252 g_assert (next <= nursery_frag_real_end);
1253 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", next, nursery_frag_real_end));
1254 memset (next, 0, nursery_frag_real_end - next);
1255 for (frag = nursery_fragments; frag; frag = frag->next) {
1256 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_start, frag->fragment_end));
1257 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1263 need_remove_object_for_domain (char *start, MonoDomain *domain)
1265 if (mono_object_domain (start) == domain) {
1266 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1267 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1274 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1276 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1277 if (vt->klass == mono_defaults.internal_thread_class)
1278 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1279 /* The object could be a proxy for an object in the domain
1281 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1282 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1284 /* The server could already have been zeroed out, so
1285 we need to check for that, too. */
1286 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1287 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1289 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1294 static MonoDomain *check_domain = NULL;
1297 check_obj_not_in_domain (void **o)
1299 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1303 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1307 check_domain = domain;
1308 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1309 for (root = roots_hash [root_type][i]; root; root = root->next) {
1310 void **start_root = (void**)root->start_root;
1311 mword desc = root->root_desc;
1313 /* The MonoDomain struct is allowed to hold
1314 references to objects in its own domain. */
1315 if (start_root == (void**)domain)
1318 switch (desc & ROOT_DESC_TYPE_MASK) {
1319 case ROOT_DESC_BITMAP:
1320 desc >>= ROOT_DESC_TYPE_SHIFT;
1322 if ((desc & 1) && *start_root)
1323 check_obj_not_in_domain (*start_root);
1328 case ROOT_DESC_COMPLEX: {
1329 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1330 int bwords = (*bitmap_data) - 1;
1331 void **start_run = start_root;
1333 while (bwords-- > 0) {
1334 gsize bmap = *bitmap_data++;
1335 void **objptr = start_run;
1337 if ((bmap & 1) && *objptr)
1338 check_obj_not_in_domain (*objptr);
1342 start_run += GC_BITS_PER_WORD;
1346 case ROOT_DESC_USER: {
1347 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1348 marker (start_root, check_obj_not_in_domain);
1351 case ROOT_DESC_RUN_LEN:
1352 g_assert_not_reached ();
1354 g_assert_not_reached ();
1358 check_domain = NULL;
1362 check_for_xdomain_refs (void)
1366 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1367 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1369 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1371 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1372 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1376 clear_domain_process_object (char *obj, MonoDomain *domain)
1380 process_object_for_domain_clearing (obj, domain);
1381 remove = need_remove_object_for_domain (obj, domain);
1383 if (remove && ((MonoObject*)obj)->synchronisation) {
1384 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1386 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1393 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1395 if (clear_domain_process_object (obj, domain))
1396 memset (obj, 0, size);
1400 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1402 clear_domain_process_object (obj, domain);
1406 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1408 if (need_remove_object_for_domain (obj, domain))
1409 major_collector.free_non_pinned_object (obj, size);
1413 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1415 if (need_remove_object_for_domain (obj, domain))
1416 major_collector.free_pinned_object (obj, size);
1420 * When appdomains are unloaded we can easily remove objects that have finalizers,
1421 * but all the others could still be present in random places on the heap.
1422 * We need a sweep to get rid of them even though it's going to be costly
1424 * The reason we need to remove them is because we access the vtable and class
1425 * structures to know the object size and the reference bitmap: once the domain is
1426 * unloaded the point to random memory.
1429 mono_gc_clear_domain (MonoDomain * domain)
1431 LOSObject *bigobj, *prev;
1436 clear_nursery_fragments (nursery_next);
1438 if (xdomain_checks && domain != mono_get_root_domain ()) {
1439 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1440 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1441 check_for_xdomain_refs ();
1444 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1445 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain);
1447 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1448 to memory returned to the OS.*/
1449 null_ephemerons_for_domain (domain);
1451 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1452 null_links_for_domain (domain, i);
1454 /* We need two passes over major and large objects because
1455 freeing such objects might give their memory back to the OS
1456 (in the case of large objects) or obliterate its vtable
1457 (pinned objects with major-copying or pinned and non-pinned
1458 objects with major-mark&sweep), but we might need to
1459 dereference a pointer from an object to another object if
1460 the first object is a proxy. */
1461 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1462 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1463 clear_domain_process_object (bigobj->data, domain);
1466 for (bigobj = los_object_list; bigobj;) {
1467 if (need_remove_object_for_domain (bigobj->data, domain)) {
1468 LOSObject *to_free = bigobj;
1470 prev->next = bigobj->next;
1472 los_object_list = bigobj->next;
1473 bigobj = bigobj->next;
1474 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1476 free_large_object (to_free);
1480 bigobj = bigobj->next;
1482 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1483 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1489 global_remset_cache_clear (void)
1491 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1495 * Tries to check if a given remset location was already added to the global remset.
1498 * A 2 entry, LRU cache of recently saw location remsets.
1500 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1502 * Returns TRUE is the element was added..
1505 global_remset_location_was_not_added (gpointer ptr)
1508 gpointer first = global_remset_cache [0], second;
1510 HEAVY_STAT (++stat_global_remsets_discarded);
1514 second = global_remset_cache [1];
1516 if (second == ptr) {
1517 /*Move the second to the front*/
1518 global_remset_cache [0] = second;
1519 global_remset_cache [1] = first;
1521 HEAVY_STAT (++stat_global_remsets_discarded);
1525 global_remset_cache [0] = second;
1526 global_remset_cache [1] = ptr;
1531 * mono_sgen_add_to_global_remset:
1533 * The global remset contains locations which point into newspace after
1534 * a minor collection. This can happen if the objects they point to are pinned.
1536 * LOCKING: If called from a parallel collector, the global remset
1537 * lock must be held. For serial collectors that is not necessary.
1540 mono_sgen_add_to_global_remset (gpointer ptr)
1545 if (use_cardtable) {
1546 sgen_card_table_mark_address ((mword)ptr);
1550 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1552 lock = (current_collection_generation == GENERATION_OLD && major_collector.is_parallel);
1556 if (!global_remset_location_was_not_added (ptr))
1559 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1560 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1562 HEAVY_STAT (++stat_global_remsets_added);
1565 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1566 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1568 if (global_remset->store_next + 3 < global_remset->end_set) {
1569 *(global_remset->store_next++) = (mword)ptr;
1572 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1573 rs->next = global_remset;
1575 *(global_remset->store_next++) = (mword)ptr;
1578 int global_rs_size = 0;
1580 for (rs = global_remset; rs; rs = rs->next) {
1581 global_rs_size += rs->store_next - rs->data;
1583 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1588 UNLOCK_GLOBAL_REMSET;
1594 * Scan objects in the gray stack until the stack is empty. This should be called
1595 * frequently after each object is copied, to achieve better locality and cache
1599 drain_gray_stack (GrayQueue *queue)
1603 if (current_collection_generation == GENERATION_NURSERY) {
1605 GRAY_OBJECT_DEQUEUE (queue, obj);
1608 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1609 major_collector.minor_scan_object (obj, queue);
1612 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1616 GRAY_OBJECT_DEQUEUE (queue, obj);
1619 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1620 major_collector.major_scan_object (obj, queue);
1626 * Addresses from start to end are already sorted. This function finds
1627 * the object header for each address and pins the object. The
1628 * addresses must be inside the passed section. The (start of the)
1629 * address array is overwritten with the addresses of the actually
1630 * pinned objects. Return the number of pinned objects.
1633 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1638 void *last_obj = NULL;
1639 size_t last_obj_size = 0;
1642 void **definitely_pinned = start;
1643 while (start < end) {
1645 /* the range check should be reduntant */
1646 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1647 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1648 /* multiple pointers to the same object */
1649 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1653 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1654 g_assert (idx < section->num_scan_start);
1655 search_start = (void*)section->scan_starts [idx];
1656 if (!search_start || search_start > addr) {
1659 search_start = section->scan_starts [idx];
1660 if (search_start && search_start <= addr)
1663 if (!search_start || search_start > addr)
1664 search_start = start_nursery;
1666 if (search_start < last_obj)
1667 search_start = (char*)last_obj + last_obj_size;
1668 /* now addr should be in an object a short distance from search_start
1669 * Note that search_start must point to zeroed mem or point to an object.
1672 if (!*(void**)search_start) {
1673 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1676 last_obj = search_start;
1677 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1678 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1679 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1680 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));
1681 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1682 pin_object (search_start);
1683 GRAY_OBJECT_ENQUEUE (queue, search_start);
1685 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1686 definitely_pinned [count] = search_start;
1690 /* skip to the next object */
1691 search_start = (void*)((char*)search_start + last_obj_size);
1692 } while (search_start <= addr);
1693 /* we either pinned the correct object or we ignored the addr because
1694 * it points to unused zeroed memory.
1700 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1705 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1707 int num_entries = section->pin_queue_num_entries;
1709 void **start = section->pin_queue_start;
1711 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1712 section->data, section->next_data, queue);
1713 section->pin_queue_num_entries = reduced_to;
1715 section->pin_queue_start = NULL;
1719 /* Sort the addresses in array in increasing order.
1720 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1723 sort_addresses (void **array, int size)
1728 for (i = 1; i < size; ++i) {
1731 int parent = (child - 1) / 2;
1733 if (array [parent] >= array [child])
1736 tmp = array [parent];
1737 array [parent] = array [child];
1738 array [child] = tmp;
1744 for (i = size - 1; i > 0; --i) {
1747 array [i] = array [0];
1753 while (root * 2 + 1 <= end) {
1754 int child = root * 2 + 1;
1756 if (child < end && array [child] < array [child + 1])
1758 if (array [root] >= array [child])
1762 array [root] = array [child];
1763 array [child] = tmp;
1770 static G_GNUC_UNUSED void
1771 print_nursery_gaps (void* start_nursery, void *end_nursery)
1774 gpointer first = start_nursery;
1776 for (i = 0; i < next_pin_slot; ++i) {
1777 next = pin_queue [i];
1778 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1782 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1785 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1787 optimize_pin_queue (int start_slot)
1789 void **start, **cur, **end;
1790 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1791 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1792 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1793 if ((next_pin_slot - start_slot) > 1)
1794 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1795 start = cur = pin_queue + start_slot;
1796 end = pin_queue + next_pin_slot;
1799 while (*start == *cur && cur < end)
1803 next_pin_slot = start - pin_queue;
1804 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1805 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1810 * Scan the memory between start and end and queue values which could be pointers
1811 * to the area between start_nursery and end_nursery for later consideration.
1812 * Typically used for thread stacks.
1815 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1818 while (start < end) {
1819 if (*start >= start_nursery && *start < end_nursery) {
1821 * *start can point to the middle of an object
1822 * note: should we handle pointing at the end of an object?
1823 * pinning in C# code disallows pointing at the end of an object
1824 * but there is some small chance that an optimizing C compiler
1825 * may keep the only reference to an object by pointing
1826 * at the end of it. We ignore this small chance for now.
1827 * Pointers to the end of an object are indistinguishable
1828 * from pointers to the start of the next object in memory
1829 * so if we allow that we'd need to pin two objects...
1830 * We queue the pointer in an array, the
1831 * array will then be sorted and uniqued. This way
1832 * we can coalesce several pinning pointers and it should
1833 * be faster since we'd do a memory scan with increasing
1834 * addresses. Note: we can align the address to the allocation
1835 * alignment, so the unique process is more effective.
1837 mword addr = (mword)*start;
1838 addr &= ~(ALLOC_ALIGN - 1);
1839 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
1840 pin_stage_ptr ((void*)addr);
1842 pin_stats_register_address ((char*)addr, pin_type);
1843 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
1848 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1852 * Debugging function: find in the conservative roots where @obj is being pinned.
1854 static G_GNUC_UNUSED void
1855 find_pinning_reference (char *obj, size_t size)
1859 char *endobj = obj + size;
1860 for (i = 0; i < roots_hash_size [0]; ++i) {
1861 for (root = roots_hash [0][i]; root; root = root->next) {
1862 /* if desc is non-null it has precise info */
1863 if (!root->root_desc) {
1864 char ** start = (char**)root->start_root;
1865 while (start < (char**)root->end_root) {
1866 if (*start >= obj && *start < endobj) {
1867 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));
1874 find_pinning_ref_from_thread (obj, size);
1878 * The first thing we do in a collection is to identify pinned objects.
1879 * This function considers all the areas of memory that need to be
1880 * conservatively scanned.
1883 pin_from_roots (void *start_nursery, void *end_nursery)
1887 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]));
1888 /* objects pinned from the API are inside these roots */
1889 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1890 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1891 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
1892 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
1895 /* now deal with the thread stacks
1896 * in the future we should be able to conservatively scan only:
1897 * *) the cpu registers
1898 * *) the unmanaged stack frames
1899 * *) the _last_ managed stack frame
1900 * *) pointers slots in managed frames
1902 scan_thread_data (start_nursery, end_nursery, FALSE);
1904 evacuate_pin_staging_area ();
1907 static CopyOrMarkObjectFunc user_copy_or_mark_func;
1908 static GrayQueue *user_copy_or_mark_queue;
1911 single_arg_user_copy_or_mark (void **obj)
1913 user_copy_or_mark_func (obj, user_copy_or_mark_queue);
1917 * The memory area from start_root to end_root contains pointers to objects.
1918 * Their position is precisely described by @desc (this means that the pointer
1919 * can be either NULL or the pointer to the start of an object).
1920 * This functions copies them to to_space updates them.
1922 * This function is not thread-safe!
1925 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
1927 switch (desc & ROOT_DESC_TYPE_MASK) {
1928 case ROOT_DESC_BITMAP:
1929 desc >>= ROOT_DESC_TYPE_SHIFT;
1931 if ((desc & 1) && *start_root) {
1932 copy_func (start_root, queue);
1933 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
1934 drain_gray_stack (queue);
1940 case ROOT_DESC_COMPLEX: {
1941 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1942 int bwords = (*bitmap_data) - 1;
1943 void **start_run = start_root;
1945 while (bwords-- > 0) {
1946 gsize bmap = *bitmap_data++;
1947 void **objptr = start_run;
1949 if ((bmap & 1) && *objptr) {
1950 copy_func (objptr, queue);
1951 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
1952 drain_gray_stack (queue);
1957 start_run += GC_BITS_PER_WORD;
1961 case ROOT_DESC_USER: {
1962 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1963 user_copy_or_mark_func = copy_func;
1964 user_copy_or_mark_queue = queue;
1965 marker (start_root, single_arg_user_copy_or_mark);
1966 user_copy_or_mark_func = NULL;
1967 user_copy_or_mark_queue = NULL;
1970 case ROOT_DESC_RUN_LEN:
1971 g_assert_not_reached ();
1973 g_assert_not_reached ();
1978 mono_sgen_update_heap_boundaries (mword low, mword high)
1983 old = lowest_heap_address;
1986 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
1989 old = highest_heap_address;
1992 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
1996 alloc_fragment (void)
1998 Fragment *frag = fragment_freelist;
2000 fragment_freelist = frag->next;
2004 frag = mono_sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
2009 /* size must be a power of 2 */
2011 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2013 /* Allocate twice the memory to be able to put the block on an aligned address */
2014 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2019 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2020 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2023 mono_sgen_free_os_memory (mem, aligned - mem);
2024 if (aligned + size < mem + size + alignment)
2025 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2031 * Allocate and setup the data structures needed to be able to allocate objects
2032 * in the nursery. The nursery is stored in nursery_section.
2035 alloc_nursery (void)
2037 GCMemSection *section;
2043 if (nursery_section)
2045 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2046 /* later we will alloc a larger area for the nursery but only activate
2047 * what we need. The rest will be used as expansion if we have too many pinned
2048 * objects in the existing nursery.
2050 /* FIXME: handle OOM */
2051 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2053 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2054 alloc_size = nursery_size;
2055 #ifdef SGEN_ALIGN_NURSERY
2056 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2058 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2060 nursery_start = data;
2061 nursery_real_end = nursery_start + nursery_size;
2062 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
2063 nursery_next = nursery_start;
2064 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));
2065 section->data = section->next_data = data;
2066 section->size = alloc_size;
2067 section->end_data = nursery_real_end;
2068 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2069 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2070 section->num_scan_start = scan_starts;
2071 section->block.role = MEMORY_ROLE_GEN0;
2072 section->block.next = NULL;
2074 nursery_section = section;
2076 /* Setup the single first large fragment */
2077 frag = alloc_fragment ();
2078 frag->fragment_start = nursery_start;
2079 frag->fragment_limit = nursery_start;
2080 frag->fragment_end = nursery_real_end;
2081 nursery_frag_real_end = nursery_real_end;
2082 /* FIXME: frag here is lost */
2086 mono_gc_get_nursery (int *shift_bits, size_t *size)
2088 *size = nursery_size;
2089 #ifdef SGEN_ALIGN_NURSERY
2090 *shift_bits = DEFAULT_NURSERY_BITS;
2094 return nursery_start;
2098 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2102 for (fin = list; fin; fin = fin->next) {
2105 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2106 copy_func (&fin->object, queue);
2110 static mword fragment_total = 0;
2112 * We found a fragment of free memory in the nursery: memzero it and if
2113 * it is big enough, add it to the list of fragments that can be used for
2117 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2120 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2121 binary_protocol_empty (frag_start, frag_size);
2122 /* Not worth dealing with smaller fragments: need to tune */
2123 if (frag_size >= FRAGMENT_MIN_SIZE) {
2124 /* memsetting just the first chunk start is bound to provide better cache locality */
2125 if (nursery_clear_policy == CLEAR_AT_GC)
2126 memset (frag_start, 0, frag_size);
2128 fragment = alloc_fragment ();
2129 fragment->fragment_start = frag_start;
2130 fragment->fragment_limit = frag_start;
2131 fragment->fragment_end = frag_end;
2132 fragment->next = nursery_fragments;
2133 nursery_fragments = fragment;
2134 fragment_total += frag_size;
2136 /* Clear unused fragments, pinning depends on this */
2137 /*TODO place an int[] here instead of the memset if size justify it*/
2138 memset (frag_start, 0, frag_size);
2143 generation_name (int generation)
2145 switch (generation) {
2146 case GENERATION_NURSERY: return "nursery";
2147 case GENERATION_OLD: return "old";
2148 default: g_assert_not_reached ();
2152 static DisappearingLinkHashTable*
2153 get_dislink_hash_table (int generation)
2155 switch (generation) {
2156 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2157 case GENERATION_OLD: return &major_disappearing_link_hash;
2158 default: g_assert_not_reached ();
2162 static FinalizeEntryHashTable*
2163 get_finalize_entry_hash_table (int generation)
2165 switch (generation) {
2166 case GENERATION_NURSERY: return &minor_finalizable_hash;
2167 case GENERATION_OLD: return &major_finalizable_hash;
2168 default: g_assert_not_reached ();
2173 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2178 int ephemeron_rounds = 0;
2179 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2182 * We copied all the reachable objects. Now it's the time to copy
2183 * the objects that were not referenced by the roots, but by the copied objects.
2184 * we built a stack of objects pointed to by gray_start: they are
2185 * additional roots and we may add more items as we go.
2186 * We loop until gray_start == gray_objects which means no more objects have
2187 * been added. Note this is iterative: no recursion is involved.
2188 * We need to walk the LO list as well in search of marked big objects
2189 * (use a flag since this is needed only on major collections). We need to loop
2190 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2191 * To achieve better cache locality and cache usage, we drain the gray stack
2192 * frequently, after each object is copied, and just finish the work here.
2194 drain_gray_stack (queue);
2196 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2197 /* walk the finalization queue and move also the objects that need to be
2198 * finalized: use the finalized objects as new roots so the objects they depend
2199 * on are also not reclaimed. As with the roots above, only objects in the nursery
2200 * are marked/copied.
2201 * We need a loop here, since objects ready for finalizers may reference other objects
2202 * that are fin-ready. Speedup with a flag?
2206 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2207 * before processing finalizable objects to avoid finalizing reachable values.
2209 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2210 * while they are been finalized.
2212 int done_with_ephemerons = 0;
2214 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2215 drain_gray_stack (queue);
2217 } while (!done_with_ephemerons);
2219 fin_ready = num_ready_finalizers;
2220 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2221 if (generation == GENERATION_OLD)
2222 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
2224 /* drain the new stack that might have been created */
2225 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2226 drain_gray_stack (queue);
2227 } while (fin_ready != num_ready_finalizers);
2230 * Clear ephemeron pairs with unreachable keys.
2231 * We pass the copy func so we can figure out if an array was promoted or not.
2233 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2236 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));
2239 * handle disappearing links
2240 * Note we do this after checking the finalization queue because if an object
2241 * survives (at least long enough to be finalized) we don't clear the link.
2242 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2243 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2246 g_assert (gray_object_queue_is_empty (queue));
2248 null_link_in_range (copy_func, start_addr, end_addr, generation, queue);
2249 if (generation == GENERATION_OLD)
2250 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, queue);
2251 if (gray_object_queue_is_empty (queue))
2253 drain_gray_stack (queue);
2256 g_assert (gray_object_queue_is_empty (queue));
2260 mono_sgen_check_section_scan_starts (GCMemSection *section)
2263 for (i = 0; i < section->num_scan_start; ++i) {
2264 if (section->scan_starts [i]) {
2265 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2266 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2272 check_scan_starts (void)
2274 if (!do_scan_starts_check)
2276 mono_sgen_check_section_scan_starts (nursery_section);
2277 major_collector.check_scan_starts ();
2280 static int last_num_pinned = 0;
2283 build_nursery_fragments (void **start, int num_entries)
2285 char *frag_start, *frag_end;
2289 while (nursery_fragments) {
2290 Fragment *next = nursery_fragments->next;
2291 nursery_fragments->next = fragment_freelist;
2292 fragment_freelist = nursery_fragments;
2293 nursery_fragments = next;
2295 frag_start = nursery_start;
2297 /* clear scan starts */
2298 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2299 for (i = 0; i < num_entries; ++i) {
2300 frag_end = start [i];
2301 /* remove the pin bit from pinned objects */
2302 unpin_object (frag_end);
2303 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2304 frag_size = frag_end - frag_start;
2306 add_nursery_frag (frag_size, frag_start, frag_end);
2307 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)start [i]));
2308 frag_start = (char*)start [i] + frag_size;
2310 nursery_last_pinned_end = frag_start;
2311 frag_end = nursery_real_end;
2312 frag_size = frag_end - frag_start;
2314 add_nursery_frag (frag_size, frag_start, frag_end);
2315 if (!nursery_fragments) {
2316 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
2317 for (i = 0; i < num_entries; ++i) {
2318 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])));
2323 nursery_next = nursery_frag_real_end = NULL;
2325 /* Clear TLABs for all threads */
2330 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2334 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2335 for (root = roots_hash [root_type][i]; root; root = root->next) {
2336 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2337 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2343 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2345 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2349 mono_sgen_dump_section (GCMemSection *section, const char *type)
2351 char *start = section->data;
2352 char *end = section->data + section->size;
2353 char *occ_start = NULL;
2355 char *old_start = NULL; /* just for debugging */
2357 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2359 while (start < end) {
2363 if (!*(void**)start) {
2365 mono_sgen_dump_occupied (occ_start, start, section->data);
2368 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2371 g_assert (start < section->next_data);
2376 vt = (GCVTable*)LOAD_VTABLE (start);
2379 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2382 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2383 start - section->data,
2384 vt->klass->name_space, vt->klass->name,
2392 mono_sgen_dump_occupied (occ_start, start, section->data);
2394 fprintf (heap_dump_file, "</section>\n");
2398 dump_object (MonoObject *obj, gboolean dump_location)
2400 static char class_name [1024];
2402 MonoClass *class = mono_object_class (obj);
2406 * Python's XML parser is too stupid to parse angle brackets
2407 * in strings, so we just ignore them;
2410 while (class->name [i] && j < sizeof (class_name) - 1) {
2411 if (!strchr ("<>\"", class->name [i]))
2412 class_name [j++] = class->name [i];
2415 g_assert (j < sizeof (class_name));
2418 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2419 class->name_space, class_name,
2420 safe_object_get_size (obj));
2421 if (dump_location) {
2422 const char *location;
2423 if (ptr_in_nursery (obj))
2424 location = "nursery";
2425 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2429 fprintf (heap_dump_file, " location=\"%s\"", location);
2431 fprintf (heap_dump_file, "/>\n");
2435 dump_heap (const char *type, int num, const char *reason)
2440 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2442 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2443 fprintf (heap_dump_file, ">\n");
2444 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2445 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2446 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2447 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2448 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2450 fprintf (heap_dump_file, "<pinned-objects>\n");
2451 for (list = pinned_objects; list; list = list->next)
2452 dump_object (list->obj, TRUE);
2453 fprintf (heap_dump_file, "</pinned-objects>\n");
2455 mono_sgen_dump_section (nursery_section, "nursery");
2457 major_collector.dump_heap (heap_dump_file);
2459 fprintf (heap_dump_file, "<los>\n");
2460 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2461 dump_object ((MonoObject*)bigobj->data, FALSE);
2462 fprintf (heap_dump_file, "</los>\n");
2464 fprintf (heap_dump_file, "</collection>\n");
2468 mono_sgen_register_moved_object (void *obj, void *destination)
2470 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2472 /* FIXME: handle this for parallel collector */
2473 g_assert (!major_collector.is_parallel);
2475 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2476 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2477 moved_objects_idx = 0;
2479 moved_objects [moved_objects_idx++] = obj;
2480 moved_objects [moved_objects_idx++] = destination;
2486 static gboolean inited = FALSE;
2491 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2492 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2493 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2494 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2495 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2496 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2497 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2498 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2499 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2501 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2502 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2503 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2504 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2505 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2506 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2507 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2508 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2509 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2510 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2511 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2512 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2513 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2516 #ifdef HEAVY_STATISTICS
2517 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2518 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2519 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2520 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2521 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2522 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2523 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2524 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2526 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2527 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2528 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2529 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2530 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2532 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2533 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2534 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2535 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2537 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2538 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2540 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2541 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2542 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2544 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
2545 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
2547 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2548 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2549 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2550 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2551 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2552 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2553 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2554 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2555 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2562 need_major_collection (void)
2564 mword los_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
2565 return minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
2569 * Collect objects in the nursery. Returns whether to trigger a major
2573 collect_nursery (size_t requested_size)
2575 size_t max_garbage_amount;
2576 char *orig_nursery_next;
2577 TV_DECLARE (all_atv);
2578 TV_DECLARE (all_btv);
2582 mono_perfcounters->gc_collections0++;
2584 current_collection_generation = GENERATION_NURSERY;
2586 binary_protocol_collection (GENERATION_NURSERY);
2587 check_scan_starts ();
2590 orig_nursery_next = nursery_next;
2591 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
2592 /* FIXME: optimize later to use the higher address where an object can be present */
2593 nursery_next = MAX (nursery_next, nursery_real_end);
2595 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)));
2596 max_garbage_amount = nursery_next - nursery_start;
2597 g_assert (nursery_section->size >= max_garbage_amount);
2599 /* world must be stopped already */
2600 TV_GETTIME (all_atv);
2603 /* Pinning depends on this */
2604 clear_nursery_fragments (orig_nursery_next);
2607 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2610 check_for_xdomain_refs ();
2612 nursery_section->next_data = nursery_next;
2614 major_collector.start_nursery_collection ();
2616 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
2619 mono_stats.minor_gc_count ++;
2621 global_remset_cache_clear ();
2623 /* pin from pinned handles */
2625 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2626 pin_from_roots (nursery_start, nursery_next);
2627 /* identify pinned objects */
2628 optimize_pin_queue (0);
2629 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, &gray_queue);
2630 nursery_section->pin_queue_start = pin_queue;
2631 nursery_section->pin_queue_num_entries = next_pin_slot;
2633 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
2634 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
2635 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2637 if (consistency_check_at_minor_collection)
2638 check_consistency ();
2641 * walk all the roots and copy the young objects to the old generation,
2642 * starting from to_space
2645 scan_from_remsets (nursery_start, nursery_next, &gray_queue);
2646 /* we don't have complete write barrier yet, so we scan all the old generation sections */
2648 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
2649 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2651 if (use_cardtable) {
2653 card_tables_collect_stats (TRUE);
2654 scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
2656 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
2659 drain_gray_stack (&gray_queue);
2662 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
2663 /* registered roots, this includes static fields */
2664 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
2665 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
2667 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2669 scan_thread_data (nursery_start, nursery_next, TRUE);
2671 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2674 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
2676 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
2677 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
2679 /* walk the pin_queue, build up the fragment list of free memory, unmark
2680 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2683 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
2684 build_nursery_fragments (pin_queue, next_pin_slot);
2685 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
2687 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
2688 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
2690 if (consistency_check_at_minor_collection)
2691 check_major_refs ();
2693 major_collector.finish_nursery_collection ();
2695 TV_GETTIME (all_btv);
2696 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2699 dump_heap ("minor", num_minor_gcs - 1, NULL);
2701 /* prepare the pin queue for the next collection */
2702 last_num_pinned = next_pin_slot;
2704 if (fin_ready_list || critical_fin_list) {
2705 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2706 mono_gc_finalize_notify ();
2710 g_assert (gray_object_queue_is_empty (&gray_queue));
2713 card_tables_collect_stats (FALSE);
2715 check_scan_starts ();
2717 binary_protocol_flush_buffers (FALSE);
2719 current_collection_generation = -1;
2721 return need_major_collection ();
2725 major_do_collection (const char *reason)
2727 LOSObject *bigobj, *prevbo;
2728 TV_DECLARE (all_atv);
2729 TV_DECLARE (all_btv);
2732 /* FIXME: only use these values for the precise scan
2733 * note that to_space pointers should be excluded anyway...
2735 char *heap_start = NULL;
2736 char *heap_end = (char*)-1;
2737 int old_num_major_sections = major_collector.get_num_major_sections ();
2738 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2739 mword los_memory_saved, los_memory_alloced, old_los_memory_usage;
2741 mono_perfcounters->gc_collections1++;
2744 * A domain could have been freed, resulting in
2745 * los_memory_usage being less than last_los_memory_usage.
2747 los_memory_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
2748 old_los_memory_usage = los_memory_usage;
2750 //count_ref_nonref_objs ();
2751 //consistency_check ();
2753 binary_protocol_collection (GENERATION_OLD);
2754 check_scan_starts ();
2755 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
2756 if (major_collector.is_parallel)
2757 gray_object_queue_init (&workers_distribute_gray_queue, mono_sgen_get_unmanaged_allocator ());
2760 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
2762 mono_stats.major_gc_count ++;
2764 /* world must be stopped already */
2765 TV_GETTIME (all_atv);
2768 /* Pinning depends on this */
2769 clear_nursery_fragments (nursery_next);
2772 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2775 check_for_xdomain_refs ();
2777 nursery_section->next_data = nursery_real_end;
2778 /* we should also coalesce scanning from sections close to each other
2779 * and deal with pointers outside of the sections later.
2781 /* The remsets are not useful for a major collection */
2783 global_remset_cache_clear ();
2785 card_table_clear ();
2789 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
2790 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
2791 optimize_pin_queue (0);
2794 * pin_queue now contains all candidate pointers, sorted and
2795 * uniqued. We must do two passes now to figure out which
2796 * objects are pinned.
2798 * The first is to find within the pin_queue the area for each
2799 * section. This requires that the pin_queue be sorted. We
2800 * also process the LOS objects and pinned chunks here.
2802 * The second, destructive, pass is to reduce the section
2803 * areas to pointers to the actually pinned objects.
2805 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
2806 /* first pass for the sections */
2807 mono_sgen_find_section_pin_queue_start_end (nursery_section);
2808 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2809 /* identify possible pointers to the insize of large objects */
2810 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
2811 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2813 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
2814 pin_object (bigobj->data);
2815 /* FIXME: only enqueue if object has references */
2816 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
2818 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
2819 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));
2822 /* second pass for the sections */
2823 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2824 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2827 time_major_pinning += TV_ELAPSED_MS (atv, btv);
2828 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2829 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2831 major_collector.init_to_space ();
2833 workers_start_all_workers (1);
2836 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
2838 /* registered roots, this includes static fields */
2839 scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2840 scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2842 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2845 /* FIXME: This is the wrong place for this, because it does
2847 scan_thread_data (heap_start, heap_end, TRUE);
2849 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2852 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
2854 /* scan the list of objects ready for finalization */
2855 scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2856 scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2858 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
2859 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2862 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
2864 if (major_collector.is_parallel) {
2865 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
2866 workers_distribute_gray_queue_sections ();
2870 workers_change_num_working (-1);
2873 if (major_collector.is_parallel)
2874 g_assert (gray_object_queue_is_empty (&gray_queue));
2876 /* all the objects in the heap */
2877 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
2879 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
2881 /* sweep the big objects list */
2883 for (bigobj = los_object_list; bigobj;) {
2884 if (object_is_pinned (bigobj->data)) {
2885 unpin_object (bigobj->data);
2888 /* not referenced anywhere, so we can free it */
2890 prevbo->next = bigobj->next;
2892 los_object_list = bigobj->next;
2894 bigobj = bigobj->next;
2895 free_large_object (to_free);
2899 bigobj = bigobj->next;
2903 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
2908 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
2910 major_collector.sweep ();
2913 time_major_sweep += TV_ELAPSED_MS (atv, btv);
2915 /* walk the pin_queue, build up the fragment list of free memory, unmark
2916 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2919 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
2922 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
2924 TV_GETTIME (all_btv);
2925 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2928 dump_heap ("major", num_major_gcs - 1, reason);
2930 /* prepare the pin queue for the next collection */
2932 if (fin_ready_list || critical_fin_list) {
2933 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2934 mono_gc_finalize_notify ();
2938 g_assert (gray_object_queue_is_empty (&gray_queue));
2940 num_major_sections = major_collector.get_num_major_sections ();
2942 num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 0);
2943 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
2945 save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2947 * We aim to allow the allocation of as many sections as is
2948 * necessary to reclaim save_target sections in the next
2949 * collection. We assume the collection pattern won't change.
2950 * In the last cycle, we had num_major_sections_saved for
2951 * minor_collection_sections_alloced. Assuming things won't
2952 * change, this must be the same ratio as save_target for
2953 * allowance_target, i.e.
2955 * num_major_sections_saved save_target
2956 * --------------------------------- == ----------------
2957 * minor_collection_sections_alloced allowance_target
2961 allowance_target = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major_collector.section_size + los_memory_alloced) / (double)(num_major_sections_saved * major_collector.section_size + los_memory_saved));
2963 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2965 minor_collection_sections_alloced = 0;
2966 last_los_memory_usage = los_memory_usage;
2968 major_collector.finish_major_collection ();
2970 check_scan_starts ();
2972 binary_protocol_flush_buffers (FALSE);
2974 //consistency_check ();
2978 major_collection (const char *reason)
2980 if (g_getenv ("MONO_GC_NO_MAJOR")) {
2981 collect_nursery (0);
2985 current_collection_generation = GENERATION_OLD;
2986 major_do_collection (reason);
2987 current_collection_generation = -1;
2991 * When deciding if it's better to collect or to expand, keep track
2992 * of how much garbage was reclaimed with the last collection: if it's too
2994 * This is called when we could not allocate a small object.
2996 static void __attribute__((noinline))
2997 minor_collect_or_expand_inner (size_t size)
2999 int do_minor_collection = 1;
3001 g_assert (nursery_section);
3002 if (do_minor_collection) {
3003 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3005 if (collect_nursery (size)) {
3006 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3007 major_collection ("minor overflow");
3008 /* keep events symmetric */
3009 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3011 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3013 /* this also sets the proper pointers for the next allocation */
3014 if (!search_fragment_for_size (size)) {
3016 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3017 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3018 for (i = 0; i < last_num_pinned; ++i) {
3019 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])));
3023 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3025 //report_internal_mem_usage ();
3029 * ######################################################################
3030 * ######## Memory allocation from the OS
3031 * ######################################################################
3032 * This section of code deals with getting memory from the OS and
3033 * allocating memory for GC-internal data structures.
3034 * Internal memory can be handled with a freelist for small objects.
3040 G_GNUC_UNUSED static void
3041 report_internal_mem_usage (void)
3043 printf ("Internal memory usage:\n");
3044 mono_sgen_report_internal_mem_usage ();
3045 printf ("Pinned memory usage:\n");
3046 major_collector.report_pinned_memory_usage ();
3050 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3051 * This must not require any lock.
3054 mono_sgen_alloc_os_memory (size_t size, int activate)
3057 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3059 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3060 size += pagesize - 1;
3061 size &= ~(pagesize - 1);
3062 ptr = mono_valloc (0, size, prot_flags);
3064 total_alloc += size;
3069 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3072 mono_sgen_free_os_memory (void *addr, size_t size)
3074 mono_vfree (addr, size);
3076 size += pagesize - 1;
3077 size &= ~(pagesize - 1);
3079 total_alloc -= size;
3083 * ######################################################################
3084 * ######## Object allocation
3085 * ######################################################################
3086 * This section of code deals with allocating memory for objects.
3087 * There are several ways:
3088 * *) allocate large objects
3089 * *) allocate normal objects
3090 * *) fast lock-free allocation
3091 * *) allocation of pinned objects
3095 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3097 /* remove from the list */
3099 prev->next = frag->next;
3101 nursery_fragments = frag->next;
3102 nursery_next = frag->fragment_start;
3103 nursery_frag_real_end = frag->fragment_end;
3105 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));
3106 frag->next = fragment_freelist;
3107 fragment_freelist = frag;
3110 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3111 * an object of size @size
3112 * Return FALSE if not found (which means we need a collection)
3115 search_fragment_for_size (size_t size)
3117 Fragment *frag, *prev;
3118 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3120 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3121 /* Clear the remaining space, pinning depends on this */
3122 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3125 for (frag = nursery_fragments; frag; frag = frag->next) {
3126 if (size <= (frag->fragment_end - frag->fragment_start)) {
3127 setup_fragment (frag, prev, size);
3136 * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3137 * This improves nursery usage.
3140 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3142 Fragment *frag, *prev, *min_prev;
3143 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));
3145 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3146 /* Clear the remaining space, pinning depends on this */
3147 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3149 min_prev = GINT_TO_POINTER (-1);
3152 for (frag = nursery_fragments; frag; frag = frag->next) {
3153 int frag_size = frag->fragment_end - frag->fragment_start;
3154 if (desired_size <= frag_size) {
3155 setup_fragment (frag, prev, desired_size);
3156 return desired_size;
3158 if (minimum_size <= frag_size)
3164 if (min_prev != GINT_TO_POINTER (-1)) {
3167 frag = min_prev->next;
3169 frag = nursery_fragments;
3171 frag_size = frag->fragment_end - frag->fragment_start;
3172 HEAVY_STAT (++stat_wasted_fragments_used);
3173 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3175 setup_fragment (frag, min_prev, minimum_size);
3183 alloc_degraded (MonoVTable *vtable, size_t size)
3185 if (need_major_collection ()) {
3186 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3188 major_collection ("degraded overflow");
3190 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3193 degraded_mode += size;
3194 return major_collector.alloc_degraded (vtable, size);
3198 * Provide a variant that takes just the vtable for small fixed-size objects.
3199 * The aligned size is already computed and stored in vt->gc_descr.
3200 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3201 * processing. We can keep track of where objects start, for example,
3202 * so when we scan the thread stacks for pinned objects, we can start
3203 * a search for the pinned object in SCAN_START_SIZE chunks.
3206 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3208 /* FIXME: handle OOM */
3213 HEAVY_STAT (++stat_objects_alloced);
3214 if (size <= MAX_SMALL_OBJ_SIZE)
3215 HEAVY_STAT (stat_bytes_alloced += size);
3217 HEAVY_STAT (stat_bytes_alloced_los += size);
3219 size = ALIGN_UP (size);
3221 g_assert (vtable->gc_descr);
3223 if (G_UNLIKELY (collect_before_allocs)) {
3224 if (nursery_section) {
3225 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3227 collect_nursery (0);
3229 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3230 if (!degraded_mode && !search_fragment_for_size (size)) {
3232 g_assert_not_reached ();
3238 * We must already have the lock here instead of after the
3239 * fast path because we might be interrupted in the fast path
3240 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3241 * and we'll end up allocating an object in a fragment which
3242 * no longer belongs to us.
3244 * The managed allocator does not do this, but it's treated
3245 * specially by the world-stopping code.
3248 if (size > MAX_SMALL_OBJ_SIZE) {
3249 p = alloc_large_inner (vtable, size);
3251 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3253 p = (void**)TLAB_NEXT;
3254 /* FIXME: handle overflow */
3255 new_next = (char*)p + size;
3256 TLAB_NEXT = new_next;
3258 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3262 * FIXME: We might need a memory barrier here so the change to tlab_next is
3263 * visible before the vtable store.
3266 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3267 binary_protocol_alloc (p , vtable, size);
3268 g_assert (*p == NULL);
3271 g_assert (TLAB_NEXT == new_next);
3278 /* there are two cases: the object is too big or we run out of space in the TLAB */
3279 /* we also reach here when the thread does its first allocation after a minor
3280 * collection, since the tlab_ variables are initialized to NULL.
3281 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3282 * objects that need finalizers can have the high bit set in their size
3283 * so the above check fails and we can readily add the object to the queue.
3284 * This avoids taking again the GC lock when registering, but this is moot when
3285 * doing thread-local allocation, so it may not be a good idea.
3287 g_assert (TLAB_NEXT == new_next);
3288 if (TLAB_NEXT >= TLAB_REAL_END) {
3290 * Run out of space in the TLAB. When this happens, some amount of space
3291 * remains in the TLAB, but not enough to satisfy the current allocation
3292 * request. Currently, we retire the TLAB in all cases, later we could
3293 * keep it if the remaining space is above a treshold, and satisfy the
3294 * allocation directly from the nursery.
3297 /* when running in degraded mode, we continue allocing that way
3298 * for a while, to decrease the number of useless nursery collections.
3300 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3301 p = alloc_degraded (vtable, size);
3302 binary_protocol_alloc_degraded (p, vtable, size);
3306 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3307 if (size > tlab_size) {
3308 /* Allocate directly from the nursery */
3309 if (nursery_next + size >= nursery_frag_real_end) {
3310 if (!search_fragment_for_size (size)) {
3311 minor_collect_or_expand_inner (size);
3312 if (degraded_mode) {
3313 p = alloc_degraded (vtable, size);
3314 binary_protocol_alloc_degraded (p, vtable, size);
3320 p = (void*)nursery_next;
3321 nursery_next += size;
3322 if (nursery_next > nursery_frag_real_end) {
3327 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3328 memset (p, 0, size);
3330 int alloc_size = tlab_size;
3331 int available_in_nursery = nursery_frag_real_end - nursery_next;
3333 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3335 if (alloc_size >= available_in_nursery) {
3336 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3337 alloc_size = available_in_nursery;
3339 alloc_size = search_fragment_for_size_range (tlab_size, size);
3341 alloc_size = tlab_size;
3342 minor_collect_or_expand_inner (tlab_size);
3343 if (degraded_mode) {
3344 p = alloc_degraded (vtable, size);
3345 binary_protocol_alloc_degraded (p, vtable, size);
3352 /* Allocate a new TLAB from the current nursery fragment */
3353 TLAB_START = nursery_next;
3354 nursery_next += alloc_size;
3355 TLAB_NEXT = TLAB_START;
3356 TLAB_REAL_END = TLAB_START + alloc_size;
3357 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3359 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3360 memset (TLAB_START, 0, alloc_size);
3362 /* Allocate from the TLAB */
3363 p = (void*)TLAB_NEXT;
3365 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3367 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3370 /* Reached tlab_temp_end */
3372 /* record the scan start so we can find pinned objects more easily */
3373 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3374 /* we just bump tlab_temp_end as well */
3375 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3376 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3380 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3381 binary_protocol_alloc (p, vtable, size);
3388 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3394 size = ALIGN_UP (size);
3396 g_assert (vtable->gc_descr);
3397 if (size <= MAX_SMALL_OBJ_SIZE) {
3398 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3400 p = (void**)TLAB_NEXT;
3401 /* FIXME: handle overflow */
3402 new_next = (char*)p + size;
3403 TLAB_NEXT = new_next;
3405 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3409 * FIXME: We might need a memory barrier here so the change to tlab_next is
3410 * visible before the vtable store.
3413 HEAVY_STAT (++stat_objects_alloced);
3414 HEAVY_STAT (stat_bytes_alloced += size);
3416 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3417 binary_protocol_alloc (p, vtable, size);
3418 g_assert (*p == NULL);
3421 g_assert (TLAB_NEXT == new_next);
3430 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
3433 #ifndef DISABLE_CRITICAL_REGION
3435 ENTER_CRITICAL_REGION;
3436 res = mono_gc_try_alloc_obj_nolock (vtable, size);
3438 EXIT_CRITICAL_REGION;
3441 EXIT_CRITICAL_REGION;
3444 res = mono_gc_alloc_obj_nolock (vtable, size);
3450 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
3453 #ifndef DISABLE_CRITICAL_REGION
3455 ENTER_CRITICAL_REGION;
3456 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
3458 arr->max_length = max_length;
3459 EXIT_CRITICAL_REGION;
3462 EXIT_CRITICAL_REGION;
3467 arr = mono_gc_alloc_obj_nolock (vtable, size);
3468 arr->max_length = max_length;
3476 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
3479 MonoArrayBounds *bounds;
3483 arr = mono_gc_alloc_obj_nolock (vtable, size);
3484 arr->max_length = max_length;
3486 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
3487 arr->bounds = bounds;
3495 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
3498 #ifndef DISABLE_CRITICAL_REGION
3500 ENTER_CRITICAL_REGION;
3501 str = mono_gc_try_alloc_obj_nolock (vtable, size);
3504 EXIT_CRITICAL_REGION;
3507 EXIT_CRITICAL_REGION;
3512 str = mono_gc_alloc_obj_nolock (vtable, size);
3521 * To be used for interned strings and possibly MonoThread, reflection handles.
3522 * We may want to explicitly free these objects.
3525 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
3527 /* FIXME: handle OOM */
3529 size = ALIGN_UP (size);
3531 if (size > MAX_SMALL_OBJ_SIZE) {
3532 /* large objects are always pinned anyway */
3533 p = alloc_large_inner (vtable, size);
3535 DEBUG (9, g_assert (vtable->klass->inited));
3536 p = major_collector.alloc_small_pinned_obj (size, vtable->klass->has_references);
3538 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3539 binary_protocol_alloc_pinned (p, vtable, size);
3546 * ######################################################################
3547 * ######## Finalization support
3548 * ######################################################################
3552 * this is valid for the nursery: if the object has been forwarded it means it's
3553 * still refrenced from a root. If it is pinned it's still alive as well.
3554 * Return TRUE if @obj is ready to be finalized.
3556 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3559 is_critical_finalizer (FinalizeEntry *entry)
3564 if (!mono_defaults.critical_finalizer_object)
3567 obj = entry->object;
3568 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3570 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
3574 queue_finalization_entry (FinalizeEntry *entry) {
3575 if (is_critical_finalizer (entry)) {
3576 entry->next = critical_fin_list;
3577 critical_fin_list = entry;
3579 entry->next = fin_ready_list;
3580 fin_ready_list = entry;
3584 /* LOCKING: requires that the GC lock is held */
3586 rehash_fin_table (FinalizeEntryHashTable *hash_table)
3588 FinalizeEntry **finalizable_hash = hash_table->table;
3589 mword finalizable_hash_size = hash_table->size;
3592 FinalizeEntry **new_hash;
3593 FinalizeEntry *entry, *next;
3594 int new_size = g_spaced_primes_closest (hash_table->num_registered);
3596 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
3597 for (i = 0; i < finalizable_hash_size; ++i) {
3598 for (entry = finalizable_hash [i]; entry; entry = next) {
3599 hash = mono_object_hash (entry->object) % new_size;
3601 entry->next = new_hash [hash];
3602 new_hash [hash] = entry;
3605 mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
3606 hash_table->table = new_hash;
3607 hash_table->size = new_size;
3610 /* LOCKING: requires that the GC lock is held */
3612 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
3614 if (hash_table->num_registered >= hash_table->size * 2)
3615 rehash_fin_table (hash_table);
3618 /* LOCKING: requires that the GC lock is held */
3620 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
3622 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
3623 FinalizeEntry *entry, *prev;
3625 FinalizeEntry **finalizable_hash = hash_table->table;
3626 mword finalizable_hash_size = hash_table->size;
3630 for (i = 0; i < finalizable_hash_size; ++i) {
3632 for (entry = finalizable_hash [i]; entry;) {
3633 if ((char*)entry->object >= start && (char*)entry->object < end && !major_collector.is_object_live (entry->object)) {
3634 gboolean is_fin_ready = object_is_fin_ready (entry->object);
3635 char *copy = entry->object;
3636 copy_func ((void**)©, queue);
3639 FinalizeEntry *next;
3640 /* remove and put in fin_ready_list */
3642 prev->next = entry->next;
3644 finalizable_hash [i] = entry->next;
3646 num_ready_finalizers++;
3647 hash_table->num_registered--;
3648 queue_finalization_entry (entry);
3649 /* Make it survive */
3650 from = entry->object;
3651 entry->object = copy;
3652 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));
3656 char *from = entry->object;
3657 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
3658 FinalizeEntry *next = entry->next;
3659 unsigned int major_hash;
3660 /* remove from the list */
3662 prev->next = entry->next;
3664 finalizable_hash [i] = entry->next;
3665 hash_table->num_registered--;
3667 entry->object = copy;
3669 /* insert it into the major hash */
3670 rehash_fin_table_if_necessary (&major_finalizable_hash);
3671 major_hash = mono_object_hash ((MonoObject*) copy) %
3672 major_finalizable_hash.size;
3673 entry->next = major_finalizable_hash.table [major_hash];
3674 major_finalizable_hash.table [major_hash] = entry;
3675 major_finalizable_hash.num_registered++;
3677 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
3682 /* update pointer */
3683 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
3684 entry->object = copy;
3689 entry = entry->next;
3695 object_is_reachable (char *object, char *start, char *end)
3697 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
3698 if (object < start || object >= end)
3700 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
3703 /* LOCKING: requires that the GC lock is held */
3705 null_ephemerons_for_domain (MonoDomain *domain)
3707 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3710 MonoObject *object = (MonoObject*)current->array;
3712 if (object && !object->vtable) {
3713 EphemeronLinkNode *tmp = current;
3716 prev->next = current->next;
3718 ephemeron_list = current->next;
3720 current = current->next;
3721 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3724 current = current->next;
3729 /* LOCKING: requires that the GC lock is held */
3731 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3733 int was_in_nursery, was_promoted;
3734 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3736 Ephemeron *cur, *array_end;
3740 char *object = current->array;
3742 if (!object_is_reachable (object, start, end)) {
3743 EphemeronLinkNode *tmp = current;
3745 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
3748 prev->next = current->next;
3750 ephemeron_list = current->next;
3752 current = current->next;
3753 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3758 was_in_nursery = ptr_in_nursery (object);
3759 copy_func ((void**)&object, queue);
3760 current->array = object;
3762 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
3763 was_promoted = was_in_nursery && !ptr_in_nursery (object);
3765 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
3767 array = (MonoArray*)object;
3768 cur = mono_array_addr (array, Ephemeron, 0);
3769 array_end = cur + mono_array_length_fast (array);
3770 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3772 for (; cur < array_end; ++cur) {
3773 char *key = (char*)cur->key;
3775 if (!key || key == tombstone)
3778 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3779 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3780 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3782 if (!object_is_reachable (key, start, end)) {
3783 cur->key = tombstone;
3789 if (ptr_in_nursery (key)) {/*key was not promoted*/
3790 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
3791 mono_sgen_add_to_global_remset (&cur->key);
3793 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
3794 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
3795 mono_sgen_add_to_global_remset (&cur->value);
3800 current = current->next;
3804 /* LOCKING: requires that the GC lock is held */
3806 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3808 int nothing_marked = 1;
3809 EphemeronLinkNode *current = ephemeron_list;
3811 Ephemeron *cur, *array_end;
3814 for (current = ephemeron_list; current; current = current->next) {
3815 char *object = current->array;
3816 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
3818 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
3819 if (object < start || object >= end)
3822 /*It has to be alive*/
3823 if (!object_is_reachable (object, start, end)) {
3824 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
3828 copy_func ((void**)&object, queue);
3830 array = (MonoArray*)object;
3831 cur = mono_array_addr (array, Ephemeron, 0);
3832 array_end = cur + mono_array_length_fast (array);
3833 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3835 for (; cur < array_end; ++cur) {
3836 char *key = cur->key;
3838 if (!key || key == tombstone)
3841 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3842 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3843 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3845 if (object_is_reachable (key, start, end)) {
3846 char *value = cur->value;
3848 copy_func ((void**)&cur->key, queue);
3850 if (!object_is_reachable (value, start, end))
3852 copy_func ((void**)&cur->value, queue);
3858 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
3859 return nothing_marked;
3862 /* LOCKING: requires that the GC lock is held */
3864 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
3866 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
3867 DisappearingLink **disappearing_link_hash = hash->table;
3868 int disappearing_link_hash_size = hash->size;
3869 DisappearingLink *entry, *prev;
3871 if (!hash->num_links)
3873 for (i = 0; i < disappearing_link_hash_size; ++i) {
3875 for (entry = disappearing_link_hash [i]; entry;) {
3876 char *object = DISLINK_OBJECT (entry);
3877 if (object >= start && object < end && !major_collector.is_object_live (object)) {
3878 gboolean track = DISLINK_TRACK (entry);
3879 if (!track && object_is_fin_ready (object)) {
3880 void **p = entry->link;
3881 DisappearingLink *old;
3883 /* remove from list */
3885 prev->next = entry->next;
3887 disappearing_link_hash [i] = entry->next;
3888 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
3890 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3895 char *copy = object;
3896 copy_func ((void**)©, queue);
3898 /* Update pointer if it's moved. If the object
3899 * has been moved out of the nursery, we need to
3900 * remove the link from the minor hash table to
3903 * FIXME: what if an object is moved earlier?
3906 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
3907 void **link = entry->link;
3908 DisappearingLink *old;
3909 /* remove from list */
3911 prev->next = entry->next;
3913 disappearing_link_hash [i] = entry->next;
3915 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3919 add_or_remove_disappearing_link ((MonoObject*)copy, link,
3920 track, GENERATION_OLD);
3922 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
3926 /* We set the track resurrection bit to
3927 * FALSE if the object is to be finalized
3928 * so that the object can be collected in
3929 * the next cycle (i.e. after it was
3932 *entry->link = HIDE_POINTER (copy,
3933 object_is_fin_ready (object) ? FALSE : track);
3934 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
3939 entry = entry->next;
3944 /* LOCKING: requires that the GC lock is held */
3946 null_links_for_domain (MonoDomain *domain, int generation)
3948 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
3949 DisappearingLink **disappearing_link_hash = hash->table;
3950 int disappearing_link_hash_size = hash->size;
3951 DisappearingLink *entry, *prev;
3953 for (i = 0; i < disappearing_link_hash_size; ++i) {
3955 for (entry = disappearing_link_hash [i]; entry; ) {
3956 char *object = DISLINK_OBJECT (entry);
3957 if (object && !((MonoObject*)object)->vtable) {
3958 DisappearingLink *next = entry->next;
3963 disappearing_link_hash [i] = next;
3965 if (*(entry->link)) {
3966 *(entry->link) = NULL;
3967 g_warning ("Disappearing link %p not freed", entry->link);
3969 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3976 entry = entry->next;
3981 /* LOCKING: requires that the GC lock is held */
3983 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
3984 FinalizeEntryHashTable *hash_table)
3986 FinalizeEntry **finalizable_hash = hash_table->table;
3987 mword finalizable_hash_size = hash_table->size;
3988 FinalizeEntry *entry, *prev;
3991 if (no_finalize || !out_size || !out_array)
3994 for (i = 0; i < finalizable_hash_size; ++i) {
3996 for (entry = finalizable_hash [i]; entry;) {
3997 if (mono_object_domain (entry->object) == domain) {
3998 FinalizeEntry *next;
3999 /* remove and put in out_array */
4001 prev->next = entry->next;
4003 finalizable_hash [i] = entry->next;
4005 hash_table->num_registered--;
4006 out_array [count ++] = entry->object;
4007 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));
4009 if (count == out_size)
4014 entry = entry->next;
4021 * mono_gc_finalizers_for_domain:
4022 * @domain: the unloading appdomain
4023 * @out_array: output array
4024 * @out_size: size of output array
4026 * Store inside @out_array up to @out_size objects that belong to the unloading
4027 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4028 * until it returns 0.
4029 * The items are removed from the finalizer data structure, so the caller is supposed
4031 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4034 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4039 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4040 if (result < out_size) {
4041 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4042 &major_finalizable_hash);
4050 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4052 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4053 FinalizeEntry **finalizable_hash;
4054 mword finalizable_hash_size;
4055 FinalizeEntry *entry, *prev;
4059 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4060 hash = mono_object_hash (obj);
4062 rehash_fin_table_if_necessary (hash_table);
4063 finalizable_hash = hash_table->table;
4064 finalizable_hash_size = hash_table->size;
4065 hash %= finalizable_hash_size;
4067 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4068 if (entry->object == obj) {
4070 /* remove from the list */
4072 prev->next = entry->next;
4074 finalizable_hash [hash] = entry->next;
4075 hash_table->num_registered--;
4076 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));
4077 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4085 /* request to deregister, but already out of the list */
4089 entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4090 entry->object = obj;
4091 entry->next = finalizable_hash [hash];
4092 finalizable_hash [hash] = entry;
4093 hash_table->num_registered++;
4094 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)));
4099 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4101 if (ptr_in_nursery (obj))
4102 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4104 register_for_finalization (obj, user_data, GENERATION_OLD);
4108 rehash_dislink (DisappearingLinkHashTable *hash_table)
4110 DisappearingLink **disappearing_link_hash = hash_table->table;
4111 int disappearing_link_hash_size = hash_table->size;
4114 DisappearingLink **new_hash;
4115 DisappearingLink *entry, *next;
4116 int new_size = g_spaced_primes_closest (hash_table->num_links);
4118 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4119 for (i = 0; i < disappearing_link_hash_size; ++i) {
4120 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4121 hash = mono_aligned_addr_hash (entry->link) % new_size;
4123 entry->next = new_hash [hash];
4124 new_hash [hash] = entry;
4127 mono_sgen_free_internal_dynamic (disappearing_link_hash,
4128 disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4129 hash_table->table = new_hash;
4130 hash_table->size = new_size;
4133 /* LOCKING: assumes the GC lock is held */
4135 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4137 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4138 DisappearingLink *entry, *prev;
4140 DisappearingLink **disappearing_link_hash = hash_table->table;
4141 int disappearing_link_hash_size = hash_table->size;
4143 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4144 rehash_dislink (hash_table);
4145 disappearing_link_hash = hash_table->table;
4146 disappearing_link_hash_size = hash_table->size;
4148 /* FIXME: add check that link is not in the heap */
4149 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4150 entry = disappearing_link_hash [hash];
4152 for (; entry; entry = entry->next) {
4153 /* link already added */
4154 if (link == entry->link) {
4155 /* NULL obj means remove */
4158 prev->next = entry->next;
4160 disappearing_link_hash [hash] = entry->next;
4161 hash_table->num_links--;
4162 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4163 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4166 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4174 entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4175 *link = HIDE_POINTER (obj, track);
4177 entry->next = disappearing_link_hash [hash];
4178 disappearing_link_hash [hash] = entry;
4179 hash_table->num_links++;
4180 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)));
4183 /* LOCKING: assumes the GC lock is held */
4185 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4187 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4188 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4190 if (ptr_in_nursery (obj))
4191 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4193 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4198 mono_gc_invoke_finalizers (void)
4200 FinalizeEntry *entry = NULL;
4201 gboolean entry_is_critical = FALSE;
4204 /* FIXME: batch to reduce lock contention */
4205 while (fin_ready_list || critical_fin_list) {
4209 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4211 /* We have finalized entry in the last
4212 interation, now we need to remove it from
4215 *list = entry->next;
4217 FinalizeEntry *e = *list;
4218 while (e->next != entry)
4220 e->next = entry->next;
4222 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4226 /* Now look for the first non-null entry. */
4227 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4230 entry_is_critical = FALSE;
4232 entry_is_critical = TRUE;
4233 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4238 g_assert (entry->object);
4239 num_ready_finalizers--;
4240 obj = entry->object;
4241 entry->object = NULL;
4242 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4250 g_assert (entry->object == NULL);
4252 /* the object is on the stack so it is pinned */
4253 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4254 mono_gc_run_finalize (obj, NULL);
4261 mono_gc_pending_finalizers (void)
4263 return fin_ready_list || critical_fin_list;
4266 /* Negative value to remove */
4268 mono_gc_add_memory_pressure (gint64 value)
4270 /* FIXME: Use interlocked functions */
4272 memory_pressure += value;
4277 mono_sgen_register_major_sections_alloced (int num_sections)
4279 minor_collection_sections_alloced += num_sections;
4283 mono_sgen_get_minor_collection_allowance (void)
4285 return minor_collection_allowance;
4289 * ######################################################################
4290 * ######## registered roots support
4291 * ######################################################################
4295 rehash_roots (gboolean pinned)
4299 RootRecord **new_hash;
4300 RootRecord *entry, *next;
4303 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4304 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4305 for (i = 0; i < roots_hash_size [pinned]; ++i) {
4306 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4307 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4309 entry->next = new_hash [hash];
4310 new_hash [hash] = entry;
4313 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4314 roots_hash [pinned] = new_hash;
4315 roots_hash_size [pinned] = new_size;
4319 find_root (int root_type, char *start, guint32 addr_hash)
4321 RootRecord *new_root;
4323 guint32 hash = addr_hash % roots_hash_size [root_type];
4324 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4325 /* we allow changing the size and the descriptor (for thread statics etc) */
4326 if (new_root->start_root == start) {
4335 * We do not coalesce roots.
4338 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4340 RootRecord *new_root;
4341 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
4344 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4345 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
4348 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4349 new_root = find_root (i, start, addr_hash);
4350 /* we allow changing the size and the descriptor (for thread statics etc) */
4352 size_t old_size = new_root->end_root - new_root->start_root;
4353 new_root->end_root = new_root->start_root + size;
4354 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
4355 ((new_root->root_desc == 0) && (descr == NULL)));
4356 new_root->root_desc = (mword)descr;
4358 roots_size -= old_size;
4363 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
4365 new_root->start_root = start;
4366 new_root->end_root = new_root->start_root + size;
4367 new_root->root_desc = (mword)descr;
4369 hash = addr_hash % roots_hash_size [root_type];
4370 num_roots_entries [root_type]++;
4371 new_root->next = roots_hash [root_type] [hash];
4372 roots_hash [root_type][hash] = new_root;
4373 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));
4383 mono_gc_register_root (char *start, size_t size, void *descr)
4385 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4389 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4391 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4395 mono_gc_deregister_root (char* addr)
4397 RootRecord *tmp, *prev;
4398 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
4402 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4403 hash = addr_hash % roots_hash_size [root_type];
4404 tmp = roots_hash [root_type][hash];
4407 if (tmp->start_root == (char*)addr) {
4409 prev->next = tmp->next;
4411 roots_hash [root_type][hash] = tmp->next;
4412 roots_size -= (tmp->end_root - tmp->start_root);
4413 num_roots_entries [root_type]--;
4414 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
4415 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
4426 * ######################################################################
4427 * ######## Thread handling (stop/start code)
4428 * ######################################################################
4431 /* FIXME: handle large/small config */
4432 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
4434 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
4436 #if USE_SIGNAL_BASED_START_STOP_WORLD
4438 static MonoSemType suspend_ack_semaphore;
4439 static MonoSemType *suspend_ack_semaphore_ptr;
4440 static unsigned int global_stop_count = 0;
4442 static sigset_t suspend_signal_mask;
4443 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4445 /* LOCKING: assumes the GC lock is held */
4447 mono_sgen_get_thread_table (void)
4449 return thread_table;
4453 mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
4455 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
4456 SgenThreadInfo *info;
4458 info = thread_table [hash];
4459 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
4466 update_current_thread_stack (void *start)
4468 void *ptr = cur_thread_regs;
4469 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
4471 info->stack_start = align_pointer (&ptr);
4472 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
4473 ARCH_STORE_REGS (ptr);
4474 info->stopped_regs = ptr;
4475 if (gc_callbacks.thread_suspend_func)
4476 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
4480 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
4481 * have cross-domain checks in the write barrier.
4483 //#define XDOMAIN_CHECKS_IN_WBARRIER
4485 #ifndef SGEN_BINARY_PROTOCOL
4486 #ifndef HEAVY_STATISTICS
4487 #define MANAGED_ALLOCATION
4488 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
4489 #define MANAGED_WBARRIER
4495 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
4498 mono_sgen_wait_for_suspend_ack (int count)
4502 for (i = 0; i < count; ++i) {
4503 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
4504 if (errno != EINTR) {
4505 g_error ("sem_wait ()");
4512 restart_threads_until_none_in_managed_allocator (void)
4514 SgenThreadInfo *info;
4515 int i, result, num_threads_died = 0;
4516 int sleep_duration = -1;
4519 int restart_count = 0, restarted_count = 0;
4520 /* restart all threads that stopped in the
4522 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4523 for (info = thread_table [i]; info; info = info->next) {
4526 if (!info->stack_start || info->in_critical_region ||
4527 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
4528 binary_protocol_thread_restart ((gpointer)info->id);
4529 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4530 result = thread_resume (pthread_mach_thread_np (info->id));
4532 result = pthread_kill (info->id, restart_signal_num);
4540 /* we set the stopped_ip to
4541 NULL for threads which
4542 we're not restarting so
4543 that we can easily identify
4545 info->stopped_ip = NULL;
4546 info->stopped_domain = NULL;
4550 /* if no threads were restarted, we're done */
4551 if (restart_count == 0)
4554 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4555 /* mach thread_resume is synchronous so we dont need to wait for them */
4557 /* wait for the threads to signal their restart */
4558 mono_sgen_wait_for_suspend_ack (restart_count);
4561 if (sleep_duration < 0) {
4565 g_usleep (sleep_duration);
4566 sleep_duration += 10;
4569 /* stop them again */
4570 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4571 for (info = thread_table [i]; info; info = info->next) {
4572 if (info->skip || info->stopped_ip == NULL)
4574 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4575 result = thread_suspend (pthread_mach_thread_np (info->id));
4577 result = pthread_kill (info->id, suspend_signal_num);
4586 /* some threads might have died */
4587 num_threads_died += restart_count - restarted_count;
4588 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4589 /* mach thread_resume is synchronous so we dont need to wait for them */
4591 /* wait for the threads to signal their suspension
4593 mono_sgen_wait_for_suspend_ack (restart_count);
4597 return num_threads_died;
4600 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
4602 suspend_handler (int sig, siginfo_t *siginfo, void *context)
4604 SgenThreadInfo *info;
4607 int old_errno = errno;
4608 gpointer regs [ARCH_NUM_REGS];
4609 gpointer stack_start;
4611 id = pthread_self ();
4612 info = mono_sgen_thread_info_lookup (id);
4613 info->stopped_domain = mono_domain_get ();
4614 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
4615 stop_count = global_stop_count;
4616 /* duplicate signal */
4617 if (0 && info->stop_count == stop_count) {
4621 #ifdef HAVE_KW_THREAD
4622 /* update the remset info in the thread data structure */
4623 info->remset = remembered_set;
4625 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
4626 /* If stack_start is not within the limits, then don't set it
4627 in info and we will be restarted. */
4628 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
4629 info->stack_start = stack_start;
4631 ARCH_COPY_SIGCTX_REGS (regs, context);
4632 info->stopped_regs = regs;
4634 g_assert (!info->stack_start);
4637 /* Notify the JIT */
4638 if (gc_callbacks.thread_suspend_func)
4639 gc_callbacks.thread_suspend_func (info->runtime_data, context);
4641 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4642 /* notify the waiting thread */
4643 MONO_SEM_POST (suspend_ack_semaphore_ptr);
4644 info->stop_count = stop_count;
4646 /* wait until we receive the restart signal */
4649 sigsuspend (&suspend_signal_mask);
4650 } while (info->signal != restart_signal_num);
4652 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4653 /* notify the waiting thread */
4654 MONO_SEM_POST (suspend_ack_semaphore_ptr);
4660 restart_handler (int sig)
4662 SgenThreadInfo *info;
4663 int old_errno = errno;
4665 info = mono_sgen_thread_info_lookup (pthread_self ());
4666 info->signal = restart_signal_num;
4667 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4673 acquire_gc_locks (void)
4679 release_gc_locks (void)
4681 UNLOCK_INTERRUPTION;
4684 static TV_DECLARE (stop_world_time);
4685 static unsigned long max_pause_usec = 0;
4687 /* LOCKING: assumes the GC lock is held */
4689 stop_world (int generation)
4693 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
4694 acquire_gc_locks ();
4696 update_current_thread_stack (&count);
4698 global_stop_count++;
4699 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 ()));
4700 TV_GETTIME (stop_world_time);
4701 count = mono_sgen_thread_handshake (suspend_signal_num);
4702 count -= restart_threads_until_none_in_managed_allocator ();
4703 g_assert (count >= 0);
4704 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
4705 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
4709 /* LOCKING: assumes the GC lock is held */
4711 restart_world (int generation)
4714 SgenThreadInfo *info;
4715 TV_DECLARE (end_sw);
4718 /* notify the profiler of the leftovers */
4719 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
4720 if (moved_objects_idx) {
4721 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
4722 moved_objects_idx = 0;
4725 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
4726 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4727 for (info = thread_table [i]; info; info = info->next) {
4728 info->stack_start = NULL;
4729 info->stopped_regs = NULL;
4733 release_gc_locks ();
4735 count = mono_sgen_thread_handshake (restart_signal_num);
4736 TV_GETTIME (end_sw);
4737 usec = TV_ELAPSED (stop_world_time, end_sw);
4738 max_pause_usec = MAX (usec, max_pause_usec);
4739 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
4740 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
4744 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
4747 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
4749 gc_callbacks = *callbacks;
4753 mono_gc_get_gc_callbacks ()
4755 return &gc_callbacks;
4758 /* Variables holding start/end nursery so it won't have to be passed at every call */
4759 static void *scan_area_arg_start, *scan_area_arg_end;
4762 mono_gc_conservatively_scan_area (void *start, void *end)
4764 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
4768 mono_gc_scan_object (void *obj)
4770 g_assert_not_reached ();
4771 if (current_collection_generation == GENERATION_NURSERY)
4772 major_collector.copy_object (&obj, &gray_queue);
4774 major_collector.copy_or_mark_object (&obj, &gray_queue);
4779 * Mark from thread stacks and registers.
4782 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
4785 SgenThreadInfo *info;
4787 scan_area_arg_start = start_nursery;
4788 scan_area_arg_end = end_nursery;
4790 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4791 for (info = thread_table [i]; info; info = info->next) {
4793 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));
4796 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));
4797 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
4798 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
4800 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
4803 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
4804 start_nursery, end_nursery, PIN_TYPE_STACK);
4810 find_pinning_ref_from_thread (char *obj, size_t size)
4813 SgenThreadInfo *info;
4814 char *endobj = obj + size;
4816 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4817 for (info = thread_table [i]; info; info = info->next) {
4818 char **start = (char**)info->stack_start;
4821 while (start < (char**)info->stack_end) {
4822 if (*start >= obj && *start < endobj) {
4823 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));
4828 /* FIXME: check info->stopped_regs */
4834 ptr_on_stack (void *ptr)
4836 gpointer stack_start = &stack_start;
4837 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
4839 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
4845 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
4852 HEAVY_STAT (++stat_global_remsets_processed);
4854 HEAVY_STAT (++stat_local_remsets_processed);
4856 /* FIXME: exclude stack locations */
4857 switch ((*p) & REMSET_TYPE_MASK) {
4858 case REMSET_LOCATION:
4860 //__builtin_prefetch (ptr);
4861 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
4862 gpointer old = *ptr;
4863 major_collector.copy_object (ptr, queue);
4864 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
4866 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
4867 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4869 * If the object is pinned, each reference to it from nonpinned objects
4870 * becomes part of the global remset, which can grow very large.
4872 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4873 mono_sgen_add_to_global_remset (ptr);
4876 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
4880 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4881 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4884 while (count-- > 0) {
4885 major_collector.copy_object (ptr, queue);
4886 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
4887 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
4888 mono_sgen_add_to_global_remset (ptr);
4893 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4894 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4896 major_collector.minor_scan_object ((char*)ptr, queue);
4898 case REMSET_VTYPE: {
4899 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4900 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4905 ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
4909 g_assert_not_reached ();
4914 #ifdef HEAVY_STATISTICS
4916 collect_store_remsets (RememberedSet *remset, mword *bumper)
4918 mword *p = remset->data;
4923 while (p < remset->store_next) {
4924 switch ((*p) & REMSET_TYPE_MASK) {
4925 case REMSET_LOCATION:
4928 ++stat_saved_remsets_1;
4930 if (*p == last1 || *p == last2) {
4931 ++stat_saved_remsets_2;
4948 g_assert_not_reached ();
4958 RememberedSet *remset;
4960 SgenThreadInfo *info;
4962 mword *addresses, *bumper, *p, *r;
4964 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4965 for (info = thread_table [i]; info; info = info->next) {
4966 for (remset = info->remset; remset; remset = remset->next)
4967 size += remset->store_next - remset->data;
4970 for (remset = freed_thread_remsets; remset; remset = remset->next)
4971 size += remset->store_next - remset->data;
4972 for (remset = global_remset; remset; remset = remset->next)
4973 size += remset->store_next - remset->data;
4975 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
4977 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4978 for (info = thread_table [i]; info; info = info->next) {
4979 for (remset = info->remset; remset; remset = remset->next)
4980 bumper = collect_store_remsets (remset, bumper);
4983 for (remset = global_remset; remset; remset = remset->next)
4984 bumper = collect_store_remsets (remset, bumper);
4985 for (remset = freed_thread_remsets; remset; remset = remset->next)
4986 bumper = collect_store_remsets (remset, bumper);
4988 g_assert (bumper <= addresses + size);
4990 stat_store_remsets += bumper - addresses;
4992 sort_addresses ((void**)addresses, bumper - addresses);
4995 while (r < bumper) {
5001 stat_store_remsets_unique += p - addresses;
5003 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5008 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5010 *info->store_remset_buffer_index_addr = 0;
5011 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5015 remset_byte_size (RememberedSet *remset)
5017 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5021 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5024 SgenThreadInfo *info;
5025 RememberedSet *remset;
5026 GenericStoreRememberedSet *store_remset;
5027 mword *p, *next_p, *store_pos;
5029 #ifdef HEAVY_STATISTICS
5033 /* the global one */
5034 for (remset = global_remset; remset; remset = remset->next) {
5035 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));
5036 store_pos = remset->data;
5037 for (p = remset->data; p < remset->store_next; p = next_p) {
5038 void **ptr = (void**)p [0];
5040 /*Ignore previously processed remset.*/
5041 if (!global_remset_location_was_not_added (ptr)) {
5046 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5049 * Clear global remsets of locations which no longer point to the
5050 * nursery. Otherwise, they could grow indefinitely between major
5053 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5055 if (ptr_in_nursery (*ptr)) {
5056 *store_pos ++ = p [0];
5057 HEAVY_STAT (++stat_global_remsets_readded);
5061 /* Truncate the remset */
5062 remset->store_next = store_pos;
5065 /* the generic store ones */
5066 store_remset = generic_store_remsets;
5067 while (store_remset) {
5068 GenericStoreRememberedSet *next = store_remset->next;
5070 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5071 gpointer addr = store_remset->data [i];
5073 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5076 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5078 store_remset = next;
5080 generic_store_remsets = NULL;
5082 /* the per-thread ones */
5083 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5084 for (info = thread_table [i]; info; info = info->next) {
5085 RememberedSet *next;
5087 for (remset = info->remset; remset; remset = next) {
5088 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));
5089 for (p = remset->data; p < remset->store_next;)
5090 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5091 remset->store_next = remset->data;
5092 next = remset->next;
5093 remset->next = NULL;
5094 if (remset != info->remset) {
5095 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5096 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5099 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5100 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5101 clear_thread_store_remset_buffer (info);
5105 /* the freed thread ones */
5106 while (freed_thread_remsets) {
5107 RememberedSet *next;
5108 remset = freed_thread_remsets;
5109 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));
5110 for (p = remset->data; p < remset->store_next;)
5111 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5112 next = remset->next;
5113 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5114 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5115 freed_thread_remsets = next;
5120 * Clear the info in the remembered sets: we're doing a major collection, so
5121 * the per-thread ones are not needed and the global ones will be reconstructed
5125 clear_remsets (void)
5128 SgenThreadInfo *info;
5129 RememberedSet *remset, *next;
5131 /* the global list */
5132 for (remset = global_remset; remset; remset = next) {
5133 remset->store_next = remset->data;
5134 next = remset->next;
5135 remset->next = NULL;
5136 if (remset != global_remset) {
5137 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5138 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5141 /* the generic store ones */
5142 while (generic_store_remsets) {
5143 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5144 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5145 generic_store_remsets = gs_next;
5147 /* the per-thread ones */
5148 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5149 for (info = thread_table [i]; info; info = info->next) {
5150 for (remset = info->remset; remset; remset = next) {
5151 remset->store_next = remset->data;
5152 next = remset->next;
5153 remset->next = NULL;
5154 if (remset != info->remset) {
5155 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5156 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5159 clear_thread_store_remset_buffer (info);
5163 /* the freed thread ones */
5164 while (freed_thread_remsets) {
5165 next = freed_thread_remsets->next;
5166 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5167 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5168 freed_thread_remsets = next;
5173 * Clear the thread local TLAB variables for all threads.
5178 SgenThreadInfo *info;
5181 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5182 for (info = thread_table [i]; info; info = info->next) {
5183 /* A new TLAB will be allocated when the thread does its first allocation */
5184 *info->tlab_start_addr = NULL;
5185 *info->tlab_next_addr = NULL;
5186 *info->tlab_temp_end_addr = NULL;
5187 *info->tlab_real_end_addr = NULL;
5192 /* LOCKING: assumes the GC lock is held */
5193 static SgenThreadInfo*
5194 gc_register_current_thread (void *addr)
5197 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5198 #ifndef HAVE_KW_THREAD
5199 SgenThreadInfo *__thread_info__ = info;
5205 memset (info, 0, sizeof (SgenThreadInfo));
5206 #ifndef HAVE_KW_THREAD
5207 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5209 g_assert (!pthread_getspecific (thread_info_key));
5210 pthread_setspecific (thread_info_key, info);
5215 info->id = ARCH_GET_THREAD ();
5216 info->stop_count = -1;
5219 info->stack_start = NULL;
5220 info->tlab_start_addr = &TLAB_START;
5221 info->tlab_next_addr = &TLAB_NEXT;
5222 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5223 info->tlab_real_end_addr = &TLAB_REAL_END;
5224 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5225 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5226 info->stopped_ip = NULL;
5227 info->stopped_domain = NULL;
5228 info->stopped_regs = NULL;
5230 binary_protocol_thread_register ((gpointer)info->id);
5232 #ifdef HAVE_KW_THREAD
5233 tlab_next_addr = &tlab_next;
5234 store_remset_buffer_index_addr = &store_remset_buffer_index;
5237 /* try to get it with attributes first */
5238 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5242 pthread_attr_t attr;
5243 pthread_getattr_np (pthread_self (), &attr);
5244 pthread_attr_getstack (&attr, &sstart, &size);
5245 info->stack_start_limit = sstart;
5246 info->stack_end = (char*)sstart + size;
5247 pthread_attr_destroy (&attr);
5249 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5250 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5251 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5254 /* FIXME: we assume the stack grows down */
5255 gsize stack_bottom = (gsize)addr;
5256 stack_bottom += 4095;
5257 stack_bottom &= ~4095;
5258 info->stack_end = (char*)stack_bottom;
5262 #ifdef HAVE_KW_THREAD
5263 stack_end = info->stack_end;
5266 /* hash into the table */
5267 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
5268 info->next = thread_table [hash];
5269 thread_table [hash] = info;
5271 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
5272 pthread_setspecific (remembered_set_key, info->remset);
5273 #ifdef HAVE_KW_THREAD
5274 remembered_set = info->remset;
5277 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5278 STORE_REMSET_BUFFER_INDEX = 0;
5280 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
5282 if (gc_callbacks.thread_attach_func)
5283 info->runtime_data = gc_callbacks.thread_attach_func ();
5289 add_generic_store_remset_from_buffer (gpointer *buffer)
5291 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5292 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5293 remset->next = generic_store_remsets;
5294 generic_store_remsets = remset;
5298 unregister_current_thread (void)
5301 SgenThreadInfo *prev = NULL;
5303 RememberedSet *rset;
5304 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
5306 binary_protocol_thread_unregister ((gpointer)id);
5308 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5309 p = thread_table [hash];
5311 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
5312 while (!ARCH_THREAD_EQUALS (p->id, id)) {
5317 thread_table [hash] = p->next;
5319 prev->next = p->next;
5322 if (freed_thread_remsets) {
5323 for (rset = p->remset; rset->next; rset = rset->next)
5325 rset->next = freed_thread_remsets;
5326 freed_thread_remsets = p->remset;
5328 freed_thread_remsets = p->remset;
5331 if (*p->store_remset_buffer_index_addr)
5332 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5333 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5338 unregister_thread (void *k)
5340 g_assert (!mono_domain_get ());
5342 unregister_current_thread ();
5347 mono_gc_register_thread (void *baseptr)
5349 SgenThreadInfo *info;
5353 info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5355 info = gc_register_current_thread (baseptr);
5357 return info != NULL;
5360 #if USE_PTHREAD_INTERCEPT
5363 void *(*start_routine) (void *);
5366 MonoSemType registered;
5367 } SgenThreadStartInfo;
5370 gc_start_thread (void *arg)
5372 SgenThreadStartInfo *start_info = arg;
5373 SgenThreadInfo* info;
5374 void *t_arg = start_info->arg;
5375 void *(*start_func) (void*) = start_info->start_routine;
5380 info = gc_register_current_thread (&result);
5382 post_result = MONO_SEM_POST (&(start_info->registered));
5383 g_assert (!post_result);
5384 result = start_func (t_arg);
5385 g_assert (!mono_domain_get ());
5387 * this is done by the pthread key dtor
5389 unregister_current_thread ();
5397 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5399 SgenThreadStartInfo *start_info;
5402 start_info = malloc (sizeof (SgenThreadStartInfo));
5405 MONO_SEM_INIT (&(start_info->registered), 0);
5406 start_info->arg = arg;
5407 start_info->start_routine = start_routine;
5409 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
5411 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
5412 /*if (EINTR != errno) ABORT("sem_wait failed"); */
5415 MONO_SEM_DESTROY (&(start_info->registered));
5421 mono_gc_pthread_join (pthread_t thread, void **retval)
5423 return pthread_join (thread, retval);
5427 mono_gc_pthread_detach (pthread_t thread)
5429 return pthread_detach (thread);
5432 #endif /* USE_PTHREAD_INTERCEPT */
5435 * ######################################################################
5436 * ######## Write barriers
5437 * ######################################################################
5441 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
5444 dummy_use (gpointer v) {
5445 __asm__ volatile ("" : "=r"(v) : "r"(v));
5449 static RememberedSet*
5450 alloc_remset (int size, gpointer id) {
5451 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
5452 res->store_next = res->data;
5453 res->end_set = res->data + size;
5455 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
5460 * Note: the write barriers first do the needed GC work and then do the actual store:
5461 * this way the value is visible to the conservative GC scan after the write barrier
5462 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
5463 * the conservative scan, otherwise by the remembered set scan.
5466 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
5468 HEAVY_STAT (++stat_wbarrier_set_field);
5469 if (ptr_in_nursery (field_ptr)) {
5470 *(void**)field_ptr = value;
5473 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
5474 if (use_cardtable) {
5475 *(void**)field_ptr = value;
5476 if (ptr_in_nursery (value))
5477 sgen_card_table_mark_address ((mword)field_ptr);
5484 rs = REMEMBERED_SET;
5485 if (rs->store_next < rs->end_set) {
5486 *(rs->store_next++) = (mword)field_ptr;
5487 *(void**)field_ptr = value;
5491 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5492 rs->next = REMEMBERED_SET;
5493 REMEMBERED_SET = rs;
5494 #ifdef HAVE_KW_THREAD
5495 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5497 *(rs->store_next++) = (mword)field_ptr;
5498 *(void**)field_ptr = value;
5504 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
5506 HEAVY_STAT (++stat_wbarrier_set_arrayref);
5507 if (ptr_in_nursery (slot_ptr)) {
5508 *(void**)slot_ptr = value;
5511 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
5512 if (use_cardtable) {
5513 *(void**)slot_ptr = value;
5514 if (ptr_in_nursery (value))
5515 sgen_card_table_mark_address ((mword)slot_ptr);
5522 rs = REMEMBERED_SET;
5523 if (rs->store_next < rs->end_set) {
5524 *(rs->store_next++) = (mword)slot_ptr;
5525 *(void**)slot_ptr = value;
5529 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5530 rs->next = REMEMBERED_SET;
5531 REMEMBERED_SET = rs;
5532 #ifdef HAVE_KW_THREAD
5533 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5535 *(rs->store_next++) = (mword)slot_ptr;
5536 *(void**)slot_ptr = value;
5542 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
5544 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
5545 /*This check can be done without taking a lock since dest_ptr array is pinned*/
5546 if (ptr_in_nursery (dest_ptr) || count <= 0) {
5547 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5551 if (use_cardtable) {
5552 gpointer *dest = dest_ptr;
5553 gpointer *src = src_ptr;
5555 /*overlapping that required backward copying*/
5556 if (src < dest && (src + count) > dest) {
5557 gpointer *start = dest;
5561 for (; dest >= start; --src, --dest) {
5562 gpointer value = *src;
5564 if (ptr_in_nursery (value))
5565 sgen_card_table_mark_address ((mword)dest);
5569 gpointer *end = dest + count;
5570 for (; dest < end; ++src, ++dest) {
5571 gpointer value = *src;
5573 if (ptr_in_nursery (value))
5574 sgen_card_table_mark_address ((mword)dest);
5582 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5584 rs = REMEMBERED_SET;
5585 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
5586 if (rs->store_next + 1 < rs->end_set) {
5587 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5588 *(rs->store_next++) = count;
5592 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5593 rs->next = REMEMBERED_SET;
5594 REMEMBERED_SET = rs;
5595 #ifdef HAVE_KW_THREAD
5596 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5598 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5599 *(rs->store_next++) = count;
5605 static char *found_obj;
5608 find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
5610 if (ptr >= obj && ptr < obj + size) {
5611 g_assert (!found_obj);
5616 /* for use in the debugger */
5617 char* find_object_for_ptr (char *ptr);
5619 find_object_for_ptr (char *ptr)
5623 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
5625 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
5626 (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
5631 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
5632 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
5633 return bigobj->data;
5637 * Very inefficient, but this is debugging code, supposed to
5638 * be called from gdb, so we don't care.
5641 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
5646 evacuate_remset_buffer (void)
5651 buffer = STORE_REMSET_BUFFER;
5653 add_generic_store_remset_from_buffer (buffer);
5654 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5656 STORE_REMSET_BUFFER_INDEX = 0;
5660 mono_gc_wbarrier_generic_nostore (gpointer ptr)
5666 HEAVY_STAT (++stat_wbarrier_generic_store);
5668 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
5669 /* FIXME: ptr_in_heap must be called with the GC lock held */
5670 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
5671 char *start = find_object_for_ptr (ptr);
5672 MonoObject *value = *(MonoObject**)ptr;
5676 MonoObject *obj = (MonoObject*)start;
5677 if (obj->vtable->domain != value->vtable->domain)
5678 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
5684 if (*(gpointer*)ptr)
5685 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
5687 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
5688 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
5692 if (use_cardtable) {
5693 if (ptr_in_nursery(*(gpointer*)ptr))
5694 sgen_card_table_mark_address ((mword)ptr);
5700 buffer = STORE_REMSET_BUFFER;
5701 index = STORE_REMSET_BUFFER_INDEX;
5702 /* This simple optimization eliminates a sizable portion of
5703 entries. Comparing it to the last but one entry as well
5704 doesn't eliminate significantly more entries. */
5705 if (buffer [index] == ptr) {
5710 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
5711 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
5714 if (index >= STORE_REMSET_BUFFER_SIZE) {
5715 evacuate_remset_buffer ();
5716 index = STORE_REMSET_BUFFER_INDEX;
5717 g_assert (index == 0);
5720 buffer [index] = ptr;
5721 STORE_REMSET_BUFFER_INDEX = index;
5727 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
5729 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
5730 *(void**)ptr = value;
5731 if (ptr_in_nursery (value))
5732 mono_gc_wbarrier_generic_nostore (ptr);
5736 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
5738 mword *dest = _dest;
5743 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
5748 size -= SIZEOF_VOID_P;
5755 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
5758 size_t size = count * mono_class_value_size (klass, NULL);
5760 HEAVY_STAT (++stat_wbarrier_value_copy);
5761 g_assert (klass->valuetype);
5763 memmove (dest, src, size);
5764 if (use_cardtable) {
5765 sgen_card_table_mark_range ((mword)dest, size);
5767 rs = REMEMBERED_SET;
5768 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !klass->has_references) {
5772 g_assert (klass->gc_descr_inited);
5773 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));
5775 if (rs->store_next + 3 < rs->end_set) {
5776 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5777 *(rs->store_next++) = (mword)klass->gc_descr;
5778 *(rs->store_next++) = (mword)count;
5782 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5783 rs->next = REMEMBERED_SET;
5784 REMEMBERED_SET = rs;
5785 #ifdef HAVE_KW_THREAD
5786 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5788 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5789 *(rs->store_next++) = (mword)klass->gc_descr;
5790 *(rs->store_next++) = (mword)count;
5796 * mono_gc_wbarrier_object_copy:
5798 * Write barrier to call when obj is the result of a clone or copy of an object.
5801 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
5807 HEAVY_STAT (++stat_wbarrier_object_copy);
5808 rs = REMEMBERED_SET;
5809 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
5810 size = mono_object_class (obj)->instance_size;
5812 /* do not copy the sync state */
5813 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
5814 size - sizeof (MonoObject));
5815 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
5819 if (rs->store_next < rs->end_set) {
5820 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5824 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5825 rs->next = REMEMBERED_SET;
5826 REMEMBERED_SET = rs;
5827 #ifdef HAVE_KW_THREAD
5828 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5830 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5835 * ######################################################################
5836 * ######## Collector debugging
5837 * ######################################################################
5840 const char*descriptor_types [] = {
5852 describe_ptr (char *ptr)
5858 if (ptr_in_nursery (ptr)) {
5859 printf ("Pointer inside nursery.\n");
5861 if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
5862 printf ("Pointer inside oldspace.\n");
5863 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
5864 printf ("Pointer is inside a pinned chunk.\n");
5866 printf ("Pointer unknown.\n");
5871 if (object_is_pinned (ptr))
5872 printf ("Object is pinned.\n");
5874 if (object_is_forwarded (ptr))
5875 printf ("Object is forwared.\n");
5877 // FIXME: Handle pointers to the inside of objects
5878 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
5880 printf ("VTable: %p\n", vtable);
5881 if (vtable == NULL) {
5882 printf ("VTable is invalid (empty).\n");
5885 if (ptr_in_nursery (vtable)) {
5886 printf ("VTable is invalid (points inside nursery).\n");
5889 printf ("Class: %s\n", vtable->klass->name);
5891 desc = ((GCVTable*)vtable)->desc;
5892 printf ("Descriptor: %lx\n", (long)desc);
5895 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
5899 find_in_remset_loc (mword *p, char *addr, gboolean *found)
5905 switch ((*p) & REMSET_TYPE_MASK) {
5906 case REMSET_LOCATION:
5907 if (*p == (mword)addr)
5911 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5913 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5917 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5918 count = safe_object_get_size ((MonoObject*)ptr);
5919 count = ALIGN_UP (count);
5920 count /= sizeof (mword);
5921 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5925 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5929 switch (desc & 0x7) {
5930 case DESC_TYPE_RUN_LENGTH:
5931 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
5933 case DESC_TYPE_SMALL_BITMAP:
5934 OBJ_BITMAP_SIZE (skip_size, desc, start);
5938 g_assert_not_reached ();
5941 /* The descriptor includes the size of MonoObject */
5942 skip_size -= sizeof (MonoObject);
5944 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
5949 g_assert_not_reached ();
5955 * Return whenever ADDR occurs in the remembered sets
5958 find_in_remsets (char *addr)
5961 SgenThreadInfo *info;
5962 RememberedSet *remset;
5963 GenericStoreRememberedSet *store_remset;
5965 gboolean found = FALSE;
5967 /* the global one */
5968 for (remset = global_remset; remset; remset = remset->next) {
5969 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));
5970 for (p = remset->data; p < remset->store_next;) {
5971 p = find_in_remset_loc (p, addr, &found);
5977 /* the generic store ones */
5978 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
5979 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5980 if (store_remset->data [i] == addr)
5985 /* the per-thread ones */
5986 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5987 for (info = thread_table [i]; info; info = info->next) {
5989 for (remset = info->remset; remset; remset = remset->next) {
5990 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));
5991 for (p = remset->data; p < remset->store_next;) {
5992 p = find_in_remset_loc (p, addr, &found);
5997 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
5998 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6004 /* the freed thread ones */
6005 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6006 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));
6007 for (p = remset->data; p < remset->store_next;) {
6008 p = find_in_remset_loc (p, addr, &found);
6017 static gboolean missing_remsets;
6020 * We let a missing remset slide if the target object is pinned,
6021 * because the store might have happened but the remset not yet added,
6022 * but in that case the target must be pinned. We might theoretically
6023 * miss some missing remsets this way, but it's very unlikely.
6026 #define HANDLE_PTR(ptr,obj) do { \
6027 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6028 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6029 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); \
6030 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6031 if (!object_is_pinned (*(ptr))) \
6032 missing_remsets = TRUE; \
6038 * Check that each object reference which points into the nursery can
6039 * be found in the remembered sets.
6042 check_consistency_callback (char *start, size_t size, void *dummy)
6044 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6045 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6047 #define SCAN_OBJECT_ACTION
6048 #include "sgen-scan-object.h"
6052 * Perform consistency check of the heap.
6054 * Assumes the world is stopped.
6057 check_consistency (void)
6061 // Need to add more checks
6063 missing_remsets = FALSE;
6065 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6067 // Check that oldspace->newspace pointers are registered with the collector
6068 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6070 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6071 check_consistency_callback (bigobj->data, bigobj->size, NULL);
6073 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6075 #ifdef SGEN_BINARY_PROTOCOL
6076 if (!binary_protocol_file)
6078 g_assert (!missing_remsets);
6083 #define HANDLE_PTR(ptr,obj) do { \
6084 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6085 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6089 check_major_refs_callback (char *start, size_t size, void *dummy)
6091 #define SCAN_OBJECT_ACTION
6092 #include "sgen-scan-object.h"
6096 check_major_refs (void)
6100 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6102 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6103 check_major_refs_callback (bigobj->data, bigobj->size, NULL);
6106 /* Check that the reference is valid */
6108 #define HANDLE_PTR(ptr,obj) do { \
6110 g_assert (safe_name (*(ptr)) != NULL); \
6117 * Perform consistency check on an object. Currently we only check that the
6118 * reference fields are valid.
6121 check_object (char *start)
6126 #include "sgen-scan-object.h"
6130 * ######################################################################
6131 * ######## Other mono public interface functions.
6132 * ######################################################################
6136 mono_gc_collect (int generation)
6141 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6142 stop_world (generation);
6143 if (generation == 0) {
6144 collect_nursery (0);
6146 major_collection ("user request");
6148 restart_world (generation);
6149 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6154 mono_gc_max_generation (void)
6160 mono_gc_collection_count (int generation)
6162 if (generation == 0)
6163 return num_minor_gcs;
6164 return num_major_gcs;
6168 mono_gc_get_used_size (void)
6172 tot = los_memory_usage;
6173 tot += nursery_section->next_data - nursery_section->data;
6174 tot += major_collector.get_used_size ();
6175 /* FIXME: account for pinned objects */
6181 mono_gc_get_heap_size (void)
6187 mono_gc_disable (void)
6195 mono_gc_enable (void)
6203 mono_gc_get_los_limit (void)
6205 return MAX_SMALL_OBJ_SIZE;
6209 mono_object_is_alive (MonoObject* o)
6215 mono_gc_get_generation (MonoObject *obj)
6217 if (ptr_in_nursery (obj))
6223 mono_gc_enable_events (void)
6228 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6231 mono_gc_register_disappearing_link (obj, link_addr, track);
6236 mono_gc_weak_link_remove (void **link_addr)
6239 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
6244 mono_gc_weak_link_get (void **link_addr)
6248 return (MonoObject*) REVEAL_POINTER (*link_addr);
6252 mono_gc_ephemeron_array_add (MonoObject *obj)
6254 EphemeronLinkNode *node;
6258 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
6263 node->array = (char*)obj;
6264 node->next = ephemeron_list;
6265 ephemeron_list = node;
6267 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6274 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6277 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
6278 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6279 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6281 mword complex = alloc_complex_descriptor (bitmap, numbits);
6282 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6286 static void *all_ref_root_descrs [32];
6289 mono_gc_make_root_descr_all_refs (int numbits)
6294 if (numbits < 32 && all_ref_root_descrs [numbits])
6295 return all_ref_root_descrs [numbits];
6297 gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
6298 memset (gc_bitmap, 0xff, numbits / 8);
6300 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
6301 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
6305 all_ref_root_descrs [numbits] = descr;
6311 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6315 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6316 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6317 user_descriptors [user_descriptors_next ++] = marker;
6323 mono_gc_alloc_fixed (size_t size, void *descr)
6325 /* FIXME: do a single allocation */
6326 void *res = calloc (1, size);
6329 if (!mono_gc_register_root (res, size, descr)) {
6337 mono_gc_free_fixed (void* addr)
6339 mono_gc_deregister_root (addr);
6344 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6348 result = func (data);
6349 UNLOCK_INTERRUPTION;
6354 mono_gc_is_gc_thread (void)
6358 result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
6363 /* Tries to extract a number from the passed string, taking in to account m, k
6366 mono_sgen_parse_environment_string_extract_number (const char *str, glong *out)
6369 int len = strlen (str), shift = 0;
6371 gboolean is_suffix = FALSE;
6374 switch (str [len - 1]) {
6385 suffix = str [len - 1];
6390 val = strtol (str, &endptr, 10);
6392 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
6393 || (errno != 0 && val == 0) || (endptr == str))
6397 if (*(endptr + 1)) /* Invalid string. */
6407 mono_gc_base_init (void)
6411 char *major_collector_opt = NULL;
6412 struct sigaction sinfo;
6414 /* the gc_initialized guard seems to imply this method is
6415 idempotent, but LOCK_INIT(gc_mutex) might not be. It's
6416 defined in sgen-gc.h as nothing, so there's no danger at
6418 LOCK_INIT (gc_mutex);
6420 if (gc_initialized) {
6424 pagesize = mono_pagesize ();
6425 gc_debug_file = stderr;
6427 LOCK_INIT (interruption_mutex);
6428 LOCK_INIT (global_remset_mutex);
6430 if ((env = getenv ("MONO_GC_PARAMS"))) {
6431 opts = g_strsplit (env, ",", -1);
6432 for (ptr = opts; *ptr; ++ptr) {
6434 if (g_str_has_prefix (opt, "major=")) {
6435 opt = strchr (opt, '=') + 1;
6436 major_collector_opt = g_strdup (opt);
6444 mono_sgen_init_internal_allocator ();
6446 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
6447 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
6448 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
6449 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
6450 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
6451 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
6452 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6453 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
6454 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
6456 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
6457 mono_sgen_marksweep_init (&major_collector);
6458 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
6459 mono_sgen_marksweep_fixed_init (&major_collector);
6460 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
6461 mono_sgen_marksweep_par_init (&major_collector);
6462 workers_init (mono_cpu_count ());
6463 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
6464 mono_sgen_marksweep_fixed_par_init (&major_collector);
6465 workers_init (mono_cpu_count ());
6466 } else if (!strcmp (major_collector_opt, "copying")) {
6467 mono_sgen_copying_init (&major_collector);
6469 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
6473 #ifdef SGEN_HAVE_CARDTABLE
6474 use_cardtable = major_collector.supports_cardtable;
6476 use_cardtable = FALSE;
6480 for (ptr = opts; *ptr; ++ptr) {
6482 if (g_str_has_prefix (opt, "major="))
6484 if (g_str_has_prefix (opt, "wbarrier=")) {
6485 opt = strchr (opt, '=') + 1;
6486 if (strcmp (opt, "remset") == 0) {
6487 use_cardtable = FALSE;
6488 } else if (strcmp (opt, "cardtable") == 0) {
6489 if (!use_cardtable) {
6490 if (major_collector.supports_cardtable)
6491 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
6493 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
6500 if (g_str_has_prefix (opt, "nursery-size=")) {
6502 opt = strchr (opt, '=') + 1;
6503 if (*opt && mono_sgen_parse_environment_string_extract_number (opt, &val)) {
6504 default_nursery_size = val;
6505 #ifdef SGEN_ALIGN_NURSERY
6506 if ((val & (val - 1))) {
6507 fprintf (stderr, "The nursery size must be a power of two.\n");
6511 default_nursery_bits = 0;
6512 while (1 << (++ default_nursery_bits) != default_nursery_size)
6516 fprintf (stderr, "nursery-size must be an integer.\n");
6522 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
6523 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
6524 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6525 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
6526 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
6527 if (major_collector.print_gc_param_usage)
6528 major_collector.print_gc_param_usage ();
6535 if (major_collector_opt)
6536 g_free (major_collector_opt);
6538 nursery_size = DEFAULT_NURSERY_SIZE;
6539 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
6543 if ((env = getenv ("MONO_GC_DEBUG"))) {
6544 opts = g_strsplit (env, ",", -1);
6545 for (ptr = opts; ptr && *ptr; ptr ++) {
6547 if (opt [0] >= '0' && opt [0] <= '9') {
6548 gc_debug_level = atoi (opt);
6553 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
6554 gc_debug_file = fopen (rf, "wb");
6556 gc_debug_file = stderr;
6559 } else if (!strcmp (opt, "collect-before-allocs")) {
6560 collect_before_allocs = TRUE;
6561 } else if (!strcmp (opt, "check-at-minor-collections")) {
6562 consistency_check_at_minor_collection = TRUE;
6563 nursery_clear_policy = CLEAR_AT_GC;
6564 } else if (!strcmp (opt, "xdomain-checks")) {
6565 xdomain_checks = TRUE;
6566 } else if (!strcmp (opt, "clear-at-gc")) {
6567 nursery_clear_policy = CLEAR_AT_GC;
6568 } else if (!strcmp (opt, "conservative-stack-mark")) {
6569 conservative_stack_mark = TRUE;
6570 } else if (!strcmp (opt, "check-scan-starts")) {
6571 do_scan_starts_check = TRUE;
6572 } else if (g_str_has_prefix (opt, "heap-dump=")) {
6573 char *filename = strchr (opt, '=') + 1;
6574 nursery_clear_policy = CLEAR_AT_GC;
6575 heap_dump_file = fopen (filename, "w");
6577 fprintf (heap_dump_file, "<sgen-dump>\n");
6578 #ifdef SGEN_BINARY_PROTOCOL
6579 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
6580 char *filename = strchr (opt, '=') + 1;
6581 binary_protocol_file = fopen (filename, "w");
6584 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
6585 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
6586 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
6593 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
6594 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
6596 sigfillset (&sinfo.sa_mask);
6597 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
6598 sinfo.sa_sigaction = suspend_handler;
6599 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
6600 g_error ("failed sigaction");
6603 sinfo.sa_handler = restart_handler;
6604 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
6605 g_error ("failed sigaction");
6608 sigfillset (&suspend_signal_mask);
6609 sigdelset (&suspend_signal_mask, restart_signal_num);
6611 global_remset = alloc_remset (1024, NULL);
6612 global_remset->next = NULL;
6614 pthread_key_create (&remembered_set_key, unregister_thread);
6616 #ifndef HAVE_KW_THREAD
6617 pthread_key_create (&thread_info_key, NULL);
6623 gc_initialized = TRUE;
6625 mono_gc_register_thread (&sinfo);
6629 mono_gc_get_suspend_signal (void)
6631 return suspend_signal_num;
6641 #ifdef HAVE_KW_THREAD
6642 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
6643 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6644 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6645 mono_mb_emit_i4 ((mb), (offset)); \
6648 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
6649 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6650 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6651 mono_mb_emit_i4 ((mb), thread_info_key); \
6652 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
6653 mono_mb_emit_byte ((mb), CEE_ADD); \
6654 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
6658 #ifdef MANAGED_ALLOCATION
6659 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
6660 * for each class. This is currently not easy to do, as it is hard to generate basic
6661 * blocks + branches, but it is easy with the linear IL codebase.
6663 * For this to work we'd need to solve the TLAB race, first. Now we
6664 * require the allocator to be in a few known methods to make sure
6665 * that they are executed atomically via the restart mechanism.
6668 create_allocator (int atype)
6670 int p_var, size_var;
6671 guint32 slowpath_branch, max_size_branch;
6672 MonoMethodBuilder *mb;
6674 MonoMethodSignature *csig;
6675 static gboolean registered = FALSE;
6676 int tlab_next_addr_var, new_next_var;
6678 const char *name = NULL;
6679 AllocatorWrapperInfo *info;
6681 #ifdef HAVE_KW_THREAD
6682 int tlab_next_addr_offset = -1;
6683 int tlab_temp_end_offset = -1;
6685 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
6686 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6688 g_assert (tlab_next_addr_offset != -1);
6689 g_assert (tlab_temp_end_offset != -1);
6693 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
6694 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
6698 if (atype == ATYPE_SMALL) {
6700 name = "AllocSmall";
6701 } else if (atype == ATYPE_NORMAL) {
6704 } else if (atype == ATYPE_VECTOR) {
6706 name = "AllocVector";
6708 g_assert_not_reached ();
6711 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
6712 csig->ret = &mono_defaults.object_class->byval_arg;
6713 for (i = 0; i < num_params; ++i)
6714 csig->params [i] = &mono_defaults.int_class->byval_arg;
6716 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
6717 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
6718 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6719 /* size = vtable->klass->instance_size; */
6720 mono_mb_emit_ldarg (mb, 0);
6721 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6722 mono_mb_emit_byte (mb, CEE_ADD);
6723 mono_mb_emit_byte (mb, CEE_LDIND_I);
6724 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
6725 mono_mb_emit_byte (mb, CEE_ADD);
6726 /* FIXME: assert instance_size stays a 4 byte integer */
6727 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6728 mono_mb_emit_stloc (mb, size_var);
6729 } else if (atype == ATYPE_VECTOR) {
6730 MonoExceptionClause *clause;
6732 MonoClass *oom_exc_class;
6735 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
6736 mono_mb_emit_ldarg (mb, 1);
6737 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
6738 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
6739 mono_mb_emit_exception (mb, "OverflowException", NULL);
6740 mono_mb_patch_short_branch (mb, pos);
6742 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
6743 clause->try_offset = mono_mb_get_label (mb);
6745 /* vtable->klass->sizes.element_size */
6746 mono_mb_emit_ldarg (mb, 0);
6747 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6748 mono_mb_emit_byte (mb, CEE_ADD);
6749 mono_mb_emit_byte (mb, CEE_LDIND_I);
6750 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
6751 mono_mb_emit_byte (mb, CEE_ADD);
6752 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6755 mono_mb_emit_ldarg (mb, 1);
6756 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
6757 /* + sizeof (MonoArray) */
6758 mono_mb_emit_icon (mb, sizeof (MonoArray));
6759 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
6760 mono_mb_emit_stloc (mb, size_var);
6762 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
6765 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
6766 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
6767 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
6768 "System", "OverflowException");
6769 g_assert (clause->data.catch_class);
6770 clause->handler_offset = mono_mb_get_label (mb);
6772 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
6773 "System", "OutOfMemoryException");
6774 g_assert (oom_exc_class);
6775 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
6778 mono_mb_emit_byte (mb, CEE_POP);
6779 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
6780 mono_mb_emit_byte (mb, CEE_THROW);
6782 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
6783 mono_mb_set_clauses (mb, 1, clause);
6784 mono_mb_patch_branch (mb, pos_leave);
6787 g_assert_not_reached ();
6790 /* size += ALLOC_ALIGN - 1; */
6791 mono_mb_emit_ldloc (mb, size_var);
6792 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
6793 mono_mb_emit_byte (mb, CEE_ADD);
6794 /* size &= ~(ALLOC_ALIGN - 1); */
6795 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
6796 mono_mb_emit_byte (mb, CEE_AND);
6797 mono_mb_emit_stloc (mb, size_var);
6799 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
6800 if (atype != ATYPE_SMALL) {
6801 mono_mb_emit_ldloc (mb, size_var);
6802 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
6803 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
6807 * We need to modify tlab_next, but the JIT only supports reading, so we read
6808 * another tls var holding its address instead.
6811 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
6812 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6813 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
6814 mono_mb_emit_stloc (mb, tlab_next_addr_var);
6816 /* p = (void**)tlab_next; */
6817 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6818 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
6819 mono_mb_emit_byte (mb, CEE_LDIND_I);
6820 mono_mb_emit_stloc (mb, p_var);
6822 /* new_next = (char*)p + size; */
6823 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6824 mono_mb_emit_ldloc (mb, p_var);
6825 mono_mb_emit_ldloc (mb, size_var);
6826 mono_mb_emit_byte (mb, CEE_CONV_I);
6827 mono_mb_emit_byte (mb, CEE_ADD);
6828 mono_mb_emit_stloc (mb, new_next_var);
6830 /* tlab_next = new_next */
6831 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
6832 mono_mb_emit_ldloc (mb, new_next_var);
6833 mono_mb_emit_byte (mb, CEE_STIND_I);
6835 /* if (G_LIKELY (new_next < tlab_temp_end)) */
6836 mono_mb_emit_ldloc (mb, new_next_var);
6837 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
6838 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
6841 if (atype != ATYPE_SMALL)
6842 mono_mb_patch_short_branch (mb, max_size_branch);
6844 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
6845 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
6847 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
6848 mono_mb_emit_ldarg (mb, 0);
6849 mono_mb_emit_ldloc (mb, size_var);
6850 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6851 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
6852 } else if (atype == ATYPE_VECTOR) {
6853 mono_mb_emit_ldarg (mb, 1);
6854 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
6856 g_assert_not_reached ();
6858 mono_mb_emit_byte (mb, CEE_RET);
6861 mono_mb_patch_short_branch (mb, slowpath_branch);
6863 /* FIXME: Memory barrier */
6866 mono_mb_emit_ldloc (mb, p_var);
6867 mono_mb_emit_ldarg (mb, 0);
6868 mono_mb_emit_byte (mb, CEE_STIND_I);
6870 if (atype == ATYPE_VECTOR) {
6871 /* arr->max_length = max_length; */
6872 mono_mb_emit_ldloc (mb, p_var);
6873 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
6874 mono_mb_emit_ldarg (mb, 1);
6875 mono_mb_emit_byte (mb, CEE_STIND_I);
6879 mono_mb_emit_ldloc (mb, p_var);
6880 mono_mb_emit_byte (mb, CEE_RET);
6882 res = mono_mb_create_method (mb, csig, 8);
6884 mono_method_get_header (res)->init_locals = FALSE;
6886 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
6887 info->gc_name = "sgen";
6888 info->alloc_type = atype;
6889 mono_marshal_set_wrapper_info (res, info);
6896 mono_gc_get_gc_name (void)
6901 static MonoMethod* alloc_method_cache [ATYPE_NUM];
6902 static MonoMethod *write_barrier_method;
6905 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
6911 if (!mono_thread_internal_current ())
6912 /* Happens during thread attach */
6917 ji = mono_jit_info_table_find (domain, ip);
6920 method = ji->method;
6922 if (method == write_barrier_method)
6924 for (i = 0; i < ATYPE_NUM; ++i)
6925 if (method == alloc_method_cache [i])
6931 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
6932 * The signature of the called method is:
6933 * object allocate (MonoVTable *vtable)
6936 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
6938 #ifdef MANAGED_ALLOCATION
6939 MonoClass *klass = vtable->klass;
6941 #ifdef HAVE_KW_THREAD
6942 int tlab_next_offset = -1;
6943 int tlab_temp_end_offset = -1;
6944 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
6945 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6947 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
6951 if (!mono_runtime_has_tls_get ())
6953 if (klass->instance_size > tlab_size)
6955 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
6959 if (klass->byval_arg.type == MONO_TYPE_STRING)
6961 if (collect_before_allocs)
6964 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
6965 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
6967 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
6974 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
6976 #ifdef MANAGED_ALLOCATION
6977 MonoClass *klass = vtable->klass;
6979 #ifdef HAVE_KW_THREAD
6980 int tlab_next_offset = -1;
6981 int tlab_temp_end_offset = -1;
6982 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
6983 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6985 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
6991 if (!mono_runtime_has_tls_get ())
6993 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
6995 if (collect_before_allocs)
6997 g_assert (!klass->has_finalize && !klass->marshalbyref);
6999 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7006 mono_gc_get_managed_allocator_by_type (int atype)
7008 #ifdef MANAGED_ALLOCATION
7011 if (!mono_runtime_has_tls_get ())
7014 mono_loader_lock ();
7015 res = alloc_method_cache [atype];
7017 res = alloc_method_cache [atype] = create_allocator (atype);
7018 mono_loader_unlock ();
7026 mono_gc_get_managed_allocator_types (void)
7033 mono_gc_get_write_barrier (void)
7036 MonoMethodBuilder *mb;
7037 MonoMethodSignature *sig;
7038 #ifdef MANAGED_WBARRIER
7039 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7040 #ifndef SGEN_ALIGN_NURSERY
7041 int label_continue_1, label_continue_2, label_no_wb_5;
7042 int dereferenced_var;
7044 int buffer_var, buffer_index_var, dummy_var;
7046 #ifdef HAVE_KW_THREAD
7047 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7048 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7050 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7051 g_assert (stack_end_offset != -1);
7052 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7053 g_assert (store_remset_buffer_offset != -1);
7054 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7055 g_assert (store_remset_buffer_index_offset != -1);
7056 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7057 g_assert (store_remset_buffer_index_addr_offset != -1);
7061 g_assert (!use_cardtable);
7063 // FIXME: Maybe create a separate version for ctors (the branch would be
7064 // correctly predicted more times)
7065 if (write_barrier_method)
7066 return write_barrier_method;
7068 /* Create the IL version of mono_gc_barrier_generic_store () */
7069 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7070 sig->ret = &mono_defaults.void_class->byval_arg;
7071 sig->params [0] = &mono_defaults.int_class->byval_arg;
7073 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7075 #ifdef MANAGED_WBARRIER
7076 if (mono_runtime_has_tls_get ()) {
7077 #ifdef SGEN_ALIGN_NURSERY
7078 // if (ptr_in_nursery (ptr)) return;
7080 * Masking out the bits might be faster, but we would have to use 64 bit
7081 * immediates, which might be slower.
7083 mono_mb_emit_ldarg (mb, 0);
7084 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7085 mono_mb_emit_byte (mb, CEE_SHR_UN);
7086 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7087 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7089 // if (!ptr_in_nursery (*ptr)) return;
7090 mono_mb_emit_ldarg (mb, 0);
7091 mono_mb_emit_byte (mb, CEE_LDIND_I);
7092 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7093 mono_mb_emit_byte (mb, CEE_SHR_UN);
7094 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7095 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7098 // if (ptr < (nursery_start)) goto continue;
7099 mono_mb_emit_ldarg (mb, 0);
7100 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7101 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7103 // if (ptr >= nursery_real_end)) goto continue;
7104 mono_mb_emit_ldarg (mb, 0);
7105 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7106 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7109 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7112 mono_mb_patch_branch (mb, label_continue_1);
7113 mono_mb_patch_branch (mb, label_continue_2);
7115 // Dereference and store in local var
7116 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7117 mono_mb_emit_ldarg (mb, 0);
7118 mono_mb_emit_byte (mb, CEE_LDIND_I);
7119 mono_mb_emit_stloc (mb, dereferenced_var);
7121 // if (*ptr < nursery_start) return;
7122 mono_mb_emit_ldloc (mb, dereferenced_var);
7123 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7124 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7126 // if (*ptr >= nursery_end) return;
7127 mono_mb_emit_ldloc (mb, dereferenced_var);
7128 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7129 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7132 // if (ptr >= stack_end) goto need_wb;
7133 mono_mb_emit_ldarg (mb, 0);
7134 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7135 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7137 // if (ptr >= stack_start) return;
7138 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7139 mono_mb_emit_ldarg (mb, 0);
7140 mono_mb_emit_ldloc_addr (mb, dummy_var);
7141 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7144 mono_mb_patch_branch (mb, label_need_wb);
7146 // buffer = STORE_REMSET_BUFFER;
7147 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7148 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7149 mono_mb_emit_stloc (mb, buffer_var);
7151 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7152 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7153 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7154 mono_mb_emit_stloc (mb, buffer_index_var);
7156 // if (buffer [buffer_index] == ptr) return;
7157 mono_mb_emit_ldloc (mb, buffer_var);
7158 mono_mb_emit_ldloc (mb, buffer_index_var);
7159 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7160 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7161 mono_mb_emit_byte (mb, CEE_SHL);
7162 mono_mb_emit_byte (mb, CEE_ADD);
7163 mono_mb_emit_byte (mb, CEE_LDIND_I);
7164 mono_mb_emit_ldarg (mb, 0);
7165 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7168 mono_mb_emit_ldloc (mb, buffer_index_var);
7169 mono_mb_emit_icon (mb, 1);
7170 mono_mb_emit_byte (mb, CEE_ADD);
7171 mono_mb_emit_stloc (mb, buffer_index_var);
7173 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7174 mono_mb_emit_ldloc (mb, buffer_index_var);
7175 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7176 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7178 // buffer [buffer_index] = ptr;
7179 mono_mb_emit_ldloc (mb, buffer_var);
7180 mono_mb_emit_ldloc (mb, buffer_index_var);
7181 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7182 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7183 mono_mb_emit_byte (mb, CEE_SHL);
7184 mono_mb_emit_byte (mb, CEE_ADD);
7185 mono_mb_emit_ldarg (mb, 0);
7186 mono_mb_emit_byte (mb, CEE_STIND_I);
7188 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7189 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7190 mono_mb_emit_ldloc (mb, buffer_index_var);
7191 mono_mb_emit_byte (mb, CEE_STIND_I);
7194 mono_mb_patch_branch (mb, label_no_wb_1);
7195 mono_mb_patch_branch (mb, label_no_wb_2);
7196 mono_mb_patch_branch (mb, label_no_wb_3);
7197 mono_mb_patch_branch (mb, label_no_wb_4);
7198 #ifndef SGEN_ALIGN_NURSERY
7199 mono_mb_patch_branch (mb, label_no_wb_5);
7201 mono_mb_emit_byte (mb, CEE_RET);
7204 mono_mb_patch_branch (mb, label_slow_path);
7208 mono_mb_emit_ldarg (mb, 0);
7209 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7210 mono_mb_emit_byte (mb, CEE_RET);
7212 res = mono_mb_create_method (mb, sig, 16);
7215 mono_loader_lock ();
7216 if (write_barrier_method) {
7217 /* Already created */
7218 mono_free_method (res);
7220 /* double-checked locking */
7221 mono_memory_barrier ();
7222 write_barrier_method = res;
7224 mono_loader_unlock ();
7226 return write_barrier_method;
7230 mono_gc_get_description (void)
7232 return g_strdup ("sgen");
7236 mono_gc_set_desktop_mode (void)
7241 mono_gc_is_moving (void)
7247 mono_gc_is_disabled (void)
7253 mono_sgen_debug_printf (int level, const char *format, ...)
7257 if (level > gc_debug_level)
7260 va_start (ap, format);
7261 vfprintf (gc_debug_file, format, ap);
7265 #endif /* HAVE_SGEN_GC */