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-archdep.h"
203 #include "metadata/mono-gc.h"
204 #include "metadata/method-builder.h"
205 #include "metadata/profiler-private.h"
206 #include "metadata/monitor.h"
207 #include "metadata/threadpool-internals.h"
208 #include "metadata/mempool-internals.h"
209 #include "metadata/marshal.h"
210 #include "utils/mono-mmap.h"
211 #include "utils/mono-time.h"
212 #include "utils/mono-semaphore.h"
213 #include "utils/mono-counters.h"
214 #include "utils/mono-proclib.h"
216 #include <mono/utils/memcheck.h>
218 #if defined(__MACH__)
219 #include "utils/mach-support.h"
222 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
226 #include "mono/cil/opcode.def"
232 #undef pthread_create
234 #undef pthread_detach
237 * ######################################################################
238 * ######## Types and constants used by the GC.
239 * ######################################################################
242 static int gc_initialized = 0;
243 /* If set, do a minor collection before every allocation */
244 static gboolean collect_before_allocs = FALSE;
245 /* If set, do a heap consistency check before each minor collection */
246 static gboolean consistency_check_at_minor_collection = FALSE;
247 /* If set, check that there are no references to the domain left at domain unload */
248 static gboolean xdomain_checks = FALSE;
249 /* If not null, dump the heap after each collection into this file */
250 static FILE *heap_dump_file = NULL;
251 /* If set, mark stacks conservatively, even if precise marking is possible */
252 static gboolean conservative_stack_mark = TRUE;
253 /* If set, do a plausibility check on the scan_starts before and after
255 static gboolean do_scan_starts_check = FALSE;
257 #ifdef HEAVY_STATISTICS
258 static long long stat_objects_alloced = 0;
259 static long long stat_bytes_alloced = 0;
260 long long stat_objects_alloced_degraded = 0;
261 long long stat_bytes_alloced_degraded = 0;
262 static long long stat_bytes_alloced_los = 0;
264 long long stat_copy_object_called_nursery = 0;
265 long long stat_objects_copied_nursery = 0;
266 long long stat_copy_object_called_major = 0;
267 long long stat_objects_copied_major = 0;
269 long long stat_scan_object_called_nursery = 0;
270 long long stat_scan_object_called_major = 0;
272 long long stat_nursery_copy_object_failed_from_space = 0;
273 long long stat_nursery_copy_object_failed_forwarded = 0;
274 long long stat_nursery_copy_object_failed_pinned = 0;
276 static long long stat_store_remsets = 0;
277 static long long stat_store_remsets_unique = 0;
278 static long long stat_saved_remsets_1 = 0;
279 static long long stat_saved_remsets_2 = 0;
280 static long long stat_local_remsets_processed = 0;
281 static long long stat_global_remsets_added = 0;
282 static long long stat_global_remsets_readded = 0;
283 static long long stat_global_remsets_processed = 0;
284 static long long stat_global_remsets_discarded = 0;
286 static long long stat_wasted_fragments_used = 0;
287 static long long stat_wasted_fragments_bytes = 0;
289 static int stat_wbarrier_set_field = 0;
290 static int stat_wbarrier_set_arrayref = 0;
291 static int stat_wbarrier_arrayref_copy = 0;
292 static int stat_wbarrier_generic_store = 0;
293 static int stat_wbarrier_generic_store_remset = 0;
294 static int stat_wbarrier_set_root = 0;
295 static int stat_wbarrier_value_copy = 0;
296 static int stat_wbarrier_object_copy = 0;
299 static long long time_minor_pre_collection_fragment_clear = 0;
300 static long long time_minor_pinning = 0;
301 static long long time_minor_scan_remsets = 0;
302 static long long time_minor_scan_pinned = 0;
303 static long long time_minor_scan_registered_roots = 0;
304 static long long time_minor_scan_thread_data = 0;
305 static long long time_minor_finish_gray_stack = 0;
306 static long long time_minor_fragment_creation = 0;
308 static long long time_major_pre_collection_fragment_clear = 0;
309 static long long time_major_pinning = 0;
310 static long long time_major_scan_pinned = 0;
311 static long long time_major_scan_registered_roots = 0;
312 static long long time_major_scan_thread_data = 0;
313 static long long time_major_scan_alloc_pinned = 0;
314 static long long time_major_scan_finalized = 0;
315 static long long time_major_scan_big_objects = 0;
316 static long long time_major_finish_gray_stack = 0;
317 static long long time_major_free_bigobjs = 0;
318 static long long time_major_los_sweep = 0;
319 static long long time_major_sweep = 0;
320 static long long time_major_fragment_creation = 0;
322 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
324 static int gc_debug_level = 0;
325 static FILE* gc_debug_file;
329 mono_gc_flush_info (void)
331 fflush (gc_debug_file);
336 * Define this to allow the user to change the nursery size by
337 * specifying its value in the MONO_GC_PARAMS environmental
338 * variable. See mono_gc_base_init for details.
340 #define USER_CONFIG 1
342 #define TV_DECLARE(name) gint64 name
343 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
344 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
345 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
347 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
349 /* The method used to clear the nursery */
350 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
351 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
356 CLEAR_AT_TLAB_CREATION
357 } NurseryClearPolicy;
359 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
362 * The young generation is divided into fragments. This is because
363 * we can hand one fragments to a thread for lock-less fast alloc and
364 * because the young generation ends up fragmented anyway by pinned objects.
365 * Once a collection is done, a list of fragments is created. When doing
366 * thread local alloc we use smallish nurseries so we allow new threads to
367 * allocate memory from gen0 without triggering a collection. Threads that
368 * are found to allocate lots of memory are given bigger fragments. This
369 * should make the finalizer thread use little nursery memory after a while.
370 * We should start assigning threads very small fragments: if there are many
371 * threads the nursery will be full of reserved space that the threads may not
372 * use at all, slowing down allocation speed.
373 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
374 * Allocation Buffers (TLABs).
376 typedef struct _Fragment Fragment;
380 char *fragment_start;
381 char *fragment_limit; /* the current soft limit for allocation */
385 /* the runtime can register areas of memory as roots: we keep two lists of roots,
386 * a pinned root set for conservatively scanned roots and a normal one for
387 * precisely scanned roots (currently implemented as a single list).
389 typedef struct _RootRecord RootRecord;
398 * We're never actually using the first element. It's always set to
399 * NULL to simplify the elimination of consecutive duplicate
402 #define STORE_REMSET_BUFFER_SIZE 1024
404 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
405 struct _GenericStoreRememberedSet {
406 GenericStoreRememberedSet *next;
407 /* We need one entry less because the first entry of store
408 remset buffers is always a dummy and we don't copy it. */
409 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
412 /* we have 4 possible values in the low 2 bits */
414 REMSET_LOCATION, /* just a pointer to the exact location */
415 REMSET_RANGE, /* range of pointer fields */
416 REMSET_OBJECT, /* mark all the object for scanning */
417 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
418 REMSET_TYPE_MASK = 0x3
421 #ifdef HAVE_KW_THREAD
422 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
424 static pthread_key_t remembered_set_key;
425 static RememberedSet *global_remset;
426 static RememberedSet *freed_thread_remsets;
427 static GenericStoreRememberedSet *generic_store_remsets = NULL;
429 /*A two slots cache for recently inserted remsets */
430 static gpointer global_remset_cache [2];
432 /* FIXME: later choose a size that takes into account the RememberedSet struct
433 * and doesn't waste any alloc paddin space.
435 #define DEFAULT_REMSET_SIZE 1024
436 static RememberedSet* alloc_remset (int size, gpointer id);
438 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
439 #define object_is_pinned SGEN_OBJECT_IS_PINNED
440 #define pin_object SGEN_PIN_OBJECT
441 #define unpin_object SGEN_UNPIN_OBJECT
443 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_real_end))
445 #define LOAD_VTABLE SGEN_LOAD_VTABLE
448 safe_name (void* obj)
450 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
451 return vt->klass->name;
454 #define safe_object_get_size mono_sgen_safe_object_get_size
457 * ######################################################################
458 * ######## Global data.
459 * ######################################################################
461 static LOCK_DECLARE (gc_mutex);
462 static int gc_disabled = 0;
463 static int num_minor_gcs = 0;
464 static int num_major_gcs = 0;
466 static gboolean use_cardtable;
470 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
471 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
472 static int default_nursery_size = (1 << 22);
473 #ifdef SGEN_ALIGN_NURSERY
474 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
475 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
476 static int default_nursery_bits = 22;
481 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
482 #ifdef SGEN_ALIGN_NURSERY
483 #define DEFAULT_NURSERY_BITS 22
488 #ifndef SGEN_ALIGN_NURSERY
489 #define DEFAULT_NURSERY_BITS -1
492 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
494 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
496 /* the minimum size of a fragment that we consider useful for allocation */
497 #define FRAGMENT_MIN_SIZE (512)
499 static mword pagesize = 4096;
500 static mword nursery_size;
501 static int degraded_mode = 0;
503 static mword total_alloc = 0;
504 /* use this to tune when to do a major/minor collection */
505 static mword memory_pressure = 0;
506 static mword minor_collection_allowance;
507 static int minor_collection_sections_alloced = 0;
509 static GCMemSection *nursery_section = NULL;
510 static mword lowest_heap_address = ~(mword)0;
511 static mword highest_heap_address = 0;
513 static LOCK_DECLARE (interruption_mutex);
514 static LOCK_DECLARE (global_remset_mutex);
516 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
517 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
519 typedef struct _FinalizeEntry FinalizeEntry;
520 struct _FinalizeEntry {
525 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
526 struct _FinalizeEntryHashTable {
527 FinalizeEntry **table;
532 typedef struct _DisappearingLink DisappearingLink;
533 struct _DisappearingLink {
534 DisappearingLink *next;
538 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
539 struct _DisappearingLinkHashTable {
540 DisappearingLink **table;
545 typedef struct _EphemeronLinkNode EphemeronLinkNode;
547 struct _EphemeronLinkNode {
548 EphemeronLinkNode *next;
563 int current_collection_generation = -1;
566 * The link pointer is hidden by negating each bit. We use the lowest
567 * bit of the link (before negation) to store whether it needs
568 * resurrection tracking.
570 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
571 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
573 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
574 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
577 * The finalizable hash has the object as the key, the
578 * disappearing_link hash, has the link address as key.
580 static FinalizeEntryHashTable minor_finalizable_hash;
581 static FinalizeEntryHashTable major_finalizable_hash;
582 /* objects that are ready to be finalized */
583 static FinalizeEntry *fin_ready_list = NULL;
584 static FinalizeEntry *critical_fin_list = NULL;
586 static DisappearingLinkHashTable minor_disappearing_link_hash;
587 static DisappearingLinkHashTable major_disappearing_link_hash;
589 static EphemeronLinkNode *ephemeron_list;
591 static int num_ready_finalizers = 0;
592 static int no_finalize = 0;
595 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
596 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
597 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
601 /* registered roots: the key to the hash is the root start address */
603 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
605 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
606 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
607 static mword roots_size = 0; /* amount of memory in the root set */
608 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
611 * The current allocation cursors
612 * We allocate objects in the nursery.
613 * The nursery is the area between nursery_start and nursery_real_end.
614 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
615 * from nursery fragments.
616 * tlab_next is the pointer to the space inside the TLAB where the next object will
618 * tlab_temp_end is the pointer to the end of the temporary space reserved for
619 * the allocation: it allows us to set the scan starts at reasonable intervals.
620 * tlab_real_end points to the end of the TLAB.
621 * nursery_frag_real_end points to the end of the currently used nursery fragment.
622 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
623 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
624 * At the next allocation, the area of the nursery where objects can be present is
625 * between MIN(nursery_first_pinned_start, first_fragment_start) and
626 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
628 static char *nursery_start = NULL;
630 #ifdef HAVE_KW_THREAD
631 #define TLAB_ACCESS_INIT
632 #define TLAB_START tlab_start
633 #define TLAB_NEXT tlab_next
634 #define TLAB_TEMP_END tlab_temp_end
635 #define TLAB_REAL_END tlab_real_end
636 #define REMEMBERED_SET remembered_set
637 #define STORE_REMSET_BUFFER store_remset_buffer
638 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
639 #define IN_CRITICAL_REGION thread_info->in_critical_region
641 static pthread_key_t thread_info_key;
642 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
643 #define TLAB_START (__thread_info__->tlab_start)
644 #define TLAB_NEXT (__thread_info__->tlab_next)
645 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
646 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
647 #define REMEMBERED_SET (__thread_info__->remset)
648 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
649 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
650 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
653 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
654 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
655 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
658 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
659 * variables for next+temp_end ?
661 #ifdef HAVE_KW_THREAD
662 static __thread SgenThreadInfo *thread_info;
663 static __thread char *tlab_start;
664 static __thread char *tlab_next;
665 static __thread char *tlab_temp_end;
666 static __thread char *tlab_real_end;
667 static __thread gpointer *store_remset_buffer;
668 static __thread long store_remset_buffer_index;
669 /* Used by the managed allocator/wbarrier */
670 static __thread char **tlab_next_addr;
671 static __thread char *stack_end;
672 static __thread long *store_remset_buffer_index_addr;
674 static char *nursery_next = NULL;
675 static char *nursery_frag_real_end = NULL;
676 static char *nursery_real_end = NULL;
677 static char *nursery_last_pinned_end = NULL;
679 /* The size of a TLAB */
680 /* The bigger the value, the less often we have to go to the slow path to allocate a new
681 * one, but the more space is wasted by threads not allocating much memory.
683 * FIXME: Make this self-tuning for each thread.
685 static guint32 tlab_size = (1024 * 4);
687 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
688 #define MAX_NURSERY_TLAB_WASTE 512
690 /* fragments that are free and ready to be used for allocation */
691 static Fragment *nursery_fragments = NULL;
692 /* freeelist of fragment structures */
693 static Fragment *fragment_freelist = NULL;
695 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
697 /* Functions supplied by the runtime to be called by the GC */
698 static MonoGCCallbacks gc_callbacks;
700 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
701 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
703 #define ALIGN_UP SGEN_ALIGN_UP
705 #define MOVED_OBJECTS_NUM 64
706 static void *moved_objects [MOVED_OBJECTS_NUM];
707 static int moved_objects_idx = 0;
710 * ######################################################################
711 * ######## Macros and function declarations.
712 * ######################################################################
715 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
718 align_pointer (void *ptr)
720 mword p = (mword)ptr;
721 p += sizeof (gpointer) - 1;
722 p &= ~ (sizeof (gpointer) - 1);
726 typedef SgenGrayQueue GrayQueue;
728 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
729 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
731 /* forward declarations */
732 static int stop_world (void);
733 static int restart_world (void);
734 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
735 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
736 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
737 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
738 static void find_pinning_ref_from_thread (char *obj, size_t size);
739 static void update_current_thread_stack (void *start);
740 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
741 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
742 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
743 static void null_links_for_domain (MonoDomain *domain, int generation);
744 static gboolean search_fragment_for_size (size_t size);
745 static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
746 static void clear_nursery_fragments (char *next);
747 static void pin_from_roots (void *start_nursery, void *end_nursery);
748 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
749 static void optimize_pin_queue (int start_slot);
750 static void clear_remsets (void);
751 static void clear_tlabs (void);
752 static void sort_addresses (void **array, int size);
753 static void drain_gray_stack (GrayQueue *queue);
754 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
755 static gboolean need_major_collection (void);
756 static void major_collection (const char *reason);
758 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
760 void describe_ptr (char *ptr);
761 void check_object (char *start);
763 static void check_consistency (void);
764 static void check_major_refs (void);
765 static void check_scan_starts (void);
766 static void check_for_xdomain_refs (void);
767 static void dump_heap (const char *type, int num, const char *reason);
769 void mono_gc_scan_for_specific_ref (MonoObject *key);
771 static void init_stats (void);
773 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
774 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
775 static void null_ephemerons_for_domain (MonoDomain *domain);
777 SgenMajorCollector major;
779 #include "sgen-protocol.c"
780 #include "sgen-pinning.c"
781 #include "sgen-pinning-stats.c"
782 #include "sgen-gray.c"
783 #include "sgen-workers.c"
784 #include "sgen-los.c"
785 #include "sgen-cardtable.c"
787 /* Root bitmap descriptors are simpler: the lower three bits describe the type
788 * and we either have 30/62 bitmap bits or nibble-based run-length,
789 * or a complex descriptor, or a user defined marker function.
792 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
797 ROOT_DESC_TYPE_MASK = 0x7,
798 ROOT_DESC_TYPE_SHIFT = 3,
801 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
803 #define MAX_USER_DESCRIPTORS 16
805 static gsize* complex_descriptors = NULL;
806 static int complex_descriptors_size = 0;
807 static int complex_descriptors_next = 0;
808 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
809 static int user_descriptors_next = 0;
812 alloc_complex_descriptor (gsize *bitmap, int numbits)
816 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
817 nwords = numbits / GC_BITS_PER_WORD + 1;
820 res = complex_descriptors_next;
821 /* linear search, so we don't have duplicates with domain load/unload
822 * this should not be performance critical or we'd have bigger issues
823 * (the number and size of complex descriptors should be small).
825 for (i = 0; i < complex_descriptors_next; ) {
826 if (complex_descriptors [i] == nwords) {
828 for (j = 0; j < nwords - 1; ++j) {
829 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
839 i += complex_descriptors [i];
841 if (complex_descriptors_next + nwords > complex_descriptors_size) {
842 int new_size = complex_descriptors_size * 2 + nwords;
843 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
844 complex_descriptors_size = new_size;
846 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
847 complex_descriptors_next += nwords;
848 complex_descriptors [res] = nwords;
849 for (i = 0; i < nwords - 1; ++i) {
850 complex_descriptors [res + 1 + i] = bitmap [i];
851 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
858 mono_sgen_get_complex_descriptor (GCVTable *vt)
860 return complex_descriptors + (vt->desc >> LOW_TYPE_BITS);
864 * Descriptor builders.
867 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
869 return (void*) DESC_TYPE_RUN_LENGTH;
873 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
875 int first_set = -1, num_set = 0, last_set = -1, i;
877 size_t stored_size = obj_size;
878 for (i = 0; i < numbits; ++i) {
879 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
887 * We don't encode the size of types that don't contain
888 * references because they might not be aligned, i.e. the
889 * bottom two bits might be set, which would clash with the
890 * bits we need to encode the descriptor type. Since we don't
891 * use the encoded size to skip objects, other than for
892 * processing remsets, in which case only the positions of
893 * references are relevant, this is not a problem.
896 return (void*)DESC_TYPE_RUN_LENGTH;
897 g_assert (!(stored_size & 0x3));
898 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
899 /* check run-length encoding first: one byte offset, one byte number of pointers
900 * on 64 bit archs, we can have 3 runs, just one on 32.
901 * It may be better to use nibbles.
904 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
905 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
907 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
908 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
909 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));
912 /* we know the 2-word header is ptr-free */
913 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
914 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
915 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
919 /* we know the 2-word header is ptr-free */
920 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
921 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
922 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
925 /* it's a complex object ... */
926 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
930 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
932 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
934 int first_set = -1, num_set = 0, last_set = -1, i;
935 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
936 for (i = 0; i < numbits; ++i) {
937 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
944 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
946 return (void*)DESC_TYPE_RUN_LENGTH;
947 if (elem_size <= MAX_ELEMENT_SIZE) {
948 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
950 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
952 /* Note: we also handle structs with just ref fields */
953 if (num_set * sizeof (gpointer) == elem_size) {
954 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
956 /* FIXME: try run-len first */
957 /* Note: we can't skip the object header here, because it's not present */
958 if (last_set <= SMALL_BITMAP_SIZE) {
959 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
962 /* it's am array of complex structs ... */
963 desc = DESC_TYPE_COMPLEX_ARR;
964 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
968 /* Return the bitmap encoded by a descriptor */
970 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
972 mword d = (mword)descr;
976 case DESC_TYPE_RUN_LENGTH: {
977 int first_set = (d >> 16) & 0xff;
978 int num_set = (d >> 24) & 0xff;
981 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
983 for (i = first_set; i < first_set + num_set; ++i)
984 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
986 *numbits = first_set + num_set;
990 case DESC_TYPE_SMALL_BITMAP:
991 bitmap = g_new0 (gsize, 1);
993 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
995 *numbits = GC_BITS_PER_WORD;
999 g_assert_not_reached ();
1004 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1006 MonoObject *o = (MonoObject*)(obj);
1007 MonoObject *ref = (MonoObject*)*(ptr);
1008 int offset = (char*)(ptr) - (char*)o;
1010 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1012 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1014 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1015 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1017 /* Thread.cached_culture_info */
1018 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1019 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1020 !strcmp(o->vtable->klass->name_space, "System") &&
1021 !strcmp(o->vtable->klass->name, "Object[]"))
1024 * 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
1025 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1026 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1027 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1028 * 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
1029 * 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
1030 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1031 * 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
1032 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1034 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1035 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1036 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1037 !strcmp (o->vtable->klass->name, "MemoryStream"))
1039 /* append_job() in threadpool.c */
1040 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1041 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1042 !strcmp (o->vtable->klass->name_space, "System") &&
1043 !strcmp (o->vtable->klass->name, "Object[]") &&
1044 mono_thread_pool_is_queue_array ((MonoArray*) o))
1050 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1052 MonoObject *o = (MonoObject*)(obj);
1053 MonoObject *ref = (MonoObject*)*(ptr);
1054 int offset = (char*)(ptr) - (char*)o;
1056 MonoClassField *field;
1059 if (!ref || ref->vtable->domain == domain)
1061 if (is_xdomain_ref_allowed (ptr, obj, domain))
1065 for (class = o->vtable->klass; class; class = class->parent) {
1068 for (i = 0; i < class->field.count; ++i) {
1069 if (class->fields[i].offset == offset) {
1070 field = &class->fields[i];
1078 if (ref->vtable->klass == mono_defaults.string_class)
1079 str = mono_string_to_utf8 ((MonoString*)ref);
1082 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1083 o, o->vtable->klass->name_space, o->vtable->klass->name,
1084 offset, field ? field->name : "",
1085 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1086 mono_gc_scan_for_specific_ref (o);
1092 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1095 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1097 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1099 #include "sgen-scan-object.h"
1103 #define HANDLE_PTR(ptr,obj) do { \
1104 if ((MonoObject*)*(ptr) == key) { \
1105 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1106 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1111 scan_object_for_specific_ref (char *start, MonoObject *key)
1113 #include "sgen-scan-object.h"
1117 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data)
1119 while (start < end) {
1121 if (!*(void**)start) {
1122 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1126 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
1128 callback (start, size, data);
1135 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1137 scan_object_for_specific_ref (obj, key);
1141 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1145 g_print ("found ref to %p in root record %p\n", key, root);
1148 static MonoObject *check_key = NULL;
1149 static RootRecord *check_root = NULL;
1152 check_root_obj_specific_ref_from_marker (void **obj)
1154 check_root_obj_specific_ref (check_root, check_key, *obj);
1158 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1163 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1164 for (root = roots_hash [root_type][i]; root; root = root->next) {
1165 void **start_root = (void**)root->start_root;
1166 mword desc = root->root_desc;
1170 switch (desc & ROOT_DESC_TYPE_MASK) {
1171 case ROOT_DESC_BITMAP:
1172 desc >>= ROOT_DESC_TYPE_SHIFT;
1175 check_root_obj_specific_ref (root, key, *start_root);
1180 case ROOT_DESC_COMPLEX: {
1181 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1182 int bwords = (*bitmap_data) - 1;
1183 void **start_run = start_root;
1185 while (bwords-- > 0) {
1186 gsize bmap = *bitmap_data++;
1187 void **objptr = start_run;
1190 check_root_obj_specific_ref (root, key, *objptr);
1194 start_run += GC_BITS_PER_WORD;
1198 case ROOT_DESC_USER: {
1199 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1200 marker (start_root, check_root_obj_specific_ref_from_marker);
1203 case ROOT_DESC_RUN_LEN:
1204 g_assert_not_reached ();
1206 g_assert_not_reached ();
1215 mono_gc_scan_for_specific_ref (MonoObject *key)
1221 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1222 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1224 major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1226 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1227 scan_object_for_specific_ref (bigobj->data, key);
1229 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1230 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1232 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1233 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1234 void **ptr = (void**)root->start_root;
1236 while (ptr < (void**)root->end_root) {
1237 check_root_obj_specific_ref (root, *ptr, key);
1244 /* Clear all remaining nursery fragments */
1246 clear_nursery_fragments (char *next)
1249 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1250 g_assert (next <= nursery_frag_real_end);
1251 memset (next, 0, nursery_frag_real_end - next);
1252 for (frag = nursery_fragments; frag; frag = frag->next) {
1253 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1259 need_remove_object_for_domain (char *start, MonoDomain *domain)
1261 if (mono_object_domain (start) == domain) {
1262 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1263 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1270 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1272 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1273 if (vt->klass == mono_defaults.internal_thread_class)
1274 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1275 /* The object could be a proxy for an object in the domain
1277 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1278 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1280 /* The server could already have been zeroed out, so
1281 we need to check for that, too. */
1282 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1283 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1285 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1290 static MonoDomain *check_domain = NULL;
1293 check_obj_not_in_domain (void **o)
1295 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1299 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1303 check_domain = domain;
1304 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1305 for (root = roots_hash [root_type][i]; root; root = root->next) {
1306 void **start_root = (void**)root->start_root;
1307 mword desc = root->root_desc;
1309 /* The MonoDomain struct is allowed to hold
1310 references to objects in its own domain. */
1311 if (start_root == (void**)domain)
1314 switch (desc & ROOT_DESC_TYPE_MASK) {
1315 case ROOT_DESC_BITMAP:
1316 desc >>= ROOT_DESC_TYPE_SHIFT;
1318 if ((desc & 1) && *start_root)
1319 check_obj_not_in_domain (*start_root);
1324 case ROOT_DESC_COMPLEX: {
1325 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1326 int bwords = (*bitmap_data) - 1;
1327 void **start_run = start_root;
1329 while (bwords-- > 0) {
1330 gsize bmap = *bitmap_data++;
1331 void **objptr = start_run;
1333 if ((bmap & 1) && *objptr)
1334 check_obj_not_in_domain (*objptr);
1338 start_run += GC_BITS_PER_WORD;
1342 case ROOT_DESC_USER: {
1343 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1344 marker (start_root, check_obj_not_in_domain);
1347 case ROOT_DESC_RUN_LEN:
1348 g_assert_not_reached ();
1350 g_assert_not_reached ();
1354 check_domain = NULL;
1358 check_for_xdomain_refs (void)
1362 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1363 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1365 major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1367 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1368 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1372 clear_domain_process_object (char *obj, MonoDomain *domain)
1376 process_object_for_domain_clearing (obj, domain);
1377 remove = need_remove_object_for_domain (obj, domain);
1379 if (remove && ((MonoObject*)obj)->synchronisation) {
1380 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1382 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1389 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1391 if (clear_domain_process_object (obj, domain))
1392 memset (obj, 0, size);
1396 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1398 clear_domain_process_object (obj, domain);
1402 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1404 if (need_remove_object_for_domain (obj, domain))
1405 major.free_non_pinned_object (obj, size);
1409 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1411 if (need_remove_object_for_domain (obj, domain))
1412 major.free_pinned_object (obj, size);
1416 * When appdomains are unloaded we can easily remove objects that have finalizers,
1417 * but all the others could still be present in random places on the heap.
1418 * We need a sweep to get rid of them even though it's going to be costly
1420 * The reason we need to remove them is because we access the vtable and class
1421 * structures to know the object size and the reference bitmap: once the domain is
1422 * unloaded the point to random memory.
1425 mono_gc_clear_domain (MonoDomain * domain)
1427 LOSObject *bigobj, *prev;
1432 clear_nursery_fragments (nursery_next);
1434 if (xdomain_checks && domain != mono_get_root_domain ()) {
1435 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1436 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1437 check_for_xdomain_refs ();
1440 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1441 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain);
1443 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1444 to memory returned to the OS.*/
1445 null_ephemerons_for_domain (domain);
1447 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1448 null_links_for_domain (domain, i);
1450 /* We need two passes over major and large objects because
1451 freeing such objects might give their memory back to the OS
1452 (in the case of large objects) or obliterate its vtable
1453 (pinned objects with major-copying or pinned and non-pinned
1454 objects with major-mark&sweep), but we might need to
1455 dereference a pointer from an object to another object if
1456 the first object is a proxy. */
1457 major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1458 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1459 clear_domain_process_object (bigobj->data, domain);
1462 for (bigobj = los_object_list; bigobj;) {
1463 if (need_remove_object_for_domain (bigobj->data, domain)) {
1464 LOSObject *to_free = bigobj;
1466 prev->next = bigobj->next;
1468 los_object_list = bigobj->next;
1469 bigobj = bigobj->next;
1470 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1472 free_large_object (to_free);
1476 bigobj = bigobj->next;
1478 major.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1479 major.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1485 global_remset_cache_clear (void)
1487 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1491 * Tries to check if a given remset location was already added to the global remset.
1494 * A 2 entry, LRU cache of recently saw location remsets.
1496 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1498 * Returns TRUE is the element was added..
1501 global_remset_location_was_not_added (gpointer ptr)
1504 gpointer first = global_remset_cache [0], second;
1506 HEAVY_STAT (++stat_global_remsets_discarded);
1510 second = global_remset_cache [1];
1512 if (second == ptr) {
1513 /*Move the second to the front*/
1514 global_remset_cache [0] = second;
1515 global_remset_cache [1] = first;
1517 HEAVY_STAT (++stat_global_remsets_discarded);
1521 global_remset_cache [0] = second;
1522 global_remset_cache [1] = ptr;
1527 * mono_sgen_add_to_global_remset:
1529 * The global remset contains locations which point into newspace after
1530 * a minor collection. This can happen if the objects they point to are pinned.
1532 * LOCKING: If called from a parallel collector, the global remset
1533 * lock must be held. For serial collectors that is not necessary.
1536 mono_sgen_add_to_global_remset (gpointer ptr)
1541 if (use_cardtable) {
1542 sgen_card_table_mark_address ((mword)ptr);
1546 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1548 lock = (current_collection_generation == GENERATION_OLD && major.is_parallel);
1552 if (!global_remset_location_was_not_added (ptr))
1555 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1556 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1558 HEAVY_STAT (++stat_global_remsets_added);
1561 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1562 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1564 if (global_remset->store_next + 3 < global_remset->end_set) {
1565 *(global_remset->store_next++) = (mword)ptr;
1568 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1569 rs->next = global_remset;
1571 *(global_remset->store_next++) = (mword)ptr;
1574 int global_rs_size = 0;
1576 for (rs = global_remset; rs; rs = rs->next) {
1577 global_rs_size += rs->store_next - rs->data;
1579 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1584 UNLOCK_GLOBAL_REMSET;
1590 * Scan objects in the gray stack until the stack is empty. This should be called
1591 * frequently after each object is copied, to achieve better locality and cache
1595 drain_gray_stack (GrayQueue *queue)
1599 if (current_collection_generation == GENERATION_NURSERY) {
1601 GRAY_OBJECT_DEQUEUE (queue, obj);
1604 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1605 major.minor_scan_object (obj, queue);
1608 if (major.is_parallel && queue == &workers_distribute_gray_queue)
1612 GRAY_OBJECT_DEQUEUE (queue, obj);
1615 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1616 major.major_scan_object (obj, queue);
1622 * Addresses from start to end are already sorted. This function finds
1623 * the object header for each address and pins the object. The
1624 * addresses must be inside the passed section. The (start of the)
1625 * address array is overwritten with the addresses of the actually
1626 * pinned objects. Return the number of pinned objects.
1629 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1634 void *last_obj = NULL;
1635 size_t last_obj_size = 0;
1638 void **definitely_pinned = start;
1639 while (start < end) {
1641 /* the range check should be reduntant */
1642 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1643 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1644 /* multiple pointers to the same object */
1645 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1649 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1650 g_assert (idx < section->num_scan_start);
1651 search_start = (void*)section->scan_starts [idx];
1652 if (!search_start || search_start > addr) {
1655 search_start = section->scan_starts [idx];
1656 if (search_start && search_start <= addr)
1659 if (!search_start || search_start > addr)
1660 search_start = start_nursery;
1662 if (search_start < last_obj)
1663 search_start = (char*)last_obj + last_obj_size;
1664 /* now addr should be in an object a short distance from search_start
1665 * Note that search_start must point to zeroed mem or point to an object.
1668 if (!*(void**)search_start) {
1669 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1672 last_obj = search_start;
1673 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1674 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1675 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1676 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));
1677 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1678 pin_object (search_start);
1679 GRAY_OBJECT_ENQUEUE (queue, search_start);
1681 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1682 definitely_pinned [count] = search_start;
1686 /* skip to the next object */
1687 search_start = (void*)((char*)search_start + last_obj_size);
1688 } while (search_start <= addr);
1689 /* we either pinned the correct object or we ignored the addr because
1690 * it points to unused zeroed memory.
1696 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1701 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1703 int num_entries = section->pin_queue_num_entries;
1705 void **start = section->pin_queue_start;
1707 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1708 section->data, section->next_data, queue);
1709 section->pin_queue_num_entries = reduced_to;
1711 section->pin_queue_start = NULL;
1715 /* Sort the addresses in array in increasing order.
1716 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1719 sort_addresses (void **array, int size)
1724 for (i = 1; i < size; ++i) {
1727 int parent = (child - 1) / 2;
1729 if (array [parent] >= array [child])
1732 tmp = array [parent];
1733 array [parent] = array [child];
1734 array [child] = tmp;
1740 for (i = size - 1; i > 0; --i) {
1743 array [i] = array [0];
1749 while (root * 2 + 1 <= end) {
1750 int child = root * 2 + 1;
1752 if (child < end && array [child] < array [child + 1])
1754 if (array [root] >= array [child])
1758 array [root] = array [child];
1759 array [child] = tmp;
1766 static G_GNUC_UNUSED void
1767 print_nursery_gaps (void* start_nursery, void *end_nursery)
1770 gpointer first = start_nursery;
1772 for (i = 0; i < next_pin_slot; ++i) {
1773 next = pin_queue [i];
1774 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1778 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1781 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1783 optimize_pin_queue (int start_slot)
1785 void **start, **cur, **end;
1786 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1787 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1788 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1789 if ((next_pin_slot - start_slot) > 1)
1790 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1791 start = cur = pin_queue + start_slot;
1792 end = pin_queue + next_pin_slot;
1795 while (*start == *cur && cur < end)
1799 next_pin_slot = start - pin_queue;
1800 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1801 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1806 * Scan the memory between start and end and queue values which could be pointers
1807 * to the area between start_nursery and end_nursery for later consideration.
1808 * Typically used for thread stacks.
1811 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1814 while (start < end) {
1815 if (*start >= start_nursery && *start < end_nursery) {
1817 * *start can point to the middle of an object
1818 * note: should we handle pointing at the end of an object?
1819 * pinning in C# code disallows pointing at the end of an object
1820 * but there is some small chance that an optimizing C compiler
1821 * may keep the only reference to an object by pointing
1822 * at the end of it. We ignore this small chance for now.
1823 * Pointers to the end of an object are indistinguishable
1824 * from pointers to the start of the next object in memory
1825 * so if we allow that we'd need to pin two objects...
1826 * We queue the pointer in an array, the
1827 * array will then be sorted and uniqued. This way
1828 * we can coalesce several pinning pointers and it should
1829 * be faster since we'd do a memory scan with increasing
1830 * addresses. Note: we can align the address to the allocation
1831 * alignment, so the unique process is more effective.
1833 mword addr = (mword)*start;
1834 addr &= ~(ALLOC_ALIGN - 1);
1835 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
1836 pin_stage_ptr ((void*)addr);
1838 pin_stats_register_address ((char*)addr, pin_type);
1839 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
1844 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1848 * Debugging function: find in the conservative roots where @obj is being pinned.
1850 static G_GNUC_UNUSED void
1851 find_pinning_reference (char *obj, size_t size)
1855 char *endobj = obj + size;
1856 for (i = 0; i < roots_hash_size [0]; ++i) {
1857 for (root = roots_hash [0][i]; root; root = root->next) {
1858 /* if desc is non-null it has precise info */
1859 if (!root->root_desc) {
1860 char ** start = (char**)root->start_root;
1861 while (start < (char**)root->end_root) {
1862 if (*start >= obj && *start < endobj) {
1863 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));
1870 find_pinning_ref_from_thread (obj, size);
1874 * The first thing we do in a collection is to identify pinned objects.
1875 * This function considers all the areas of memory that need to be
1876 * conservatively scanned.
1879 pin_from_roots (void *start_nursery, void *end_nursery)
1883 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]));
1884 /* objects pinned from the API are inside these roots */
1885 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1886 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1887 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
1888 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
1891 /* now deal with the thread stacks
1892 * in the future we should be able to conservatively scan only:
1893 * *) the cpu registers
1894 * *) the unmanaged stack frames
1895 * *) the _last_ managed stack frame
1896 * *) pointers slots in managed frames
1898 scan_thread_data (start_nursery, end_nursery, FALSE);
1900 evacuate_pin_staging_area ();
1903 static CopyOrMarkObjectFunc user_copy_or_mark_func;
1904 static GrayQueue *user_copy_or_mark_queue;
1907 single_arg_user_copy_or_mark (void **obj)
1909 user_copy_or_mark_func (obj, user_copy_or_mark_queue);
1913 * The memory area from start_root to end_root contains pointers to objects.
1914 * Their position is precisely described by @desc (this means that the pointer
1915 * can be either NULL or the pointer to the start of an object).
1916 * This functions copies them to to_space updates them.
1918 * This function is not thread-safe!
1921 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
1923 switch (desc & ROOT_DESC_TYPE_MASK) {
1924 case ROOT_DESC_BITMAP:
1925 desc >>= ROOT_DESC_TYPE_SHIFT;
1927 if ((desc & 1) && *start_root) {
1928 copy_func (start_root, queue);
1929 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
1930 drain_gray_stack (queue);
1936 case ROOT_DESC_COMPLEX: {
1937 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1938 int bwords = (*bitmap_data) - 1;
1939 void **start_run = start_root;
1941 while (bwords-- > 0) {
1942 gsize bmap = *bitmap_data++;
1943 void **objptr = start_run;
1945 if ((bmap & 1) && *objptr) {
1946 copy_func (objptr, queue);
1947 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
1948 drain_gray_stack (queue);
1953 start_run += GC_BITS_PER_WORD;
1957 case ROOT_DESC_USER: {
1958 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1959 user_copy_or_mark_func = copy_func;
1960 user_copy_or_mark_queue = queue;
1961 marker (start_root, single_arg_user_copy_or_mark);
1962 user_copy_or_mark_func = NULL;
1963 user_copy_or_mark_queue = NULL;
1966 case ROOT_DESC_RUN_LEN:
1967 g_assert_not_reached ();
1969 g_assert_not_reached ();
1974 mono_sgen_update_heap_boundaries (mword low, mword high)
1979 old = lowest_heap_address;
1982 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
1985 old = highest_heap_address;
1988 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
1992 alloc_fragment (void)
1994 Fragment *frag = fragment_freelist;
1996 fragment_freelist = frag->next;
2000 frag = mono_sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
2005 /* size must be a power of 2 */
2007 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2009 /* Allocate twice the memory to be able to put the block on an aligned address */
2010 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2015 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2016 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2019 mono_sgen_free_os_memory (mem, aligned - mem);
2020 if (aligned + size < mem + size + alignment)
2021 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2027 * Allocate and setup the data structures needed to be able to allocate objects
2028 * in the nursery. The nursery is stored in nursery_section.
2031 alloc_nursery (void)
2033 GCMemSection *section;
2039 if (nursery_section)
2041 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2042 /* later we will alloc a larger area for the nursery but only activate
2043 * what we need. The rest will be used as expansion if we have too many pinned
2044 * objects in the existing nursery.
2046 /* FIXME: handle OOM */
2047 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2049 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2050 alloc_size = nursery_size;
2051 #ifdef SGEN_ALIGN_NURSERY
2052 data = major.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2054 data = major.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2056 nursery_start = data;
2057 nursery_real_end = nursery_start + nursery_size;
2058 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
2059 nursery_next = nursery_start;
2060 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));
2061 section->data = section->next_data = data;
2062 section->size = alloc_size;
2063 section->end_data = nursery_real_end;
2064 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2065 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2066 section->num_scan_start = scan_starts;
2067 section->block.role = MEMORY_ROLE_GEN0;
2068 section->block.next = NULL;
2070 nursery_section = section;
2072 /* Setup the single first large fragment */
2073 frag = alloc_fragment ();
2074 frag->fragment_start = nursery_start;
2075 frag->fragment_limit = nursery_start;
2076 frag->fragment_end = nursery_real_end;
2077 nursery_frag_real_end = nursery_real_end;
2078 /* FIXME: frag here is lost */
2082 mono_gc_get_nursery (int *shift_bits, size_t *size)
2084 *size = nursery_size;
2085 #ifdef SGEN_ALIGN_NURSERY
2086 *shift_bits = DEFAULT_NURSERY_BITS;
2090 return nursery_start;
2094 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2098 for (fin = list; fin; fin = fin->next) {
2101 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2102 copy_func (&fin->object, queue);
2106 static mword fragment_total = 0;
2108 * We found a fragment of free memory in the nursery: memzero it and if
2109 * it is big enough, add it to the list of fragments that can be used for
2113 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2116 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2117 binary_protocol_empty (frag_start, frag_size);
2118 /* memsetting just the first chunk start is bound to provide better cache locality */
2119 if (nursery_clear_policy == CLEAR_AT_GC)
2120 memset (frag_start, 0, frag_size);
2121 /* Not worth dealing with smaller fragments: need to tune */
2122 if (frag_size >= FRAGMENT_MIN_SIZE) {
2123 fragment = alloc_fragment ();
2124 fragment->fragment_start = frag_start;
2125 fragment->fragment_limit = frag_start;
2126 fragment->fragment_end = frag_end;
2127 fragment->next = nursery_fragments;
2128 nursery_fragments = fragment;
2129 fragment_total += frag_size;
2131 /* Clear unused fragments, pinning depends on this */
2132 /*TODO place an int[] here instead of the memset if size justify it*/
2133 memset (frag_start, 0, frag_size);
2138 generation_name (int generation)
2140 switch (generation) {
2141 case GENERATION_NURSERY: return "nursery";
2142 case GENERATION_OLD: return "old";
2143 default: g_assert_not_reached ();
2147 static DisappearingLinkHashTable*
2148 get_dislink_hash_table (int generation)
2150 switch (generation) {
2151 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2152 case GENERATION_OLD: return &major_disappearing_link_hash;
2153 default: g_assert_not_reached ();
2157 static FinalizeEntryHashTable*
2158 get_finalize_entry_hash_table (int generation)
2160 switch (generation) {
2161 case GENERATION_NURSERY: return &minor_finalizable_hash;
2162 case GENERATION_OLD: return &major_finalizable_hash;
2163 default: g_assert_not_reached ();
2168 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2173 int ephemeron_rounds = 0;
2174 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major.copy_object : major.copy_or_mark_object;
2177 * We copied all the reachable objects. Now it's the time to copy
2178 * the objects that were not referenced by the roots, but by the copied objects.
2179 * we built a stack of objects pointed to by gray_start: they are
2180 * additional roots and we may add more items as we go.
2181 * We loop until gray_start == gray_objects which means no more objects have
2182 * been added. Note this is iterative: no recursion is involved.
2183 * We need to walk the LO list as well in search of marked big objects
2184 * (use a flag since this is needed only on major collections). We need to loop
2185 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2186 * To achieve better cache locality and cache usage, we drain the gray stack
2187 * frequently, after each object is copied, and just finish the work here.
2189 drain_gray_stack (queue);
2191 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2192 /* walk the finalization queue and move also the objects that need to be
2193 * finalized: use the finalized objects as new roots so the objects they depend
2194 * on are also not reclaimed. As with the roots above, only objects in the nursery
2195 * are marked/copied.
2196 * We need a loop here, since objects ready for finalizers may reference other objects
2197 * that are fin-ready. Speedup with a flag?
2201 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2202 * before processing finalizable objects to avoid finalizing reachable values.
2204 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2205 * while they are been finalized.
2207 int done_with_ephemerons = 0;
2209 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2210 drain_gray_stack (queue);
2212 } while (!done_with_ephemerons);
2214 fin_ready = num_ready_finalizers;
2215 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2216 if (generation == GENERATION_OLD)
2217 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
2219 /* drain the new stack that might have been created */
2220 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2221 drain_gray_stack (queue);
2222 } while (fin_ready != num_ready_finalizers);
2225 * Clear ephemeron pairs with unreachable keys.
2226 * We pass the copy func so we can figure out if an array was promoted or not.
2228 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2231 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));
2234 * handle disappearing links
2235 * Note we do this after checking the finalization queue because if an object
2236 * survives (at least long enough to be finalized) we don't clear the link.
2237 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2238 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2241 g_assert (gray_object_queue_is_empty (queue));
2243 null_link_in_range (copy_func, start_addr, end_addr, generation, queue);
2244 if (generation == GENERATION_OLD)
2245 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, queue);
2246 if (gray_object_queue_is_empty (queue))
2248 drain_gray_stack (queue);
2251 g_assert (gray_object_queue_is_empty (queue));
2255 mono_sgen_check_section_scan_starts (GCMemSection *section)
2258 for (i = 0; i < section->num_scan_start; ++i) {
2259 if (section->scan_starts [i]) {
2260 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2261 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2267 check_scan_starts (void)
2269 if (!do_scan_starts_check)
2271 mono_sgen_check_section_scan_starts (nursery_section);
2272 major.check_scan_starts ();
2275 static int last_num_pinned = 0;
2278 build_nursery_fragments (void **start, int num_entries)
2280 char *frag_start, *frag_end;
2284 while (nursery_fragments) {
2285 Fragment *next = nursery_fragments->next;
2286 nursery_fragments->next = fragment_freelist;
2287 fragment_freelist = nursery_fragments;
2288 nursery_fragments = next;
2290 frag_start = nursery_start;
2292 /* clear scan starts */
2293 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2294 for (i = 0; i < num_entries; ++i) {
2295 frag_end = start [i];
2296 /* remove the pin bit from pinned objects */
2297 unpin_object (frag_end);
2298 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2299 frag_size = frag_end - frag_start;
2301 add_nursery_frag (frag_size, frag_start, frag_end);
2302 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)start [i]));
2303 frag_start = (char*)start [i] + frag_size;
2305 nursery_last_pinned_end = frag_start;
2306 frag_end = nursery_real_end;
2307 frag_size = frag_end - frag_start;
2309 add_nursery_frag (frag_size, frag_start, frag_end);
2310 if (!nursery_fragments) {
2311 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
2312 for (i = 0; i < num_entries; ++i) {
2313 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])));
2318 nursery_next = nursery_frag_real_end = NULL;
2320 /* Clear TLABs for all threads */
2325 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2329 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2330 for (root = roots_hash [root_type][i]; root; root = root->next) {
2331 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2332 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2338 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2340 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2344 mono_sgen_dump_section (GCMemSection *section, const char *type)
2346 char *start = section->data;
2347 char *end = section->data + section->size;
2348 char *occ_start = NULL;
2350 char *old_start = NULL; /* just for debugging */
2352 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2354 while (start < end) {
2358 if (!*(void**)start) {
2360 mono_sgen_dump_occupied (occ_start, start, section->data);
2363 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2366 g_assert (start < section->next_data);
2371 vt = (GCVTable*)LOAD_VTABLE (start);
2374 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2377 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2378 start - section->data,
2379 vt->klass->name_space, vt->klass->name,
2387 mono_sgen_dump_occupied (occ_start, start, section->data);
2389 fprintf (heap_dump_file, "</section>\n");
2393 dump_object (MonoObject *obj, gboolean dump_location)
2395 static char class_name [1024];
2397 MonoClass *class = mono_object_class (obj);
2401 * Python's XML parser is too stupid to parse angle brackets
2402 * in strings, so we just ignore them;
2405 while (class->name [i] && j < sizeof (class_name) - 1) {
2406 if (!strchr ("<>\"", class->name [i]))
2407 class_name [j++] = class->name [i];
2410 g_assert (j < sizeof (class_name));
2413 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2414 class->name_space, class_name,
2415 safe_object_get_size (obj));
2416 if (dump_location) {
2417 const char *location;
2418 if (ptr_in_nursery (obj))
2419 location = "nursery";
2420 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2424 fprintf (heap_dump_file, " location=\"%s\"", location);
2426 fprintf (heap_dump_file, "/>\n");
2430 dump_heap (const char *type, int num, const char *reason)
2435 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2437 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2438 fprintf (heap_dump_file, ">\n");
2439 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2440 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2441 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2442 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2443 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2445 fprintf (heap_dump_file, "<pinned-objects>\n");
2446 for (list = pinned_objects; list; list = list->next)
2447 dump_object (list->obj, TRUE);
2448 fprintf (heap_dump_file, "</pinned-objects>\n");
2450 mono_sgen_dump_section (nursery_section, "nursery");
2452 major.dump_heap (heap_dump_file);
2454 fprintf (heap_dump_file, "<los>\n");
2455 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2456 dump_object ((MonoObject*)bigobj->data, FALSE);
2457 fprintf (heap_dump_file, "</los>\n");
2459 fprintf (heap_dump_file, "</collection>\n");
2463 mono_sgen_register_moved_object (void *obj, void *destination)
2465 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2467 /* FIXME: handle this for parallel collector */
2468 g_assert (!major.is_parallel);
2470 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2471 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2472 moved_objects_idx = 0;
2474 moved_objects [moved_objects_idx++] = obj;
2475 moved_objects [moved_objects_idx++] = destination;
2481 static gboolean inited = FALSE;
2486 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2487 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2488 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2489 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2490 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2491 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2492 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2493 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2495 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2496 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2497 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2498 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2499 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2500 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2501 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2502 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2503 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2504 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2505 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2506 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2507 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2510 #ifdef HEAVY_STATISTICS
2511 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2512 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2513 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2514 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2515 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2516 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2517 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2518 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2520 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2521 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2522 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2523 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2524 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2526 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2527 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2528 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2529 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2531 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2532 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2534 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2535 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2536 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2538 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
2539 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
2541 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2542 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2543 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2544 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2545 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2546 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2547 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2548 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2549 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2556 need_major_collection (void)
2558 mword los_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
2559 return minor_collection_sections_alloced * major.section_size + los_alloced > minor_collection_allowance;
2563 * Collect objects in the nursery. Returns whether to trigger a major
2567 collect_nursery (size_t requested_size)
2569 size_t max_garbage_amount;
2570 char *orig_nursery_next;
2571 TV_DECLARE (all_atv);
2572 TV_DECLARE (all_btv);
2576 current_collection_generation = GENERATION_NURSERY;
2578 binary_protocol_collection (GENERATION_NURSERY);
2579 check_scan_starts ();
2582 orig_nursery_next = nursery_next;
2583 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
2584 /* FIXME: optimize later to use the higher address where an object can be present */
2585 nursery_next = MAX (nursery_next, nursery_real_end);
2587 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)));
2588 max_garbage_amount = nursery_next - nursery_start;
2589 g_assert (nursery_section->size >= max_garbage_amount);
2591 /* world must be stopped already */
2592 TV_GETTIME (all_atv);
2595 /* Pinning depends on this */
2596 clear_nursery_fragments (orig_nursery_next);
2599 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2602 check_for_xdomain_refs ();
2604 nursery_section->next_data = nursery_next;
2606 major.start_nursery_collection ();
2608 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
2611 mono_stats.minor_gc_count ++;
2613 global_remset_cache_clear ();
2615 /* pin from pinned handles */
2617 pin_from_roots (nursery_start, nursery_next);
2618 /* identify pinned objects */
2619 optimize_pin_queue (0);
2620 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, &gray_queue);
2621 nursery_section->pin_queue_start = pin_queue;
2622 nursery_section->pin_queue_num_entries = next_pin_slot;
2624 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
2625 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
2626 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2628 if (consistency_check_at_minor_collection)
2629 check_consistency ();
2632 * walk all the roots and copy the young objects to the old generation,
2633 * starting from to_space
2636 scan_from_remsets (nursery_start, nursery_next, &gray_queue);
2637 /* we don't have complete write barrier yet, so we scan all the old generation sections */
2639 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
2640 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2642 if (use_cardtable) {
2643 scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
2644 //collect_faulted_cards ();
2647 drain_gray_stack (&gray_queue);
2650 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
2651 /* registered roots, this includes static fields */
2652 scan_from_registered_roots (major.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
2653 scan_from_registered_roots (major.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
2655 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2657 scan_thread_data (nursery_start, nursery_next, TRUE);
2659 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2662 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
2664 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
2666 /* walk the pin_queue, build up the fragment list of free memory, unmark
2667 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2670 build_nursery_fragments (pin_queue, next_pin_slot);
2672 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
2673 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
2675 if (consistency_check_at_minor_collection)
2676 check_major_refs ();
2678 major.finish_nursery_collection ();
2680 TV_GETTIME (all_btv);
2681 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2684 dump_heap ("minor", num_minor_gcs - 1, NULL);
2686 /* prepare the pin queue for the next collection */
2687 last_num_pinned = next_pin_slot;
2689 if (fin_ready_list || critical_fin_list) {
2690 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2691 mono_gc_finalize_notify ();
2695 g_assert (gray_object_queue_is_empty (&gray_queue));
2697 check_scan_starts ();
2699 binary_protocol_flush_buffers (FALSE);
2701 current_collection_generation = -1;
2703 return need_major_collection ();
2707 major_do_collection (const char *reason)
2709 LOSObject *bigobj, *prevbo;
2710 TV_DECLARE (all_atv);
2711 TV_DECLARE (all_btv);
2714 /* FIXME: only use these values for the precise scan
2715 * note that to_space pointers should be excluded anyway...
2717 char *heap_start = NULL;
2718 char *heap_end = (char*)-1;
2719 int old_num_major_sections = major.get_num_major_sections ();
2720 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2721 mword los_memory_saved, los_memory_alloced, old_los_memory_usage;
2724 * A domain could have been freed, resulting in
2725 * los_memory_usage being less than last_los_memory_usage.
2727 los_memory_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
2728 old_los_memory_usage = los_memory_usage;
2730 //count_ref_nonref_objs ();
2731 //consistency_check ();
2733 binary_protocol_collection (GENERATION_OLD);
2734 check_scan_starts ();
2735 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
2736 if (major.is_parallel)
2737 gray_object_queue_init (&workers_distribute_gray_queue, mono_sgen_get_unmanaged_allocator ());
2740 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
2742 mono_stats.major_gc_count ++;
2744 /* world must be stopped already */
2745 TV_GETTIME (all_atv);
2748 /* Pinning depends on this */
2749 clear_nursery_fragments (nursery_next);
2752 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2755 check_for_xdomain_refs ();
2757 nursery_section->next_data = nursery_real_end;
2758 /* we should also coalesce scanning from sections close to each other
2759 * and deal with pointers outside of the sections later.
2761 /* The remsets are not useful for a major collection */
2763 global_remset_cache_clear ();
2765 card_table_clear ();
2769 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
2770 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
2771 optimize_pin_queue (0);
2774 * pin_queue now contains all candidate pointers, sorted and
2775 * uniqued. We must do two passes now to figure out which
2776 * objects are pinned.
2778 * The first is to find within the pin_queue the area for each
2779 * section. This requires that the pin_queue be sorted. We
2780 * also process the LOS objects and pinned chunks here.
2782 * The second, destructive, pass is to reduce the section
2783 * areas to pointers to the actually pinned objects.
2785 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
2786 /* first pass for the sections */
2787 mono_sgen_find_section_pin_queue_start_end (nursery_section);
2788 major.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2789 /* identify possible pointers to the insize of large objects */
2790 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
2791 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2793 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
2794 pin_object (bigobj->data);
2795 /* FIXME: only enqueue if object has references */
2796 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
2798 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
2799 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));
2802 /* second pass for the sections */
2803 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2804 major.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2807 time_major_pinning += TV_ELAPSED_MS (atv, btv);
2808 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2809 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2811 major.init_to_space ();
2813 workers_start_all_workers (1);
2816 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
2818 /* registered roots, this includes static fields */
2819 scan_from_registered_roots (major.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2820 scan_from_registered_roots (major.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2822 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2825 /* FIXME: This is the wrong place for this, because it does
2827 scan_thread_data (heap_start, heap_end, TRUE);
2829 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2832 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
2834 /* scan the list of objects ready for finalization */
2835 scan_finalizer_entries (major.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2836 scan_finalizer_entries (major.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2838 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
2839 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2842 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
2844 if (major.is_parallel) {
2845 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
2846 workers_distribute_gray_queue_sections ();
2850 workers_change_num_working (-1);
2853 if (major.is_parallel)
2854 g_assert (gray_object_queue_is_empty (&gray_queue));
2856 /* all the objects in the heap */
2857 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
2859 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
2861 /* sweep the big objects list */
2863 for (bigobj = los_object_list; bigobj;) {
2864 if (object_is_pinned (bigobj->data)) {
2865 unpin_object (bigobj->data);
2868 /* not referenced anywhere, so we can free it */
2870 prevbo->next = bigobj->next;
2872 los_object_list = bigobj->next;
2874 bigobj = bigobj->next;
2875 free_large_object (to_free);
2879 bigobj = bigobj->next;
2883 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
2888 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
2893 time_major_sweep += TV_ELAPSED_MS (atv, btv);
2895 /* walk the pin_queue, build up the fragment list of free memory, unmark
2896 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2899 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
2902 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
2904 TV_GETTIME (all_btv);
2905 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2908 dump_heap ("major", num_major_gcs - 1, reason);
2910 /* prepare the pin queue for the next collection */
2912 if (fin_ready_list || critical_fin_list) {
2913 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2914 mono_gc_finalize_notify ();
2918 g_assert (gray_object_queue_is_empty (&gray_queue));
2920 num_major_sections = major.get_num_major_sections ();
2922 num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 0);
2923 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
2925 save_target = ((num_major_sections * major.section_size) + los_memory_saved) / 2;
2927 * We aim to allow the allocation of as many sections as is
2928 * necessary to reclaim save_target sections in the next
2929 * collection. We assume the collection pattern won't change.
2930 * In the last cycle, we had num_major_sections_saved for
2931 * minor_collection_sections_alloced. Assuming things won't
2932 * change, this must be the same ratio as save_target for
2933 * allowance_target, i.e.
2935 * num_major_sections_saved save_target
2936 * --------------------------------- == ----------------
2937 * minor_collection_sections_alloced allowance_target
2941 allowance_target = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major.section_size + los_memory_alloced) / (double)(num_major_sections_saved * major.section_size + los_memory_saved));
2943 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2945 minor_collection_sections_alloced = 0;
2946 last_los_memory_usage = los_memory_usage;
2948 major.finish_major_collection ();
2950 check_scan_starts ();
2952 binary_protocol_flush_buffers (FALSE);
2954 //consistency_check ();
2958 major_collection (const char *reason)
2960 if (g_getenv ("MONO_GC_NO_MAJOR")) {
2961 collect_nursery (0);
2965 current_collection_generation = GENERATION_OLD;
2966 major_do_collection (reason);
2967 current_collection_generation = -1;
2971 * When deciding if it's better to collect or to expand, keep track
2972 * of how much garbage was reclaimed with the last collection: if it's too
2974 * This is called when we could not allocate a small object.
2976 static void __attribute__((noinline))
2977 minor_collect_or_expand_inner (size_t size)
2979 int do_minor_collection = 1;
2981 g_assert (nursery_section);
2982 if (do_minor_collection) {
2984 if (collect_nursery (size))
2985 major_collection ("minor overflow");
2986 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
2988 /* this also sets the proper pointers for the next allocation */
2989 if (!search_fragment_for_size (size)) {
2991 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2992 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
2993 for (i = 0; i < last_num_pinned; ++i) {
2994 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])));
2999 //report_internal_mem_usage ();
3003 * ######################################################################
3004 * ######## Memory allocation from the OS
3005 * ######################################################################
3006 * This section of code deals with getting memory from the OS and
3007 * allocating memory for GC-internal data structures.
3008 * Internal memory can be handled with a freelist for small objects.
3014 G_GNUC_UNUSED static void
3015 report_internal_mem_usage (void)
3017 printf ("Internal memory usage:\n");
3018 mono_sgen_report_internal_mem_usage ();
3019 printf ("Pinned memory usage:\n");
3020 major.report_pinned_memory_usage ();
3024 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3025 * This must not require any lock.
3028 mono_sgen_alloc_os_memory (size_t size, int activate)
3031 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3033 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3034 size += pagesize - 1;
3035 size &= ~(pagesize - 1);
3036 ptr = mono_valloc (0, size, prot_flags);
3038 total_alloc += size;
3043 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3046 mono_sgen_free_os_memory (void *addr, size_t size)
3048 mono_vfree (addr, size);
3050 size += pagesize - 1;
3051 size &= ~(pagesize - 1);
3053 total_alloc -= size;
3057 * ######################################################################
3058 * ######## Object allocation
3059 * ######################################################################
3060 * This section of code deals with allocating memory for objects.
3061 * There are several ways:
3062 * *) allocate large objects
3063 * *) allocate normal objects
3064 * *) fast lock-free allocation
3065 * *) allocation of pinned objects
3069 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3071 /* remove from the list */
3073 prev->next = frag->next;
3075 nursery_fragments = frag->next;
3076 nursery_next = frag->fragment_start;
3077 nursery_frag_real_end = frag->fragment_end;
3079 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));
3080 frag->next = fragment_freelist;
3081 fragment_freelist = frag;
3084 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3085 * an object of size @size
3086 * Return FALSE if not found (which means we need a collection)
3089 search_fragment_for_size (size_t size)
3091 Fragment *frag, *prev;
3092 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3094 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3095 /* Clear the remaining space, pinning depends on this */
3096 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3099 for (frag = nursery_fragments; frag; frag = frag->next) {
3100 if (size <= (frag->fragment_end - frag->fragment_start)) {
3101 setup_fragment (frag, prev, size);
3110 * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3111 * This improves nursery usage.
3114 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3116 Fragment *frag, *prev, *min_prev;
3117 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));
3119 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3120 /* Clear the remaining space, pinning depends on this */
3121 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3123 min_prev = GINT_TO_POINTER (-1);
3126 for (frag = nursery_fragments; frag; frag = frag->next) {
3127 int frag_size = frag->fragment_end - frag->fragment_start;
3128 if (desired_size <= frag_size) {
3129 setup_fragment (frag, prev, desired_size);
3130 return desired_size;
3132 if (minimum_size <= frag_size)
3138 if (min_prev != GINT_TO_POINTER (-1)) {
3141 frag = min_prev->next;
3143 frag = nursery_fragments;
3145 frag_size = frag->fragment_end - frag->fragment_start;
3146 HEAVY_STAT (++stat_wasted_fragments_used);
3147 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3149 setup_fragment (frag, min_prev, minimum_size);
3157 alloc_degraded (MonoVTable *vtable, size_t size)
3159 if (need_major_collection ()) {
3161 major_collection ("degraded overflow");
3165 degraded_mode += size;
3166 return major.alloc_degraded (vtable, size);
3170 * Provide a variant that takes just the vtable for small fixed-size objects.
3171 * The aligned size is already computed and stored in vt->gc_descr.
3172 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3173 * processing. We can keep track of where objects start, for example,
3174 * so when we scan the thread stacks for pinned objects, we can start
3175 * a search for the pinned object in SCAN_START_SIZE chunks.
3178 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3180 /* FIXME: handle OOM */
3185 HEAVY_STAT (++stat_objects_alloced);
3186 if (size <= MAX_SMALL_OBJ_SIZE)
3187 HEAVY_STAT (stat_bytes_alloced += size);
3189 HEAVY_STAT (stat_bytes_alloced_los += size);
3191 size = ALIGN_UP (size);
3193 g_assert (vtable->gc_descr);
3195 if (G_UNLIKELY (collect_before_allocs)) {
3196 if (nursery_section) {
3198 collect_nursery (0);
3200 if (!degraded_mode && !search_fragment_for_size (size)) {
3202 g_assert_not_reached ();
3208 * We must already have the lock here instead of after the
3209 * fast path because we might be interrupted in the fast path
3210 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3211 * and we'll end up allocating an object in a fragment which
3212 * no longer belongs to us.
3214 * The managed allocator does not do this, but it's treated
3215 * specially by the world-stopping code.
3218 if (size > MAX_SMALL_OBJ_SIZE) {
3219 p = alloc_large_inner (vtable, size);
3221 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3223 p = (void**)TLAB_NEXT;
3224 /* FIXME: handle overflow */
3225 new_next = (char*)p + size;
3226 TLAB_NEXT = new_next;
3228 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3232 * FIXME: We might need a memory barrier here so the change to tlab_next is
3233 * visible before the vtable store.
3236 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3237 binary_protocol_alloc (p , vtable, size);
3238 g_assert (*p == NULL);
3241 g_assert (TLAB_NEXT == new_next);
3248 /* there are two cases: the object is too big or we run out of space in the TLAB */
3249 /* we also reach here when the thread does its first allocation after a minor
3250 * collection, since the tlab_ variables are initialized to NULL.
3251 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3252 * objects that need finalizers can have the high bit set in their size
3253 * so the above check fails and we can readily add the object to the queue.
3254 * This avoids taking again the GC lock when registering, but this is moot when
3255 * doing thread-local allocation, so it may not be a good idea.
3257 g_assert (TLAB_NEXT == new_next);
3258 if (TLAB_NEXT >= TLAB_REAL_END) {
3260 * Run out of space in the TLAB. When this happens, some amount of space
3261 * remains in the TLAB, but not enough to satisfy the current allocation
3262 * request. Currently, we retire the TLAB in all cases, later we could
3263 * keep it if the remaining space is above a treshold, and satisfy the
3264 * allocation directly from the nursery.
3267 /* when running in degraded mode, we continue allocing that way
3268 * for a while, to decrease the number of useless nursery collections.
3270 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3271 p = alloc_degraded (vtable, size);
3272 binary_protocol_alloc_degraded (p, vtable, size);
3276 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3277 if (size > tlab_size) {
3278 /* Allocate directly from the nursery */
3279 if (nursery_next + size >= nursery_frag_real_end) {
3280 if (!search_fragment_for_size (size)) {
3281 minor_collect_or_expand_inner (size);
3282 if (degraded_mode) {
3283 p = alloc_degraded (vtable, size);
3284 binary_protocol_alloc_degraded (p, vtable, size);
3290 p = (void*)nursery_next;
3291 nursery_next += size;
3292 if (nursery_next > nursery_frag_real_end) {
3297 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3298 memset (p, 0, size);
3300 int alloc_size = tlab_size;
3301 int available_in_nursery = nursery_frag_real_end - nursery_next;
3303 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3305 if (alloc_size >= available_in_nursery) {
3306 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3307 alloc_size = available_in_nursery;
3309 alloc_size = search_fragment_for_size_range (tlab_size, size);
3311 alloc_size = tlab_size;
3312 minor_collect_or_expand_inner (tlab_size);
3313 if (degraded_mode) {
3314 p = alloc_degraded (vtable, size);
3315 binary_protocol_alloc_degraded (p, vtable, size);
3322 /* Allocate a new TLAB from the current nursery fragment */
3323 TLAB_START = nursery_next;
3324 nursery_next += alloc_size;
3325 TLAB_NEXT = TLAB_START;
3326 TLAB_REAL_END = TLAB_START + alloc_size;
3327 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3329 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3330 memset (TLAB_START, 0, alloc_size);
3332 /* Allocate from the TLAB */
3333 p = (void*)TLAB_NEXT;
3335 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3337 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3340 /* Reached tlab_temp_end */
3342 /* record the scan start so we can find pinned objects more easily */
3343 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3344 /* we just bump tlab_temp_end as well */
3345 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3346 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3350 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3351 binary_protocol_alloc (p, vtable, size);
3358 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3364 size = ALIGN_UP (size);
3366 g_assert (vtable->gc_descr);
3367 if (size <= MAX_SMALL_OBJ_SIZE) {
3368 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3370 p = (void**)TLAB_NEXT;
3371 /* FIXME: handle overflow */
3372 new_next = (char*)p + size;
3373 TLAB_NEXT = new_next;
3375 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3379 * FIXME: We might need a memory barrier here so the change to tlab_next is
3380 * visible before the vtable store.
3383 HEAVY_STAT (++stat_objects_alloced);
3384 HEAVY_STAT (stat_bytes_alloced += size);
3386 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3387 binary_protocol_alloc (p, vtable, size);
3388 g_assert (*p == NULL);
3391 g_assert (TLAB_NEXT == new_next);
3400 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
3403 #ifndef DISABLE_CRITICAL_REGION
3405 ENTER_CRITICAL_REGION;
3406 res = mono_gc_try_alloc_obj_nolock (vtable, size);
3408 EXIT_CRITICAL_REGION;
3411 EXIT_CRITICAL_REGION;
3414 res = mono_gc_alloc_obj_nolock (vtable, size);
3420 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
3423 #ifndef DISABLE_CRITICAL_REGION
3425 ENTER_CRITICAL_REGION;
3426 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
3428 arr->max_length = max_length;
3429 EXIT_CRITICAL_REGION;
3432 EXIT_CRITICAL_REGION;
3437 arr = mono_gc_alloc_obj_nolock (vtable, size);
3438 arr->max_length = max_length;
3446 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
3449 MonoArrayBounds *bounds;
3453 arr = mono_gc_alloc_obj_nolock (vtable, size);
3454 arr->max_length = max_length;
3456 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
3457 arr->bounds = bounds;
3465 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
3468 #ifndef DISABLE_CRITICAL_REGION
3470 ENTER_CRITICAL_REGION;
3471 str = mono_gc_try_alloc_obj_nolock (vtable, size);
3474 EXIT_CRITICAL_REGION;
3477 EXIT_CRITICAL_REGION;
3482 str = mono_gc_alloc_obj_nolock (vtable, size);
3491 * To be used for interned strings and possibly MonoThread, reflection handles.
3492 * We may want to explicitly free these objects.
3495 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
3497 /* FIXME: handle OOM */
3499 size = ALIGN_UP (size);
3501 if (size > MAX_SMALL_OBJ_SIZE) {
3502 /* large objects are always pinned anyway */
3503 p = alloc_large_inner (vtable, size);
3505 DEBUG (9, g_assert (vtable->klass->inited));
3506 p = major.alloc_small_pinned_obj (size, vtable->klass->has_references);
3508 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3509 binary_protocol_alloc_pinned (p, vtable, size);
3516 * ######################################################################
3517 * ######## Finalization support
3518 * ######################################################################
3522 * this is valid for the nursery: if the object has been forwarded it means it's
3523 * still refrenced from a root. If it is pinned it's still alive as well.
3524 * Return TRUE if @obj is ready to be finalized.
3526 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3529 is_critical_finalizer (FinalizeEntry *entry)
3534 if (!mono_defaults.critical_finalizer_object)
3537 obj = entry->object;
3538 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3540 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
3544 queue_finalization_entry (FinalizeEntry *entry) {
3545 if (is_critical_finalizer (entry)) {
3546 entry->next = critical_fin_list;
3547 critical_fin_list = entry;
3549 entry->next = fin_ready_list;
3550 fin_ready_list = entry;
3554 /* LOCKING: requires that the GC lock is held */
3556 rehash_fin_table (FinalizeEntryHashTable *hash_table)
3558 FinalizeEntry **finalizable_hash = hash_table->table;
3559 mword finalizable_hash_size = hash_table->size;
3562 FinalizeEntry **new_hash;
3563 FinalizeEntry *entry, *next;
3564 int new_size = g_spaced_primes_closest (hash_table->num_registered);
3566 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
3567 for (i = 0; i < finalizable_hash_size; ++i) {
3568 for (entry = finalizable_hash [i]; entry; entry = next) {
3569 hash = mono_object_hash (entry->object) % new_size;
3571 entry->next = new_hash [hash];
3572 new_hash [hash] = entry;
3575 mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
3576 hash_table->table = new_hash;
3577 hash_table->size = new_size;
3580 /* LOCKING: requires that the GC lock is held */
3582 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
3584 if (hash_table->num_registered >= hash_table->size * 2)
3585 rehash_fin_table (hash_table);
3588 /* LOCKING: requires that the GC lock is held */
3590 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
3592 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
3593 FinalizeEntry *entry, *prev;
3595 FinalizeEntry **finalizable_hash = hash_table->table;
3596 mword finalizable_hash_size = hash_table->size;
3600 for (i = 0; i < finalizable_hash_size; ++i) {
3602 for (entry = finalizable_hash [i]; entry;) {
3603 if ((char*)entry->object >= start && (char*)entry->object < end && !major.is_object_live (entry->object)) {
3604 gboolean is_fin_ready = object_is_fin_ready (entry->object);
3605 char *copy = entry->object;
3606 copy_func ((void**)©, queue);
3609 FinalizeEntry *next;
3610 /* remove and put in fin_ready_list */
3612 prev->next = entry->next;
3614 finalizable_hash [i] = entry->next;
3616 num_ready_finalizers++;
3617 hash_table->num_registered--;
3618 queue_finalization_entry (entry);
3619 /* Make it survive */
3620 from = entry->object;
3621 entry->object = copy;
3622 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));
3626 char *from = entry->object;
3627 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
3628 FinalizeEntry *next = entry->next;
3629 unsigned int major_hash;
3630 /* remove from the list */
3632 prev->next = entry->next;
3634 finalizable_hash [i] = entry->next;
3635 hash_table->num_registered--;
3637 entry->object = copy;
3639 /* insert it into the major hash */
3640 rehash_fin_table_if_necessary (&major_finalizable_hash);
3641 major_hash = mono_object_hash ((MonoObject*) copy) %
3642 major_finalizable_hash.size;
3643 entry->next = major_finalizable_hash.table [major_hash];
3644 major_finalizable_hash.table [major_hash] = entry;
3645 major_finalizable_hash.num_registered++;
3647 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
3652 /* update pointer */
3653 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
3654 entry->object = copy;
3659 entry = entry->next;
3665 object_is_reachable (char *object, char *start, char *end)
3667 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
3668 if (object < start || object >= end)
3670 return !object_is_fin_ready (object) || major.is_object_live (object);
3673 /* LOCKING: requires that the GC lock is held */
3675 null_ephemerons_for_domain (MonoDomain *domain)
3677 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3680 MonoObject *object = (MonoObject*)current->array;
3682 if (object && !object->vtable) {
3683 EphemeronLinkNode *tmp = current;
3686 prev->next = current->next;
3688 ephemeron_list = current->next;
3690 current = current->next;
3691 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3694 current = current->next;
3699 /* LOCKING: requires that the GC lock is held */
3701 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3703 int was_in_nursery, was_promoted;
3704 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3706 Ephemeron *cur, *array_end;
3710 char *object = current->array;
3712 if (!object_is_reachable (object, start, end)) {
3713 EphemeronLinkNode *tmp = current;
3715 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
3718 prev->next = current->next;
3720 ephemeron_list = current->next;
3722 current = current->next;
3723 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3728 was_in_nursery = ptr_in_nursery (object);
3729 copy_func ((void**)&object, queue);
3730 current->array = object;
3732 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
3733 was_promoted = was_in_nursery && !ptr_in_nursery (object);
3735 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
3737 array = (MonoArray*)object;
3738 cur = mono_array_addr (array, Ephemeron, 0);
3739 array_end = cur + mono_array_length_fast (array);
3740 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3742 for (; cur < array_end; ++cur) {
3743 char *key = (char*)cur->key;
3745 if (!key || key == tombstone)
3748 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3749 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3750 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3752 if (!object_is_reachable (key, start, end)) {
3753 cur->key = tombstone;
3759 if (ptr_in_nursery (key)) {/*key was not promoted*/
3760 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
3761 mono_sgen_add_to_global_remset (&cur->key);
3763 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
3764 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
3765 mono_sgen_add_to_global_remset (&cur->value);
3770 current = current->next;
3774 /* LOCKING: requires that the GC lock is held */
3776 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3778 int nothing_marked = 1;
3779 EphemeronLinkNode *current = ephemeron_list;
3781 Ephemeron *cur, *array_end;
3784 for (current = ephemeron_list; current; current = current->next) {
3785 char *object = current->array;
3786 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
3788 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
3789 if (object < start || object >= end)
3792 /*It has to be alive*/
3793 if (!object_is_reachable (object, start, end)) {
3794 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
3798 copy_func ((void**)&object, queue);
3800 array = (MonoArray*)object;
3801 cur = mono_array_addr (array, Ephemeron, 0);
3802 array_end = cur + mono_array_length_fast (array);
3803 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3805 for (; cur < array_end; ++cur) {
3806 char *key = cur->key;
3808 if (!key || key == tombstone)
3811 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3812 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3813 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3815 if (object_is_reachable (key, start, end)) {
3816 char *value = cur->value;
3818 copy_func ((void**)&cur->key, queue);
3820 if (!object_is_reachable (value, start, end))
3822 copy_func ((void**)&cur->value, queue);
3828 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
3829 return nothing_marked;
3832 /* LOCKING: requires that the GC lock is held */
3834 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
3836 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
3837 DisappearingLink **disappearing_link_hash = hash->table;
3838 int disappearing_link_hash_size = hash->size;
3839 DisappearingLink *entry, *prev;
3841 if (!hash->num_links)
3843 for (i = 0; i < disappearing_link_hash_size; ++i) {
3845 for (entry = disappearing_link_hash [i]; entry;) {
3846 char *object = DISLINK_OBJECT (entry);
3847 if (object >= start && object < end && !major.is_object_live (object)) {
3848 gboolean track = DISLINK_TRACK (entry);
3849 if (!track && object_is_fin_ready (object)) {
3850 void **p = entry->link;
3851 DisappearingLink *old;
3853 /* remove from list */
3855 prev->next = entry->next;
3857 disappearing_link_hash [i] = entry->next;
3858 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
3860 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3865 char *copy = object;
3866 copy_func ((void**)©, queue);
3868 /* Update pointer if it's moved. If the object
3869 * has been moved out of the nursery, we need to
3870 * remove the link from the minor hash table to
3873 * FIXME: what if an object is moved earlier?
3876 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
3877 void **link = entry->link;
3878 DisappearingLink *old;
3879 /* remove from list */
3881 prev->next = entry->next;
3883 disappearing_link_hash [i] = entry->next;
3885 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3889 add_or_remove_disappearing_link ((MonoObject*)copy, link,
3890 track, GENERATION_OLD);
3892 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
3896 /* We set the track resurrection bit to
3897 * FALSE if the object is to be finalized
3898 * so that the object can be collected in
3899 * the next cycle (i.e. after it was
3902 *entry->link = HIDE_POINTER (copy,
3903 object_is_fin_ready (object) ? FALSE : track);
3904 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
3909 entry = entry->next;
3914 /* LOCKING: requires that the GC lock is held */
3916 null_links_for_domain (MonoDomain *domain, int generation)
3918 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
3919 DisappearingLink **disappearing_link_hash = hash->table;
3920 int disappearing_link_hash_size = hash->size;
3921 DisappearingLink *entry, *prev;
3923 for (i = 0; i < disappearing_link_hash_size; ++i) {
3925 for (entry = disappearing_link_hash [i]; entry; ) {
3926 char *object = DISLINK_OBJECT (entry);
3927 if (object && !((MonoObject*)object)->vtable) {
3928 DisappearingLink *next = entry->next;
3933 disappearing_link_hash [i] = next;
3935 if (*(entry->link)) {
3936 *(entry->link) = NULL;
3937 g_warning ("Disappearing link %p not freed", entry->link);
3939 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3946 entry = entry->next;
3951 /* LOCKING: requires that the GC lock is held */
3953 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
3954 FinalizeEntryHashTable *hash_table)
3956 FinalizeEntry **finalizable_hash = hash_table->table;
3957 mword finalizable_hash_size = hash_table->size;
3958 FinalizeEntry *entry, *prev;
3961 if (no_finalize || !out_size || !out_array)
3964 for (i = 0; i < finalizable_hash_size; ++i) {
3966 for (entry = finalizable_hash [i]; entry;) {
3967 if (mono_object_domain (entry->object) == domain) {
3968 FinalizeEntry *next;
3969 /* remove and put in out_array */
3971 prev->next = entry->next;
3973 finalizable_hash [i] = entry->next;
3975 hash_table->num_registered--;
3976 out_array [count ++] = entry->object;
3977 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));
3979 if (count == out_size)
3984 entry = entry->next;
3991 * mono_gc_finalizers_for_domain:
3992 * @domain: the unloading appdomain
3993 * @out_array: output array
3994 * @out_size: size of output array
3996 * Store inside @out_array up to @out_size objects that belong to the unloading
3997 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
3998 * until it returns 0.
3999 * The items are removed from the finalizer data structure, so the caller is supposed
4001 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4004 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4009 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4010 if (result < out_size) {
4011 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4012 &major_finalizable_hash);
4020 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4022 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4023 FinalizeEntry **finalizable_hash;
4024 mword finalizable_hash_size;
4025 FinalizeEntry *entry, *prev;
4029 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4030 hash = mono_object_hash (obj);
4032 rehash_fin_table_if_necessary (hash_table);
4033 finalizable_hash = hash_table->table;
4034 finalizable_hash_size = hash_table->size;
4035 hash %= finalizable_hash_size;
4037 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4038 if (entry->object == obj) {
4040 /* remove from the list */
4042 prev->next = entry->next;
4044 finalizable_hash [hash] = entry->next;
4045 hash_table->num_registered--;
4046 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));
4047 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4055 /* request to deregister, but already out of the list */
4059 entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4060 entry->object = obj;
4061 entry->next = finalizable_hash [hash];
4062 finalizable_hash [hash] = entry;
4063 hash_table->num_registered++;
4064 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)));
4069 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4071 if (ptr_in_nursery (obj))
4072 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4074 register_for_finalization (obj, user_data, GENERATION_OLD);
4078 rehash_dislink (DisappearingLinkHashTable *hash_table)
4080 DisappearingLink **disappearing_link_hash = hash_table->table;
4081 int disappearing_link_hash_size = hash_table->size;
4084 DisappearingLink **new_hash;
4085 DisappearingLink *entry, *next;
4086 int new_size = g_spaced_primes_closest (hash_table->num_links);
4088 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4089 for (i = 0; i < disappearing_link_hash_size; ++i) {
4090 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4091 hash = mono_aligned_addr_hash (entry->link) % new_size;
4093 entry->next = new_hash [hash];
4094 new_hash [hash] = entry;
4097 mono_sgen_free_internal_dynamic (disappearing_link_hash,
4098 disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4099 hash_table->table = new_hash;
4100 hash_table->size = new_size;
4103 /* LOCKING: assumes the GC lock is held */
4105 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4107 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4108 DisappearingLink *entry, *prev;
4110 DisappearingLink **disappearing_link_hash = hash_table->table;
4111 int disappearing_link_hash_size = hash_table->size;
4113 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4114 rehash_dislink (hash_table);
4115 disappearing_link_hash = hash_table->table;
4116 disappearing_link_hash_size = hash_table->size;
4118 /* FIXME: add check that link is not in the heap */
4119 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4120 entry = disappearing_link_hash [hash];
4122 for (; entry; entry = entry->next) {
4123 /* link already added */
4124 if (link == entry->link) {
4125 /* NULL obj means remove */
4128 prev->next = entry->next;
4130 disappearing_link_hash [hash] = entry->next;
4131 hash_table->num_links--;
4132 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4133 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4136 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4144 entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4145 *link = HIDE_POINTER (obj, track);
4147 entry->next = disappearing_link_hash [hash];
4148 disappearing_link_hash [hash] = entry;
4149 hash_table->num_links++;
4150 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)));
4153 /* LOCKING: assumes the GC lock is held */
4155 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4157 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4158 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4160 if (ptr_in_nursery (obj))
4161 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4163 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4168 mono_gc_invoke_finalizers (void)
4170 FinalizeEntry *entry = NULL;
4171 gboolean entry_is_critical = FALSE;
4174 /* FIXME: batch to reduce lock contention */
4175 while (fin_ready_list || critical_fin_list) {
4179 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4181 /* We have finalized entry in the last
4182 interation, now we need to remove it from
4185 *list = entry->next;
4187 FinalizeEntry *e = *list;
4188 while (e->next != entry)
4190 e->next = entry->next;
4192 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4196 /* Now look for the first non-null entry. */
4197 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4200 entry_is_critical = FALSE;
4202 entry_is_critical = TRUE;
4203 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4208 g_assert (entry->object);
4209 num_ready_finalizers--;
4210 obj = entry->object;
4211 entry->object = NULL;
4212 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4220 g_assert (entry->object == NULL);
4222 /* the object is on the stack so it is pinned */
4223 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4224 mono_gc_run_finalize (obj, NULL);
4231 mono_gc_pending_finalizers (void)
4233 return fin_ready_list || critical_fin_list;
4236 /* Negative value to remove */
4238 mono_gc_add_memory_pressure (gint64 value)
4240 /* FIXME: Use interlocked functions */
4242 memory_pressure += value;
4247 mono_sgen_register_major_sections_alloced (int num_sections)
4249 minor_collection_sections_alloced += num_sections;
4253 mono_sgen_get_minor_collection_allowance (void)
4255 return minor_collection_allowance;
4259 * ######################################################################
4260 * ######## registered roots support
4261 * ######################################################################
4265 rehash_roots (gboolean pinned)
4269 RootRecord **new_hash;
4270 RootRecord *entry, *next;
4273 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4274 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4275 for (i = 0; i < roots_hash_size [pinned]; ++i) {
4276 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4277 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4279 entry->next = new_hash [hash];
4280 new_hash [hash] = entry;
4283 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4284 roots_hash [pinned] = new_hash;
4285 roots_hash_size [pinned] = new_size;
4289 find_root (int root_type, char *start, guint32 addr_hash)
4291 RootRecord *new_root;
4293 guint32 hash = addr_hash % roots_hash_size [root_type];
4294 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4295 /* we allow changing the size and the descriptor (for thread statics etc) */
4296 if (new_root->start_root == start) {
4305 * We do not coalesce roots.
4308 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4310 RootRecord *new_root;
4311 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
4314 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4315 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
4318 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4319 new_root = find_root (i, start, addr_hash);
4320 /* we allow changing the size and the descriptor (for thread statics etc) */
4322 size_t old_size = new_root->end_root - new_root->start_root;
4323 new_root->end_root = new_root->start_root + size;
4324 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
4325 ((new_root->root_desc == 0) && (descr == NULL)));
4326 new_root->root_desc = (mword)descr;
4328 roots_size -= old_size;
4333 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
4335 new_root->start_root = start;
4336 new_root->end_root = new_root->start_root + size;
4337 new_root->root_desc = (mword)descr;
4339 hash = addr_hash % roots_hash_size [root_type];
4340 num_roots_entries [root_type]++;
4341 new_root->next = roots_hash [root_type] [hash];
4342 roots_hash [root_type][hash] = new_root;
4343 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));
4353 mono_gc_register_root (char *start, size_t size, void *descr)
4355 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4359 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4361 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4365 mono_gc_deregister_root (char* addr)
4367 RootRecord *tmp, *prev;
4368 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
4372 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4373 hash = addr_hash % roots_hash_size [root_type];
4374 tmp = roots_hash [root_type][hash];
4377 if (tmp->start_root == (char*)addr) {
4379 prev->next = tmp->next;
4381 roots_hash [root_type][hash] = tmp->next;
4382 roots_size -= (tmp->end_root - tmp->start_root);
4383 num_roots_entries [root_type]--;
4384 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
4385 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
4396 * ######################################################################
4397 * ######## Thread handling (stop/start code)
4398 * ######################################################################
4401 /* FIXME: handle large/small config */
4402 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
4404 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
4406 #if USE_SIGNAL_BASED_START_STOP_WORLD
4408 static MonoSemType suspend_ack_semaphore;
4409 static MonoSemType *suspend_ack_semaphore_ptr;
4410 static unsigned int global_stop_count = 0;
4412 static sigset_t suspend_signal_mask;
4413 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4415 /* LOCKING: assumes the GC lock is held */
4417 mono_sgen_get_thread_table (void)
4419 return thread_table;
4423 mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
4425 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
4426 SgenThreadInfo *info;
4428 info = thread_table [hash];
4429 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
4436 update_current_thread_stack (void *start)
4438 void *ptr = cur_thread_regs;
4439 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
4441 info->stack_start = align_pointer (&ptr);
4442 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
4443 ARCH_STORE_REGS (ptr);
4444 info->stopped_regs = ptr;
4445 if (gc_callbacks.thread_suspend_func)
4446 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
4450 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
4451 * have cross-domain checks in the write barrier.
4453 //#define XDOMAIN_CHECKS_IN_WBARRIER
4455 #ifndef SGEN_BINARY_PROTOCOL
4456 #ifndef HEAVY_STATISTICS
4457 #define MANAGED_ALLOCATION
4458 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
4459 #define MANAGED_WBARRIER
4465 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
4468 mono_sgen_wait_for_suspend_ack (int count)
4472 for (i = 0; i < count; ++i) {
4473 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
4474 if (errno != EINTR) {
4475 g_error ("sem_wait ()");
4482 restart_threads_until_none_in_managed_allocator (void)
4484 SgenThreadInfo *info;
4485 int i, result, num_threads_died = 0;
4486 int sleep_duration = -1;
4489 int restart_count = 0, restarted_count = 0;
4490 /* restart all threads that stopped in the
4492 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4493 for (info = thread_table [i]; info; info = info->next) {
4496 if (!info->stack_start || info->in_critical_region ||
4497 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
4498 binary_protocol_thread_restart ((gpointer)info->id);
4499 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4500 result = thread_resume (pthread_mach_thread_np (info->id));
4502 result = pthread_kill (info->id, restart_signal_num);
4510 /* we set the stopped_ip to
4511 NULL for threads which
4512 we're not restarting so
4513 that we can easily identify
4515 info->stopped_ip = NULL;
4516 info->stopped_domain = NULL;
4520 /* if no threads were restarted, we're done */
4521 if (restart_count == 0)
4524 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4525 /* mach thread_resume is synchronous so we dont need to wait for them */
4527 /* wait for the threads to signal their restart */
4528 mono_sgen_wait_for_suspend_ack (restart_count);
4531 if (sleep_duration < 0) {
4535 g_usleep (sleep_duration);
4536 sleep_duration += 10;
4539 /* stop them again */
4540 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4541 for (info = thread_table [i]; info; info = info->next) {
4542 if (info->skip || info->stopped_ip == NULL)
4544 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4545 result = thread_suspend (pthread_mach_thread_np (info->id));
4547 result = pthread_kill (info->id, suspend_signal_num);
4556 /* some threads might have died */
4557 num_threads_died += restart_count - restarted_count;
4558 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4559 /* mach thread_resume is synchronous so we dont need to wait for them */
4561 /* wait for the threads to signal their suspension
4563 mono_sgen_wait_for_suspend_ack (restart_count);
4567 return num_threads_died;
4570 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
4572 suspend_handler (int sig, siginfo_t *siginfo, void *context)
4574 SgenThreadInfo *info;
4577 int old_errno = errno;
4578 gpointer regs [ARCH_NUM_REGS];
4579 gpointer stack_start;
4580 ucontext_t *ctx = (ucontext_t*)context;
4582 id = pthread_self ();
4583 info = mono_sgen_thread_info_lookup (id);
4584 info->stopped_domain = mono_domain_get ();
4585 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (ctx);
4586 stop_count = global_stop_count;
4587 /* duplicate signal */
4588 if (0 && info->stop_count == stop_count) {
4592 #ifdef HAVE_KW_THREAD
4593 /* update the remset info in the thread data structure */
4594 info->remset = remembered_set;
4596 stack_start = (char*) ARCH_SIGCTX_SP (ctx) - REDZONE_SIZE;
4597 /* If stack_start is not within the limits, then don't set it
4598 in info and we will be restarted. */
4599 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
4600 info->stack_start = stack_start;
4602 ARCH_COPY_SIGCTX_REGS (regs, ctx);
4603 info->stopped_regs = regs;
4605 g_assert (!info->stack_start);
4608 /* Notify the JIT */
4609 if (gc_callbacks.thread_suspend_func)
4610 gc_callbacks.thread_suspend_func (info->runtime_data, ctx);
4612 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4613 /* notify the waiting thread */
4614 MONO_SEM_POST (suspend_ack_semaphore_ptr);
4615 info->stop_count = stop_count;
4617 /* wait until we receive the restart signal */
4620 sigsuspend (&suspend_signal_mask);
4621 } while (info->signal != restart_signal_num);
4623 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4624 /* notify the waiting thread */
4625 MONO_SEM_POST (suspend_ack_semaphore_ptr);
4631 restart_handler (int sig)
4633 SgenThreadInfo *info;
4634 int old_errno = errno;
4636 info = mono_sgen_thread_info_lookup (pthread_self ());
4637 info->signal = restart_signal_num;
4638 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4644 acquire_gc_locks (void)
4650 release_gc_locks (void)
4652 UNLOCK_INTERRUPTION;
4655 static TV_DECLARE (stop_world_time);
4656 static unsigned long max_pause_usec = 0;
4658 /* LOCKING: assumes the GC lock is held */
4664 acquire_gc_locks ();
4666 update_current_thread_stack (&count);
4668 global_stop_count++;
4669 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 ()));
4670 TV_GETTIME (stop_world_time);
4671 count = mono_sgen_thread_handshake (suspend_signal_num);
4672 count -= restart_threads_until_none_in_managed_allocator ();
4673 g_assert (count >= 0);
4674 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
4678 /* LOCKING: assumes the GC lock is held */
4680 restart_world (void)
4683 SgenThreadInfo *info;
4684 TV_DECLARE (end_sw);
4687 /* notify the profiler of the leftovers */
4688 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
4689 if (moved_objects_idx) {
4690 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
4691 moved_objects_idx = 0;
4694 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4695 for (info = thread_table [i]; info; info = info->next) {
4696 info->stack_start = NULL;
4697 info->stopped_regs = NULL;
4701 release_gc_locks ();
4703 count = mono_sgen_thread_handshake (restart_signal_num);
4704 TV_GETTIME (end_sw);
4705 usec = TV_ELAPSED (stop_world_time, end_sw);
4706 max_pause_usec = MAX (usec, max_pause_usec);
4707 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
4711 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
4714 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
4716 gc_callbacks = *callbacks;
4720 mono_gc_get_gc_callbacks ()
4722 return &gc_callbacks;
4725 /* Variables holding start/end nursery so it won't have to be passed at every call */
4726 static void *scan_area_arg_start, *scan_area_arg_end;
4729 mono_gc_conservatively_scan_area (void *start, void *end)
4731 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
4735 mono_gc_scan_object (void *obj)
4737 g_assert_not_reached ();
4738 if (current_collection_generation == GENERATION_NURSERY)
4739 major.copy_object (&obj, &gray_queue);
4741 major.copy_or_mark_object (&obj, &gray_queue);
4746 * Mark from thread stacks and registers.
4749 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
4752 SgenThreadInfo *info;
4754 scan_area_arg_start = start_nursery;
4755 scan_area_arg_end = end_nursery;
4757 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4758 for (info = thread_table [i]; info; info = info->next) {
4760 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));
4763 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));
4764 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
4765 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
4767 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
4770 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
4771 start_nursery, end_nursery, PIN_TYPE_STACK);
4777 find_pinning_ref_from_thread (char *obj, size_t size)
4780 SgenThreadInfo *info;
4781 char *endobj = obj + size;
4783 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4784 for (info = thread_table [i]; info; info = info->next) {
4785 char **start = (char**)info->stack_start;
4788 while (start < (char**)info->stack_end) {
4789 if (*start >= obj && *start < endobj) {
4790 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));
4795 /* FIXME: check info->stopped_regs */
4801 ptr_on_stack (void *ptr)
4803 gpointer stack_start = &stack_start;
4804 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
4806 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
4812 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
4819 HEAVY_STAT (++stat_global_remsets_processed);
4821 HEAVY_STAT (++stat_local_remsets_processed);
4823 /* FIXME: exclude stack locations */
4824 switch ((*p) & REMSET_TYPE_MASK) {
4825 case REMSET_LOCATION:
4827 //__builtin_prefetch (ptr);
4828 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
4829 gpointer old = *ptr;
4830 major.copy_object (ptr, queue);
4831 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
4833 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
4834 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4836 * If the object is pinned, each reference to it from nonpinned objects
4837 * becomes part of the global remset, which can grow very large.
4839 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4840 mono_sgen_add_to_global_remset (ptr);
4843 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
4847 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4848 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4851 while (count-- > 0) {
4852 major.copy_object (ptr, queue);
4853 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
4854 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
4855 mono_sgen_add_to_global_remset (ptr);
4860 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4861 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4863 major.minor_scan_object ((char*)ptr, queue);
4865 case REMSET_VTYPE: {
4866 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4867 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4872 ptr = (void**) major.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
4876 g_assert_not_reached ();
4881 #ifdef HEAVY_STATISTICS
4883 collect_store_remsets (RememberedSet *remset, mword *bumper)
4885 mword *p = remset->data;
4890 while (p < remset->store_next) {
4891 switch ((*p) & REMSET_TYPE_MASK) {
4892 case REMSET_LOCATION:
4895 ++stat_saved_remsets_1;
4897 if (*p == last1 || *p == last2) {
4898 ++stat_saved_remsets_2;
4915 g_assert_not_reached ();
4925 RememberedSet *remset;
4927 SgenThreadInfo *info;
4929 mword *addresses, *bumper, *p, *r;
4931 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4932 for (info = thread_table [i]; info; info = info->next) {
4933 for (remset = info->remset; remset; remset = remset->next)
4934 size += remset->store_next - remset->data;
4937 for (remset = freed_thread_remsets; remset; remset = remset->next)
4938 size += remset->store_next - remset->data;
4939 for (remset = global_remset; remset; remset = remset->next)
4940 size += remset->store_next - remset->data;
4942 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
4944 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4945 for (info = thread_table [i]; info; info = info->next) {
4946 for (remset = info->remset; remset; remset = remset->next)
4947 bumper = collect_store_remsets (remset, bumper);
4950 for (remset = global_remset; remset; remset = remset->next)
4951 bumper = collect_store_remsets (remset, bumper);
4952 for (remset = freed_thread_remsets; remset; remset = remset->next)
4953 bumper = collect_store_remsets (remset, bumper);
4955 g_assert (bumper <= addresses + size);
4957 stat_store_remsets += bumper - addresses;
4959 sort_addresses ((void**)addresses, bumper - addresses);
4962 while (r < bumper) {
4968 stat_store_remsets_unique += p - addresses;
4970 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
4975 clear_thread_store_remset_buffer (SgenThreadInfo *info)
4977 *info->store_remset_buffer_index_addr = 0;
4978 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
4982 remset_byte_size (RememberedSet *remset)
4984 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
4988 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
4991 SgenThreadInfo *info;
4992 RememberedSet *remset;
4993 GenericStoreRememberedSet *store_remset;
4994 mword *p, *next_p, *store_pos;
4996 #ifdef HEAVY_STATISTICS
5000 /* the global one */
5001 for (remset = global_remset; remset; remset = remset->next) {
5002 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));
5003 store_pos = remset->data;
5004 for (p = remset->data; p < remset->store_next; p = next_p) {
5005 void **ptr = (void**)p [0];
5007 /*Ignore previously processed remset.*/
5008 if (!global_remset_location_was_not_added (ptr)) {
5013 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5016 * Clear global remsets of locations which no longer point to the
5017 * nursery. Otherwise, they could grow indefinitely between major
5020 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5022 if (ptr_in_nursery (*ptr)) {
5023 *store_pos ++ = p [0];
5024 HEAVY_STAT (++stat_global_remsets_readded);
5028 /* Truncate the remset */
5029 remset->store_next = store_pos;
5032 /* the generic store ones */
5033 store_remset = generic_store_remsets;
5034 while (store_remset) {
5035 GenericStoreRememberedSet *next = store_remset->next;
5037 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5038 gpointer addr = store_remset->data [i];
5040 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5043 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5045 store_remset = next;
5047 generic_store_remsets = NULL;
5049 /* the per-thread ones */
5050 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5051 for (info = thread_table [i]; info; info = info->next) {
5052 RememberedSet *next;
5054 for (remset = info->remset; remset; remset = next) {
5055 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));
5056 for (p = remset->data; p < remset->store_next;)
5057 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5058 remset->store_next = remset->data;
5059 next = remset->next;
5060 remset->next = NULL;
5061 if (remset != info->remset) {
5062 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5063 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5066 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5067 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5068 clear_thread_store_remset_buffer (info);
5072 /* the freed thread ones */
5073 while (freed_thread_remsets) {
5074 RememberedSet *next;
5075 remset = freed_thread_remsets;
5076 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));
5077 for (p = remset->data; p < remset->store_next;)
5078 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5079 next = remset->next;
5080 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5081 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5082 freed_thread_remsets = next;
5087 * Clear the info in the remembered sets: we're doing a major collection, so
5088 * the per-thread ones are not needed and the global ones will be reconstructed
5092 clear_remsets (void)
5095 SgenThreadInfo *info;
5096 RememberedSet *remset, *next;
5098 /* the global list */
5099 for (remset = global_remset; remset; remset = next) {
5100 remset->store_next = remset->data;
5101 next = remset->next;
5102 remset->next = NULL;
5103 if (remset != global_remset) {
5104 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5105 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5108 /* the generic store ones */
5109 while (generic_store_remsets) {
5110 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5111 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5112 generic_store_remsets = gs_next;
5114 /* the per-thread ones */
5115 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5116 for (info = thread_table [i]; info; info = info->next) {
5117 for (remset = info->remset; remset; remset = next) {
5118 remset->store_next = remset->data;
5119 next = remset->next;
5120 remset->next = NULL;
5121 if (remset != info->remset) {
5122 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5123 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5126 clear_thread_store_remset_buffer (info);
5130 /* the freed thread ones */
5131 while (freed_thread_remsets) {
5132 next = freed_thread_remsets->next;
5133 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5134 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5135 freed_thread_remsets = next;
5140 * Clear the thread local TLAB variables for all threads.
5145 SgenThreadInfo *info;
5148 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5149 for (info = thread_table [i]; info; info = info->next) {
5150 /* A new TLAB will be allocated when the thread does its first allocation */
5151 *info->tlab_start_addr = NULL;
5152 *info->tlab_next_addr = NULL;
5153 *info->tlab_temp_end_addr = NULL;
5154 *info->tlab_real_end_addr = NULL;
5159 /* LOCKING: assumes the GC lock is held */
5160 static SgenThreadInfo*
5161 gc_register_current_thread (void *addr)
5164 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5165 #ifndef HAVE_KW_THREAD
5166 SgenThreadInfo *__thread_info__ = info;
5172 memset (info, 0, sizeof (SgenThreadInfo));
5173 #ifndef HAVE_KW_THREAD
5174 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5176 g_assert (!pthread_getspecific (thread_info_key));
5177 pthread_setspecific (thread_info_key, info);
5182 info->id = ARCH_GET_THREAD ();
5183 info->stop_count = -1;
5186 info->stack_start = NULL;
5187 info->tlab_start_addr = &TLAB_START;
5188 info->tlab_next_addr = &TLAB_NEXT;
5189 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5190 info->tlab_real_end_addr = &TLAB_REAL_END;
5191 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5192 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5193 info->stopped_ip = NULL;
5194 info->stopped_domain = NULL;
5195 info->stopped_regs = NULL;
5197 binary_protocol_thread_register ((gpointer)info->id);
5199 #ifdef HAVE_KW_THREAD
5200 tlab_next_addr = &tlab_next;
5201 store_remset_buffer_index_addr = &store_remset_buffer_index;
5204 /* try to get it with attributes first */
5205 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5209 pthread_attr_t attr;
5210 pthread_getattr_np (pthread_self (), &attr);
5211 pthread_attr_getstack (&attr, &sstart, &size);
5212 info->stack_start_limit = sstart;
5213 info->stack_end = (char*)sstart + size;
5214 pthread_attr_destroy (&attr);
5216 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5217 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5218 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5221 /* FIXME: we assume the stack grows down */
5222 gsize stack_bottom = (gsize)addr;
5223 stack_bottom += 4095;
5224 stack_bottom &= ~4095;
5225 info->stack_end = (char*)stack_bottom;
5229 #ifdef HAVE_KW_THREAD
5230 stack_end = info->stack_end;
5233 /* hash into the table */
5234 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
5235 info->next = thread_table [hash];
5236 thread_table [hash] = info;
5238 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
5239 pthread_setspecific (remembered_set_key, info->remset);
5240 #ifdef HAVE_KW_THREAD
5241 remembered_set = info->remset;
5244 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5245 STORE_REMSET_BUFFER_INDEX = 0;
5247 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
5249 if (gc_callbacks.thread_attach_func)
5250 info->runtime_data = gc_callbacks.thread_attach_func ();
5256 add_generic_store_remset_from_buffer (gpointer *buffer)
5258 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5259 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5260 remset->next = generic_store_remsets;
5261 generic_store_remsets = remset;
5265 unregister_current_thread (void)
5268 SgenThreadInfo *prev = NULL;
5270 RememberedSet *rset;
5271 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
5273 binary_protocol_thread_unregister ((gpointer)id);
5275 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5276 p = thread_table [hash];
5278 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
5279 while (!ARCH_THREAD_EQUALS (p->id, id)) {
5284 thread_table [hash] = p->next;
5286 prev->next = p->next;
5289 if (freed_thread_remsets) {
5290 for (rset = p->remset; rset->next; rset = rset->next)
5292 rset->next = freed_thread_remsets;
5293 freed_thread_remsets = p->remset;
5295 freed_thread_remsets = p->remset;
5298 if (*p->store_remset_buffer_index_addr)
5299 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5300 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5305 unregister_thread (void *k)
5307 g_assert (!mono_domain_get ());
5309 unregister_current_thread ();
5314 mono_gc_register_thread (void *baseptr)
5316 SgenThreadInfo *info;
5320 info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5322 info = gc_register_current_thread (baseptr);
5324 return info != NULL;
5327 #if USE_PTHREAD_INTERCEPT
5330 void *(*start_routine) (void *);
5333 MonoSemType registered;
5334 } SgenThreadStartInfo;
5337 gc_start_thread (void *arg)
5339 SgenThreadStartInfo *start_info = arg;
5340 SgenThreadInfo* info;
5341 void *t_arg = start_info->arg;
5342 void *(*start_func) (void*) = start_info->start_routine;
5347 info = gc_register_current_thread (&result);
5349 post_result = MONO_SEM_POST (&(start_info->registered));
5350 g_assert (!post_result);
5351 result = start_func (t_arg);
5352 g_assert (!mono_domain_get ());
5354 * this is done by the pthread key dtor
5356 unregister_current_thread ();
5364 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5366 SgenThreadStartInfo *start_info;
5369 start_info = malloc (sizeof (SgenThreadStartInfo));
5372 MONO_SEM_INIT (&(start_info->registered), 0);
5373 start_info->arg = arg;
5374 start_info->start_routine = start_routine;
5376 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
5378 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
5379 /*if (EINTR != errno) ABORT("sem_wait failed"); */
5382 MONO_SEM_DESTROY (&(start_info->registered));
5388 mono_gc_pthread_join (pthread_t thread, void **retval)
5390 return pthread_join (thread, retval);
5394 mono_gc_pthread_detach (pthread_t thread)
5396 return pthread_detach (thread);
5399 #endif /* USE_PTHREAD_INTERCEPT */
5402 * ######################################################################
5403 * ######## Write barriers
5404 * ######################################################################
5407 static RememberedSet*
5408 alloc_remset (int size, gpointer id) {
5409 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
5410 res->store_next = res->data;
5411 res->end_set = res->data + size;
5413 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
5418 * Note: the write barriers first do the needed GC work and then do the actual store:
5419 * this way the value is visible to the conservative GC scan after the write barrier
5420 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
5421 * the conservative scan, otherwise by the remembered set scan.
5424 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
5428 HEAVY_STAT (++stat_wbarrier_set_field);
5429 if (ptr_in_nursery (field_ptr)) {
5430 *(void**)field_ptr = value;
5433 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
5435 if (use_cardtable) {
5436 sgen_card_table_mark_address ((mword)field_ptr);
5438 rs = REMEMBERED_SET;
5439 if (rs->store_next < rs->end_set) {
5440 *(rs->store_next++) = (mword)field_ptr;
5441 *(void**)field_ptr = value;
5445 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5446 rs->next = REMEMBERED_SET;
5447 REMEMBERED_SET = rs;
5448 #ifdef HAVE_KW_THREAD
5449 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5451 *(rs->store_next++) = (mword)field_ptr;
5453 *(void**)field_ptr = value;
5458 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
5462 HEAVY_STAT (++stat_wbarrier_set_arrayref);
5463 if (ptr_in_nursery (slot_ptr)) {
5464 *(void**)slot_ptr = value;
5467 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
5469 if (use_cardtable) {
5470 sgen_card_table_mark_address ((mword)slot_ptr);
5472 rs = REMEMBERED_SET;
5473 if (rs->store_next < rs->end_set) {
5474 *(rs->store_next++) = (mword)slot_ptr;
5475 *(void**)slot_ptr = value;
5479 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5480 rs->next = REMEMBERED_SET;
5481 REMEMBERED_SET = rs;
5482 #ifdef HAVE_KW_THREAD
5483 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5485 *(rs->store_next++) = (mword)slot_ptr;
5487 *(void**)slot_ptr = value;
5492 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
5496 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
5498 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5499 if (ptr_in_nursery (dest_ptr)) {
5503 if (use_cardtable) {
5504 sgen_card_table_mark_range ((mword)dest_ptr, count * sizeof (gpointer));
5506 rs = REMEMBERED_SET;
5507 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
5508 if (rs->store_next + 1 < rs->end_set) {
5509 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5510 *(rs->store_next++) = count;
5514 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5515 rs->next = REMEMBERED_SET;
5516 REMEMBERED_SET = rs;
5517 #ifdef HAVE_KW_THREAD
5518 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5520 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5521 *(rs->store_next++) = count;
5526 static char *found_obj;
5529 find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
5531 if (ptr >= obj && ptr < obj + size) {
5532 g_assert (!found_obj);
5537 /* for use in the debugger */
5538 char* find_object_for_ptr (char *ptr);
5540 find_object_for_ptr (char *ptr)
5544 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
5546 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
5547 (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
5552 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
5553 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
5554 return bigobj->data;
5558 * Very inefficient, but this is debugging code, supposed to
5559 * be called from gdb, so we don't care.
5562 major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
5567 evacuate_remset_buffer (void)
5572 buffer = STORE_REMSET_BUFFER;
5574 add_generic_store_remset_from_buffer (buffer);
5575 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5577 STORE_REMSET_BUFFER_INDEX = 0;
5581 mono_gc_wbarrier_generic_nostore (gpointer ptr)
5587 HEAVY_STAT (++stat_wbarrier_generic_store);
5589 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
5590 /* FIXME: ptr_in_heap must be called with the GC lock held */
5591 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
5592 char *start = find_object_for_ptr (ptr);
5593 MonoObject *value = *(MonoObject**)ptr;
5597 MonoObject *obj = (MonoObject*)start;
5598 if (obj->vtable->domain != value->vtable->domain)
5599 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
5605 if (*(gpointer*)ptr)
5606 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
5608 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
5609 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
5613 if (use_cardtable) {
5614 if (ptr_in_nursery(*(gpointer*)ptr))
5615 sgen_card_table_mark_address ((mword)ptr);
5621 buffer = STORE_REMSET_BUFFER;
5622 index = STORE_REMSET_BUFFER_INDEX;
5623 /* This simple optimization eliminates a sizable portion of
5624 entries. Comparing it to the last but one entry as well
5625 doesn't eliminate significantly more entries. */
5626 if (buffer [index] == ptr) {
5631 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
5632 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
5635 if (index >= STORE_REMSET_BUFFER_SIZE) {
5636 evacuate_remset_buffer ();
5637 index = STORE_REMSET_BUFFER_INDEX;
5638 g_assert (index == 0);
5641 buffer [index] = ptr;
5642 STORE_REMSET_BUFFER_INDEX = index;
5648 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
5650 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
5651 *(void**)ptr = value;
5652 if (ptr_in_nursery (value))
5653 mono_gc_wbarrier_generic_nostore (ptr);
5656 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
5658 mword *dest = _dest;
5663 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
5668 size -= SIZEOF_VOID_P;
5675 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
5678 size_t size = count * mono_class_value_size (klass, NULL);
5680 HEAVY_STAT (++stat_wbarrier_value_copy);
5681 g_assert (klass->valuetype);
5683 memmove (dest, src, size);
5684 if (use_cardtable) {
5685 sgen_card_table_mark_range ((mword)dest, size);
5687 rs = REMEMBERED_SET;
5688 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !klass->has_references) {
5692 g_assert (klass->gc_descr_inited);
5693 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));
5695 if (rs->store_next + 3 < rs->end_set) {
5696 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5697 *(rs->store_next++) = (mword)klass->gc_descr;
5698 *(rs->store_next++) = (mword)count;
5702 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5703 rs->next = REMEMBERED_SET;
5704 REMEMBERED_SET = rs;
5705 #ifdef HAVE_KW_THREAD
5706 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5708 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5709 *(rs->store_next++) = (mword)klass->gc_descr;
5710 *(rs->store_next++) = (mword)count;
5716 * mono_gc_wbarrier_object_copy:
5718 * Write barrier to call when obj is the result of a clone or copy of an object.
5721 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
5727 HEAVY_STAT (++stat_wbarrier_object_copy);
5728 rs = REMEMBERED_SET;
5729 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
5730 size = mono_object_class (obj)->instance_size;
5732 /* do not copy the sync state */
5733 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
5734 size - sizeof (MonoObject));
5735 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
5739 if (rs->store_next < rs->end_set) {
5740 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5744 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5745 rs->next = REMEMBERED_SET;
5746 REMEMBERED_SET = rs;
5747 #ifdef HAVE_KW_THREAD
5748 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5750 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5755 * ######################################################################
5756 * ######## Collector debugging
5757 * ######################################################################
5760 const char*descriptor_types [] = {
5772 describe_ptr (char *ptr)
5778 if (ptr_in_nursery (ptr)) {
5779 printf ("Pointer inside nursery.\n");
5781 if (major.ptr_is_in_non_pinned_space (ptr)) {
5782 printf ("Pointer inside oldspace.\n");
5783 } else if (major.obj_is_from_pinned_alloc (ptr)) {
5784 printf ("Pointer is inside a pinned chunk.\n");
5786 printf ("Pointer unknown.\n");
5791 if (object_is_pinned (ptr))
5792 printf ("Object is pinned.\n");
5794 if (object_is_forwarded (ptr))
5795 printf ("Object is forwared.\n");
5797 // FIXME: Handle pointers to the inside of objects
5798 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
5800 printf ("VTable: %p\n", vtable);
5801 if (vtable == NULL) {
5802 printf ("VTable is invalid (empty).\n");
5805 if (ptr_in_nursery (vtable)) {
5806 printf ("VTable is invalid (points inside nursery).\n");
5809 printf ("Class: %s\n", vtable->klass->name);
5811 desc = ((GCVTable*)vtable)->desc;
5812 printf ("Descriptor: %lx\n", (long)desc);
5815 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
5819 find_in_remset_loc (mword *p, char *addr, gboolean *found)
5825 switch ((*p) & REMSET_TYPE_MASK) {
5826 case REMSET_LOCATION:
5827 if (*p == (mword)addr)
5831 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5833 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5837 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5838 count = safe_object_get_size ((MonoObject*)ptr);
5839 count = ALIGN_UP (count);
5840 count /= sizeof (mword);
5841 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5845 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5849 switch (desc & 0x7) {
5850 case DESC_TYPE_RUN_LENGTH:
5851 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
5853 case DESC_TYPE_SMALL_BITMAP:
5854 OBJ_BITMAP_SIZE (skip_size, desc, start);
5858 g_assert_not_reached ();
5861 /* The descriptor includes the size of MonoObject */
5862 skip_size -= sizeof (MonoObject);
5864 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
5869 g_assert_not_reached ();
5875 * Return whenever ADDR occurs in the remembered sets
5878 find_in_remsets (char *addr)
5881 SgenThreadInfo *info;
5882 RememberedSet *remset;
5883 GenericStoreRememberedSet *store_remset;
5885 gboolean found = FALSE;
5887 /* the global one */
5888 for (remset = global_remset; remset; remset = remset->next) {
5889 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));
5890 for (p = remset->data; p < remset->store_next;) {
5891 p = find_in_remset_loc (p, addr, &found);
5897 /* the generic store ones */
5898 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
5899 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5900 if (store_remset->data [i] == addr)
5905 /* the per-thread ones */
5906 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5907 for (info = thread_table [i]; info; info = info->next) {
5909 for (remset = info->remset; remset; remset = remset->next) {
5910 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));
5911 for (p = remset->data; p < remset->store_next;) {
5912 p = find_in_remset_loc (p, addr, &found);
5917 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
5918 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
5924 /* the freed thread ones */
5925 for (remset = freed_thread_remsets; remset; remset = remset->next) {
5926 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));
5927 for (p = remset->data; p < remset->store_next;) {
5928 p = find_in_remset_loc (p, addr, &found);
5937 static gboolean missing_remsets;
5940 * We let a missing remset slide if the target object is pinned,
5941 * because the store might have happened but the remset not yet added,
5942 * but in that case the target must be pinned. We might theoretically
5943 * miss some missing remsets this way, but it's very unlikely.
5946 #define HANDLE_PTR(ptr,obj) do { \
5947 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
5948 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
5949 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); \
5950 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
5951 if (!object_is_pinned (*(ptr))) \
5952 missing_remsets = TRUE; \
5958 * Check that each object reference which points into the nursery can
5959 * be found in the remembered sets.
5962 check_consistency_callback (char *start, size_t size, void *dummy)
5964 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
5965 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
5967 #define SCAN_OBJECT_ACTION
5968 #include "sgen-scan-object.h"
5972 * Perform consistency check of the heap.
5974 * Assumes the world is stopped.
5977 check_consistency (void)
5981 // Need to add more checks
5983 missing_remsets = FALSE;
5985 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
5987 // Check that oldspace->newspace pointers are registered with the collector
5988 major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
5990 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
5991 check_consistency_callback (bigobj->data, bigobj->size, NULL);
5993 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
5995 #ifdef SGEN_BINARY_PROTOCOL
5996 if (!binary_protocol_file)
5998 g_assert (!missing_remsets);
6003 #define HANDLE_PTR(ptr,obj) do { \
6005 g_assert (LOAD_VTABLE (*(ptr))); \
6009 check_major_refs_callback (char *start, size_t size, void *dummy)
6011 #define SCAN_OBJECT_ACTION
6012 #include "sgen-scan-object.h"
6016 check_major_refs (void)
6020 major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6022 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6023 check_major_refs_callback (bigobj->data, bigobj->size, NULL);
6026 /* Check that the reference is valid */
6028 #define HANDLE_PTR(ptr,obj) do { \
6030 g_assert (safe_name (*(ptr)) != NULL); \
6037 * Perform consistency check on an object. Currently we only check that the
6038 * reference fields are valid.
6041 check_object (char *start)
6046 #include "sgen-scan-object.h"
6050 * ######################################################################
6051 * ######## Other mono public interface functions.
6052 * ######################################################################
6056 mono_gc_collect (int generation)
6060 if (generation == 0) {
6061 collect_nursery (0);
6063 major_collection ("user request");
6070 mono_gc_max_generation (void)
6076 mono_gc_collection_count (int generation)
6078 if (generation == 0)
6079 return num_minor_gcs;
6080 return num_major_gcs;
6084 mono_gc_get_used_size (void)
6088 tot = los_memory_usage;
6089 tot += nursery_section->next_data - nursery_section->data;
6090 tot += major.get_used_size ();
6091 /* FIXME: account for pinned objects */
6097 mono_gc_get_heap_size (void)
6103 mono_gc_disable (void)
6111 mono_gc_enable (void)
6119 mono_gc_get_los_limit (void)
6121 return MAX_SMALL_OBJ_SIZE;
6125 mono_object_is_alive (MonoObject* o)
6131 mono_gc_get_generation (MonoObject *obj)
6133 if (ptr_in_nursery (obj))
6139 mono_gc_enable_events (void)
6144 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6147 mono_gc_register_disappearing_link (obj, link_addr, track);
6152 mono_gc_weak_link_remove (void **link_addr)
6155 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
6160 mono_gc_weak_link_get (void **link_addr)
6164 return (MonoObject*) REVEAL_POINTER (*link_addr);
6168 mono_gc_ephemeron_array_add (MonoObject *obj)
6170 EphemeronLinkNode *node;
6174 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
6179 node->array = (char*)obj;
6180 node->next = ephemeron_list;
6181 ephemeron_list = node;
6183 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6190 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6192 if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6193 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6195 mword complex = alloc_complex_descriptor (bitmap, numbits);
6196 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6201 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6205 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6206 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6207 user_descriptors [user_descriptors_next ++] = marker;
6213 mono_gc_alloc_fixed (size_t size, void *descr)
6215 /* FIXME: do a single allocation */
6216 void *res = calloc (1, size);
6219 if (!mono_gc_register_root (res, size, descr)) {
6227 mono_gc_free_fixed (void* addr)
6229 mono_gc_deregister_root (addr);
6234 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6238 result = func (data);
6239 UNLOCK_INTERRUPTION;
6244 mono_gc_is_gc_thread (void)
6248 result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
6253 /* Tries to extract a number from the passed string, taking in to account m, k
6256 mono_sgen_parse_environment_string_extract_number (const char *str, glong *out)
6259 int len = strlen (str), shift = 0;
6261 gboolean is_suffix = FALSE;
6264 switch (str [len - 1]) {
6275 suffix = str [len - 1];
6280 val = strtol (str, &endptr, 10);
6282 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
6283 || (errno != 0 && val == 0) || (endptr == str))
6287 if (*(endptr + 1)) /* Invalid string. */
6297 mono_gc_base_init (void)
6301 char *major_collector = NULL;
6302 struct sigaction sinfo;
6304 LOCK_INIT (gc_mutex);
6306 if (gc_initialized) {
6310 pagesize = mono_pagesize ();
6311 gc_debug_file = stderr;
6313 LOCK_INIT (interruption_mutex);
6314 LOCK_INIT (global_remset_mutex);
6316 if ((env = getenv ("MONO_GC_PARAMS"))) {
6317 opts = g_strsplit (env, ",", -1);
6318 for (ptr = opts; *ptr; ++ptr) {
6320 if (g_str_has_prefix (opt, "major=")) {
6321 opt = strchr (opt, '=') + 1;
6322 major_collector = g_strdup (opt);
6330 mono_sgen_init_internal_allocator ();
6332 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
6333 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
6334 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
6335 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
6336 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
6337 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
6338 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6339 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
6340 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
6342 if (!major_collector || !strcmp (major_collector, "marksweep")) {
6343 mono_sgen_marksweep_init (&major);
6344 } else if (!major_collector || !strcmp (major_collector, "marksweep-fixed")) {
6345 mono_sgen_marksweep_fixed_init (&major);
6346 } else if (!major_collector || !strcmp (major_collector, "marksweep-par")) {
6347 mono_sgen_marksweep_par_init (&major);
6348 workers_init (mono_cpu_count ());
6349 } else if (!major_collector || !strcmp (major_collector, "marksweep-fixed-par")) {
6350 mono_sgen_marksweep_fixed_par_init (&major);
6351 workers_init (mono_cpu_count ());
6352 } else if (!strcmp (major_collector, "copying")) {
6353 mono_sgen_copying_init (&major);
6355 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector);
6359 #ifdef SGEN_HAVE_CARDTABLE
6360 use_cardtable = major.supports_cardtable;
6362 use_cardtable = FALSE;
6366 for (ptr = opts; *ptr; ++ptr) {
6368 if (g_str_has_prefix (opt, "major="))
6370 if (g_str_has_prefix (opt, "wbarrier=")) {
6371 opt = strchr (opt, '=') + 1;
6372 if (strcmp (opt, "remset") == 0) {
6373 use_cardtable = FALSE;
6374 } else if (strcmp (opt, "cardtable") == 0) {
6375 if (!use_cardtable) {
6376 if (major.supports_cardtable)
6377 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
6379 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
6386 if (g_str_has_prefix (opt, "nursery-size=")) {
6388 opt = strchr (opt, '=') + 1;
6389 if (*opt && mono_sgen_parse_environment_string_extract_number (opt, &val)) {
6390 default_nursery_size = val;
6391 #ifdef SGEN_ALIGN_NURSERY
6392 if ((val & (val - 1))) {
6393 fprintf (stderr, "The nursery size must be a power of two.\n");
6397 default_nursery_bits = 0;
6398 while (1 << (++ default_nursery_bits) != default_nursery_size)
6402 fprintf (stderr, "nursery-size must be an integer.\n");
6408 if (!(major.handle_gc_param && major.handle_gc_param (opt))) {
6409 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
6410 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6411 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
6412 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
6413 if (major.print_gc_param_usage)
6414 major.print_gc_param_usage ();
6421 if (major_collector)
6422 g_free (major_collector);
6424 nursery_size = DEFAULT_NURSERY_SIZE;
6425 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
6429 if ((env = getenv ("MONO_GC_DEBUG"))) {
6430 opts = g_strsplit (env, ",", -1);
6431 for (ptr = opts; ptr && *ptr; ptr ++) {
6433 if (opt [0] >= '0' && opt [0] <= '9') {
6434 gc_debug_level = atoi (opt);
6439 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
6440 gc_debug_file = fopen (rf, "wb");
6442 gc_debug_file = stderr;
6445 } else if (!strcmp (opt, "collect-before-allocs")) {
6446 collect_before_allocs = TRUE;
6447 } else if (!strcmp (opt, "check-at-minor-collections")) {
6448 consistency_check_at_minor_collection = TRUE;
6449 nursery_clear_policy = CLEAR_AT_GC;
6450 } else if (!strcmp (opt, "xdomain-checks")) {
6451 xdomain_checks = TRUE;
6452 } else if (!strcmp (opt, "clear-at-gc")) {
6453 nursery_clear_policy = CLEAR_AT_GC;
6454 } else if (!strcmp (opt, "conservative-stack-mark")) {
6455 conservative_stack_mark = TRUE;
6456 } else if (!strcmp (opt, "check-scan-starts")) {
6457 do_scan_starts_check = TRUE;
6458 } else if (g_str_has_prefix (opt, "heap-dump=")) {
6459 char *filename = strchr (opt, '=') + 1;
6460 nursery_clear_policy = CLEAR_AT_GC;
6461 heap_dump_file = fopen (filename, "w");
6463 fprintf (heap_dump_file, "<sgen-dump>\n");
6464 #ifdef SGEN_BINARY_PROTOCOL
6465 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
6466 char *filename = strchr (opt, '=') + 1;
6467 binary_protocol_file = fopen (filename, "w");
6470 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
6471 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
6472 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
6479 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
6480 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
6482 sigfillset (&sinfo.sa_mask);
6483 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
6484 sinfo.sa_sigaction = suspend_handler;
6485 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
6486 g_error ("failed sigaction");
6489 sinfo.sa_handler = restart_handler;
6490 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
6491 g_error ("failed sigaction");
6494 sigfillset (&suspend_signal_mask);
6495 sigdelset (&suspend_signal_mask, restart_signal_num);
6497 global_remset = alloc_remset (1024, NULL);
6498 global_remset->next = NULL;
6500 pthread_key_create (&remembered_set_key, unregister_thread);
6502 #ifndef HAVE_KW_THREAD
6503 pthread_key_create (&thread_info_key, NULL);
6509 gc_initialized = TRUE;
6511 mono_gc_register_thread (&sinfo);
6515 mono_gc_get_suspend_signal (void)
6517 return suspend_signal_num;
6527 #ifdef HAVE_KW_THREAD
6528 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
6529 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6530 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6531 mono_mb_emit_i4 ((mb), (offset)); \
6534 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
6535 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
6536 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
6537 mono_mb_emit_i4 ((mb), thread_info_key); \
6538 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
6539 mono_mb_emit_byte ((mb), CEE_ADD); \
6540 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
6544 #ifdef MANAGED_ALLOCATION
6545 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
6546 * for each class. This is currently not easy to do, as it is hard to generate basic
6547 * blocks + branches, but it is easy with the linear IL codebase.
6549 * For this to work we'd need to solve the TLAB race, first. Now we
6550 * require the allocator to be in a few known methods to make sure
6551 * that they are executed atomically via the restart mechanism.
6554 create_allocator (int atype)
6556 int p_var, size_var;
6557 guint32 slowpath_branch, max_size_branch;
6558 MonoMethodBuilder *mb;
6560 MonoMethodSignature *csig;
6561 static gboolean registered = FALSE;
6562 int tlab_next_addr_var, new_next_var;
6564 const char *name = NULL;
6565 AllocatorWrapperInfo *info;
6567 #ifdef HAVE_KW_THREAD
6568 int tlab_next_addr_offset = -1;
6569 int tlab_temp_end_offset = -1;
6571 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
6572 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6574 g_assert (tlab_next_addr_offset != -1);
6575 g_assert (tlab_temp_end_offset != -1);
6579 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
6580 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
6584 if (atype == ATYPE_SMALL) {
6586 name = "AllocSmall";
6587 } else if (atype == ATYPE_NORMAL) {
6590 } else if (atype == ATYPE_VECTOR) {
6592 name = "AllocVector";
6594 g_assert_not_reached ();
6597 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
6598 csig->ret = &mono_defaults.object_class->byval_arg;
6599 for (i = 0; i < num_params; ++i)
6600 csig->params [i] = &mono_defaults.int_class->byval_arg;
6602 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
6603 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
6604 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6605 /* size = vtable->klass->instance_size; */
6606 mono_mb_emit_ldarg (mb, 0);
6607 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6608 mono_mb_emit_byte (mb, CEE_ADD);
6609 mono_mb_emit_byte (mb, CEE_LDIND_I);
6610 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
6611 mono_mb_emit_byte (mb, CEE_ADD);
6612 /* FIXME: assert instance_size stays a 4 byte integer */
6613 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6614 mono_mb_emit_stloc (mb, size_var);
6615 } else if (atype == ATYPE_VECTOR) {
6616 MonoExceptionClause *clause;
6618 MonoClass *oom_exc_class;
6621 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
6622 mono_mb_emit_ldarg (mb, 1);
6623 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
6624 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
6625 mono_mb_emit_exception (mb, "OverflowException", NULL);
6626 mono_mb_patch_short_branch (mb, pos);
6628 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
6629 clause->try_offset = mono_mb_get_label (mb);
6631 /* vtable->klass->sizes.element_size */
6632 mono_mb_emit_ldarg (mb, 0);
6633 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6634 mono_mb_emit_byte (mb, CEE_ADD);
6635 mono_mb_emit_byte (mb, CEE_LDIND_I);
6636 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
6637 mono_mb_emit_byte (mb, CEE_ADD);
6638 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6641 mono_mb_emit_ldarg (mb, 1);
6642 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
6643 /* + sizeof (MonoArray) */
6644 mono_mb_emit_icon (mb, sizeof (MonoArray));
6645 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
6646 mono_mb_emit_stloc (mb, size_var);
6648 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
6651 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
6652 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
6653 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
6654 "System", "OverflowException");
6655 g_assert (clause->data.catch_class);
6656 clause->handler_offset = mono_mb_get_label (mb);
6658 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
6659 "System", "OutOfMemoryException");
6660 g_assert (oom_exc_class);
6661 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
6664 mono_mb_emit_byte (mb, CEE_POP);
6665 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
6666 mono_mb_emit_byte (mb, CEE_THROW);
6668 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
6669 mono_mb_set_clauses (mb, 1, clause);
6670 mono_mb_patch_branch (mb, pos_leave);
6673 g_assert_not_reached ();
6676 /* size += ALLOC_ALIGN - 1; */
6677 mono_mb_emit_ldloc (mb, size_var);
6678 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
6679 mono_mb_emit_byte (mb, CEE_ADD);
6680 /* size &= ~(ALLOC_ALIGN - 1); */
6681 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
6682 mono_mb_emit_byte (mb, CEE_AND);
6683 mono_mb_emit_stloc (mb, size_var);
6685 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
6686 if (atype != ATYPE_SMALL) {
6687 mono_mb_emit_ldloc (mb, size_var);
6688 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
6689 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
6693 * We need to modify tlab_next, but the JIT only supports reading, so we read
6694 * another tls var holding its address instead.
6697 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
6698 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6699 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
6700 mono_mb_emit_stloc (mb, tlab_next_addr_var);
6702 /* p = (void**)tlab_next; */
6703 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6704 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
6705 mono_mb_emit_byte (mb, CEE_LDIND_I);
6706 mono_mb_emit_stloc (mb, p_var);
6708 /* new_next = (char*)p + size; */
6709 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6710 mono_mb_emit_ldloc (mb, p_var);
6711 mono_mb_emit_ldloc (mb, size_var);
6712 mono_mb_emit_byte (mb, CEE_CONV_I);
6713 mono_mb_emit_byte (mb, CEE_ADD);
6714 mono_mb_emit_stloc (mb, new_next_var);
6716 /* tlab_next = new_next */
6717 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
6718 mono_mb_emit_ldloc (mb, new_next_var);
6719 mono_mb_emit_byte (mb, CEE_STIND_I);
6721 /* if (G_LIKELY (new_next < tlab_temp_end)) */
6722 mono_mb_emit_ldloc (mb, new_next_var);
6723 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
6724 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
6727 if (atype != ATYPE_SMALL)
6728 mono_mb_patch_short_branch (mb, max_size_branch);
6730 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
6731 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
6733 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
6734 mono_mb_emit_ldarg (mb, 0);
6735 mono_mb_emit_ldloc (mb, size_var);
6736 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6737 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
6738 } else if (atype == ATYPE_VECTOR) {
6739 mono_mb_emit_ldarg (mb, 1);
6740 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
6742 g_assert_not_reached ();
6744 mono_mb_emit_byte (mb, CEE_RET);
6747 mono_mb_patch_short_branch (mb, slowpath_branch);
6749 /* FIXME: Memory barrier */
6752 mono_mb_emit_ldloc (mb, p_var);
6753 mono_mb_emit_ldarg (mb, 0);
6754 mono_mb_emit_byte (mb, CEE_STIND_I);
6756 if (atype == ATYPE_VECTOR) {
6757 /* arr->max_length = max_length; */
6758 mono_mb_emit_ldloc (mb, p_var);
6759 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
6760 mono_mb_emit_ldarg (mb, 1);
6761 mono_mb_emit_byte (mb, CEE_STIND_I);
6765 mono_mb_emit_ldloc (mb, p_var);
6766 mono_mb_emit_byte (mb, CEE_RET);
6768 res = mono_mb_create_method (mb, csig, 8);
6770 mono_method_get_header (res)->init_locals = FALSE;
6772 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
6773 info->gc_name = "sgen";
6774 info->alloc_type = atype;
6775 mono_marshal_set_wrapper_info (res, info);
6782 mono_gc_get_gc_name (void)
6787 static MonoMethod* alloc_method_cache [ATYPE_NUM];
6788 static MonoMethod *write_barrier_method;
6791 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
6799 ji = mono_jit_info_table_find (domain, ip);
6802 method = ji->method;
6804 if (method == write_barrier_method)
6806 for (i = 0; i < ATYPE_NUM; ++i)
6807 if (method == alloc_method_cache [i])
6813 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
6814 * The signature of the called method is:
6815 * object allocate (MonoVTable *vtable)
6818 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
6820 #ifdef MANAGED_ALLOCATION
6821 MonoClass *klass = vtable->klass;
6823 #ifdef HAVE_KW_THREAD
6824 int tlab_next_offset = -1;
6825 int tlab_temp_end_offset = -1;
6826 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
6827 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6829 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
6833 if (!mono_runtime_has_tls_get ())
6835 if (klass->instance_size > tlab_size)
6837 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
6841 if (klass->byval_arg.type == MONO_TYPE_STRING)
6843 if (collect_before_allocs)
6846 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
6847 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
6849 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
6856 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
6858 #ifdef MANAGED_ALLOCATION
6859 MonoClass *klass = vtable->klass;
6861 #ifdef HAVE_KW_THREAD
6862 int tlab_next_offset = -1;
6863 int tlab_temp_end_offset = -1;
6864 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
6865 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6867 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
6873 if (!mono_runtime_has_tls_get ())
6875 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
6877 if (collect_before_allocs)
6879 g_assert (!klass->has_finalize && !klass->marshalbyref);
6881 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
6888 mono_gc_get_managed_allocator_by_type (int atype)
6890 #ifdef MANAGED_ALLOCATION
6893 if (!mono_runtime_has_tls_get ())
6896 mono_loader_lock ();
6897 res = alloc_method_cache [atype];
6899 res = alloc_method_cache [atype] = create_allocator (atype);
6900 mono_loader_unlock ();
6908 mono_gc_get_managed_allocator_types (void)
6915 mono_gc_get_write_barrier (void)
6918 MonoMethodBuilder *mb;
6919 MonoMethodSignature *sig;
6920 #ifdef MANAGED_WBARRIER
6921 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
6922 #ifndef SGEN_ALIGN_NURSERY
6923 int label_continue_1, label_continue_2, label_no_wb_5;
6924 int dereferenced_var;
6926 int buffer_var, buffer_index_var, dummy_var;
6928 #ifdef HAVE_KW_THREAD
6929 int stack_end_offset = -1, store_remset_buffer_offset = -1;
6930 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
6932 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
6933 g_assert (stack_end_offset != -1);
6934 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
6935 g_assert (store_remset_buffer_offset != -1);
6936 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
6937 g_assert (store_remset_buffer_index_offset != -1);
6938 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
6939 g_assert (store_remset_buffer_index_addr_offset != -1);
6943 g_assert (!use_cardtable);
6945 // FIXME: Maybe create a separate version for ctors (the branch would be
6946 // correctly predicted more times)
6947 if (write_barrier_method)
6948 return write_barrier_method;
6950 /* Create the IL version of mono_gc_barrier_generic_store () */
6951 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
6952 sig->ret = &mono_defaults.void_class->byval_arg;
6953 sig->params [0] = &mono_defaults.int_class->byval_arg;
6955 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
6957 #ifdef MANAGED_WBARRIER
6958 if (mono_runtime_has_tls_get ()) {
6959 #ifdef SGEN_ALIGN_NURSERY
6960 // if (ptr_in_nursery (ptr)) return;
6962 * Masking out the bits might be faster, but we would have to use 64 bit
6963 * immediates, which might be slower.
6965 mono_mb_emit_ldarg (mb, 0);
6966 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
6967 mono_mb_emit_byte (mb, CEE_SHR_UN);
6968 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
6969 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
6971 // if (!ptr_in_nursery (*ptr)) return;
6972 mono_mb_emit_ldarg (mb, 0);
6973 mono_mb_emit_byte (mb, CEE_LDIND_I);
6974 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
6975 mono_mb_emit_byte (mb, CEE_SHR_UN);
6976 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
6977 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
6980 // if (ptr < (nursery_start)) goto continue;
6981 mono_mb_emit_ldarg (mb, 0);
6982 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
6983 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
6985 // if (ptr >= nursery_real_end)) goto continue;
6986 mono_mb_emit_ldarg (mb, 0);
6987 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
6988 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
6991 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
6994 mono_mb_patch_branch (mb, label_continue_1);
6995 mono_mb_patch_branch (mb, label_continue_2);
6997 // Dereference and store in local var
6998 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6999 mono_mb_emit_ldarg (mb, 0);
7000 mono_mb_emit_byte (mb, CEE_LDIND_I);
7001 mono_mb_emit_stloc (mb, dereferenced_var);
7003 // if (*ptr < nursery_start) return;
7004 mono_mb_emit_ldloc (mb, dereferenced_var);
7005 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7006 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7008 // if (*ptr >= nursery_end) return;
7009 mono_mb_emit_ldloc (mb, dereferenced_var);
7010 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7011 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7014 // if (ptr >= stack_end) goto need_wb;
7015 mono_mb_emit_ldarg (mb, 0);
7016 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7017 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7019 // if (ptr >= stack_start) return;
7020 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7021 mono_mb_emit_ldarg (mb, 0);
7022 mono_mb_emit_ldloc_addr (mb, dummy_var);
7023 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7026 mono_mb_patch_branch (mb, label_need_wb);
7028 // buffer = STORE_REMSET_BUFFER;
7029 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7030 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7031 mono_mb_emit_stloc (mb, buffer_var);
7033 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7034 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7035 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7036 mono_mb_emit_stloc (mb, buffer_index_var);
7038 // if (buffer [buffer_index] == ptr) return;
7039 mono_mb_emit_ldloc (mb, buffer_var);
7040 mono_mb_emit_ldloc (mb, buffer_index_var);
7041 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7042 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7043 mono_mb_emit_byte (mb, CEE_SHL);
7044 mono_mb_emit_byte (mb, CEE_ADD);
7045 mono_mb_emit_byte (mb, CEE_LDIND_I);
7046 mono_mb_emit_ldarg (mb, 0);
7047 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7050 mono_mb_emit_ldloc (mb, buffer_index_var);
7051 mono_mb_emit_icon (mb, 1);
7052 mono_mb_emit_byte (mb, CEE_ADD);
7053 mono_mb_emit_stloc (mb, buffer_index_var);
7055 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7056 mono_mb_emit_ldloc (mb, buffer_index_var);
7057 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7058 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7060 // buffer [buffer_index] = ptr;
7061 mono_mb_emit_ldloc (mb, buffer_var);
7062 mono_mb_emit_ldloc (mb, buffer_index_var);
7063 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7064 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7065 mono_mb_emit_byte (mb, CEE_SHL);
7066 mono_mb_emit_byte (mb, CEE_ADD);
7067 mono_mb_emit_ldarg (mb, 0);
7068 mono_mb_emit_byte (mb, CEE_STIND_I);
7070 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7071 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7072 mono_mb_emit_ldloc (mb, buffer_index_var);
7073 mono_mb_emit_byte (mb, CEE_STIND_I);
7076 mono_mb_patch_branch (mb, label_no_wb_1);
7077 mono_mb_patch_branch (mb, label_no_wb_2);
7078 mono_mb_patch_branch (mb, label_no_wb_3);
7079 mono_mb_patch_branch (mb, label_no_wb_4);
7080 #ifndef SGEN_ALIGN_NURSERY
7081 mono_mb_patch_branch (mb, label_no_wb_5);
7083 mono_mb_emit_byte (mb, CEE_RET);
7086 mono_mb_patch_branch (mb, label_slow_path);
7090 mono_mb_emit_ldarg (mb, 0);
7091 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7092 mono_mb_emit_byte (mb, CEE_RET);
7094 res = mono_mb_create_method (mb, sig, 16);
7097 mono_loader_lock ();
7098 if (write_barrier_method) {
7099 /* Already created */
7100 mono_free_method (res);
7102 /* double-checked locking */
7103 mono_memory_barrier ();
7104 write_barrier_method = res;
7106 mono_loader_unlock ();
7108 return write_barrier_method;
7112 mono_gc_get_description (void)
7114 return g_strdup ("sgen");
7118 mono_gc_set_desktop_mode (void)
7123 mono_gc_is_moving (void)
7129 mono_gc_is_disabled (void)
7135 mono_sgen_debug_printf (int level, const char *format, ...)
7139 if (level > gc_debug_level)
7142 va_start (ap, format);
7143 vfprintf (gc_debug_file, format, ap);
7147 #endif /* HAVE_SGEN_GC */