2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
15 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
18 * Permission is hereby granted to use or copy this program
19 * for any purpose, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
25 * Copyright 2001-2003 Ximian, Inc
26 * Copyright 2003-2010 Novell, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining
29 * a copy of this software and associated documentation files (the
30 * "Software"), to deal in the Software without restriction, including
31 * without limitation the rights to use, copy, modify, merge, publish,
32 * distribute, sublicense, and/or sell copies of the Software, and to
33 * permit persons to whom the Software is furnished to do so, subject to
34 * the following conditions:
36 * The above copyright notice and this permission notice shall be
37 * included in all copies or substantial portions of the Software.
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 * Important: allocation provides always zeroed memory, having to do
49 * a memset after allocation is deadly for performance.
50 * Memory usage at startup is currently as follows:
52 * 64 KB internal space
54 * We should provide a small memory config with half the sizes
56 * We currently try to make as few mono assumptions as possible:
57 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
59 * 2) gc descriptor is the second word in the vtable (first word in the class)
60 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
61 * 4) there is a function to get an object's size and the number of
62 * elements in an array.
63 * 5) we know the special way bounds are allocated for complex arrays
64 * 6) we know about proxies and how to treat them when domains are unloaded
66 * Always try to keep stack usage to a minimum: no recursive behaviour
67 * and no large stack allocs.
69 * General description.
70 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
71 * When the nursery is full we start a nursery collection: this is performed with a
73 * When the old generation is full we start a copying GC of the old generation as well:
74 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
75 * in the future. Maybe we'll even do both during the same collection like IMMIX.
77 * The things that complicate this description are:
78 * *) pinned objects: we can't move them so we need to keep track of them
79 * *) no precise info of the thread stacks and registers: we need to be able to
80 * quickly find the objects that may be referenced conservatively and pin them
81 * (this makes the first issues more important)
82 * *) large objects are too expensive to be dealt with using copying GC: we handle them
83 * with mark/sweep during major collections
84 * *) some objects need to not move even if they are small (interned strings, Type handles):
85 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
86 * PinnedChunks regions
92 *) we could have a function pointer in MonoClass to implement
93 customized write barriers for value types
95 *) investigate the stuff needed to advance a thread to a GC-safe
96 point (single-stepping, read from unmapped memory etc) and implement it.
97 This would enable us to inline allocations and write barriers, for example,
98 or at least parts of them, like the write barrier checks.
99 We may need this also for handling precise info on stacks, even simple things
100 as having uninitialized data on the stack and having to wait for the prolog
101 to zero it. Not an issue for the last frame that we scan conservatively.
102 We could always not trust the value in the slots anyway.
104 *) modify the jit to save info about references in stack locations:
105 this can be done just for locals as a start, so that at least
106 part of the stack is handled precisely.
108 *) test/fix endianess issues
110 *) Implement a card table as the write barrier instead of remembered
111 sets? Card tables are not easy to implement with our current
112 memory layout. We have several different kinds of major heap
113 objects: Small objects in regular blocks, small objects in pinned
114 chunks and LOS objects. If we just have a pointer we have no way
115 to tell which kind of object it points into, therefore we cannot
116 know where its card table is. The least we have to do to make
117 this happen is to get rid of write barriers for indirect stores.
120 *) Get rid of write barriers for indirect stores. We can do this by
121 telling the GC to wbarrier-register an object once we do an ldloca
122 or ldelema on it, and to unregister it once it's not used anymore
123 (it can only travel downwards on the stack). The problem with
124 unregistering is that it needs to happen eventually no matter
125 what, even if exceptions are thrown, the thread aborts, etc.
126 Rodrigo suggested that we could do only the registering part and
127 let the collector find out (pessimistically) when it's safe to
128 unregister, namely when the stack pointer of the thread that
129 registered the object is higher than it was when the registering
130 happened. This might make for a good first implementation to get
131 some data on performance.
133 *) Some sort of blacklist support? Blacklists is a concept from the
134 Boehm GC: if during a conservative scan we find pointers to an
135 area which we might use as heap, we mark that area as unusable, so
136 pointer retention by random pinning pointers is reduced.
138 *) experiment with max small object size (very small right now - 2kb,
139 because it's tied to the max freelist size)
141 *) add an option to mmap the whole heap in one chunk: it makes for many
142 simplifications in the checks (put the nursery at the top and just use a single
143 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
144 not flexible (too much of the address space may be used by default or we can't
145 increase the heap as needed) and we'd need a race-free mechanism to return memory
146 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
147 was written to, munmap is needed, but the following mmap may not find the same segment
150 *) memzero the major fragments after restarting the world and optionally a smaller
153 *) investigate having fragment zeroing threads
155 *) separate locks for finalization and other minor stuff to reduce
158 *) try a different copying order to improve memory locality
160 *) a thread abort after a store but before the write barrier will
161 prevent the write barrier from executing
163 *) specialized dynamically generated markers/copiers
165 *) Dynamically adjust TLAB size to the number of threads. If we have
166 too many threads that do allocation, we might need smaller TLABs,
167 and we might get better performance with larger TLABs if we only
168 have a handful of threads. We could sum up the space left in all
169 assigned TLABs and if that's more than some percentage of the
170 nursery size, reduce the TLAB size.
172 *) Explore placing unreachable objects on unused nursery memory.
173 Instead of memset'ng a region to zero, place an int[] covering it.
174 A good place to start is add_nursery_frag. The tricky thing here is
175 placing those objects atomically outside of a collection.
185 #include <semaphore.h>
194 #define _XOPEN_SOURCE
196 #include "metadata/metadata-internals.h"
197 #include "metadata/class-internals.h"
198 #include "metadata/gc-internal.h"
199 #include "metadata/object-internals.h"
200 #include "metadata/threads.h"
201 #include "metadata/sgen-gc.h"
202 #include "metadata/sgen-cardtable.h"
203 #include "metadata/sgen-protocol.h"
204 #include "metadata/sgen-archdep.h"
205 #include "metadata/mono-gc.h"
206 #include "metadata/method-builder.h"
207 #include "metadata/profiler-private.h"
208 #include "metadata/monitor.h"
209 #include "metadata/threadpool-internals.h"
210 #include "metadata/mempool-internals.h"
211 #include "metadata/marshal.h"
212 #include "utils/mono-mmap.h"
213 #include "utils/mono-time.h"
214 #include "utils/mono-semaphore.h"
215 #include "utils/mono-counters.h"
216 #include "utils/mono-proclib.h"
218 #include <mono/utils/memcheck.h>
220 #if defined(__MACH__)
221 #include "utils/mach-support.h"
224 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
228 #include "mono/cil/opcode.def"
234 #undef pthread_create
236 #undef pthread_detach
239 * ######################################################################
240 * ######## Types and constants used by the GC.
241 * ######################################################################
244 static int gc_initialized = 0;
245 /* If set, do a minor collection before every allocation */
246 static gboolean collect_before_allocs = FALSE;
247 /* If set, do a heap consistency check before each minor collection */
248 static gboolean consistency_check_at_minor_collection = FALSE;
249 /* If set, check that there are no references to the domain left at domain unload */
250 static gboolean xdomain_checks = FALSE;
251 /* If not null, dump the heap after each collection into this file */
252 static FILE *heap_dump_file = NULL;
253 /* If set, mark stacks conservatively, even if precise marking is possible */
254 static gboolean conservative_stack_mark = TRUE;
255 /* If set, do a plausibility check on the scan_starts before and after
257 static gboolean do_scan_starts_check = FALSE;
259 #ifdef HEAVY_STATISTICS
260 static long long stat_objects_alloced = 0;
261 static long long stat_bytes_alloced = 0;
262 long long stat_objects_alloced_degraded = 0;
263 long long stat_bytes_alloced_degraded = 0;
264 static long long stat_bytes_alloced_los = 0;
266 long long stat_copy_object_called_nursery = 0;
267 long long stat_objects_copied_nursery = 0;
268 long long stat_copy_object_called_major = 0;
269 long long stat_objects_copied_major = 0;
271 long long stat_scan_object_called_nursery = 0;
272 long long stat_scan_object_called_major = 0;
274 long long stat_nursery_copy_object_failed_from_space = 0;
275 long long stat_nursery_copy_object_failed_forwarded = 0;
276 long long stat_nursery_copy_object_failed_pinned = 0;
278 static long long stat_store_remsets = 0;
279 static long long stat_store_remsets_unique = 0;
280 static long long stat_saved_remsets_1 = 0;
281 static long long stat_saved_remsets_2 = 0;
282 static long long stat_local_remsets_processed = 0;
283 static long long stat_global_remsets_added = 0;
284 static long long stat_global_remsets_readded = 0;
285 static long long stat_global_remsets_processed = 0;
286 static long long stat_global_remsets_discarded = 0;
288 static long long stat_wasted_fragments_used = 0;
289 static long long stat_wasted_fragments_bytes = 0;
291 static int stat_wbarrier_set_field = 0;
292 static int stat_wbarrier_set_arrayref = 0;
293 static int stat_wbarrier_arrayref_copy = 0;
294 static int stat_wbarrier_generic_store = 0;
295 static int stat_wbarrier_generic_store_remset = 0;
296 static int stat_wbarrier_set_root = 0;
297 static int stat_wbarrier_value_copy = 0;
298 static int stat_wbarrier_object_copy = 0;
301 static long long time_minor_pre_collection_fragment_clear = 0;
302 static long long time_minor_pinning = 0;
303 static long long time_minor_scan_remsets = 0;
304 static long long time_minor_scan_card_table = 0;
305 static long long time_minor_scan_pinned = 0;
306 static long long time_minor_scan_registered_roots = 0;
307 static long long time_minor_scan_thread_data = 0;
308 static long long time_minor_finish_gray_stack = 0;
309 static long long time_minor_fragment_creation = 0;
311 static long long time_major_pre_collection_fragment_clear = 0;
312 static long long time_major_pinning = 0;
313 static long long time_major_scan_pinned = 0;
314 static long long time_major_scan_registered_roots = 0;
315 static long long time_major_scan_thread_data = 0;
316 static long long time_major_scan_alloc_pinned = 0;
317 static long long time_major_scan_finalized = 0;
318 static long long time_major_scan_big_objects = 0;
319 static long long time_major_finish_gray_stack = 0;
320 static long long time_major_free_bigobjs = 0;
321 static long long time_major_los_sweep = 0;
322 static long long time_major_sweep = 0;
323 static long long time_major_fragment_creation = 0;
325 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
327 int gc_debug_level = 0;
332 mono_gc_flush_info (void)
334 fflush (gc_debug_file);
339 * Define this to allow the user to change the nursery size by
340 * specifying its value in the MONO_GC_PARAMS environmental
341 * variable. See mono_gc_base_init for details.
343 #define USER_CONFIG 1
345 #define TV_DECLARE SGEN_TV_DECLARE
346 #define TV_GETTIME SGEN_TV_GETTIME
347 #define TV_ELAPSED SGEN_TV_ELAPSED
348 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
350 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
352 /* The method used to clear the nursery */
353 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
354 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
359 CLEAR_AT_TLAB_CREATION
360 } NurseryClearPolicy;
362 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
365 * The young generation is divided into fragments. This is because
366 * we can hand one fragments to a thread for lock-less fast alloc and
367 * because the young generation ends up fragmented anyway by pinned objects.
368 * Once a collection is done, a list of fragments is created. When doing
369 * thread local alloc we use smallish nurseries so we allow new threads to
370 * allocate memory from gen0 without triggering a collection. Threads that
371 * are found to allocate lots of memory are given bigger fragments. This
372 * should make the finalizer thread use little nursery memory after a while.
373 * We should start assigning threads very small fragments: if there are many
374 * threads the nursery will be full of reserved space that the threads may not
375 * use at all, slowing down allocation speed.
376 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
377 * Allocation Buffers (TLABs).
379 typedef struct _Fragment Fragment;
383 char *fragment_start;
384 char *fragment_limit; /* the current soft limit for allocation */
388 /* the runtime can register areas of memory as roots: we keep two lists of roots,
389 * a pinned root set for conservatively scanned roots and a normal one for
390 * precisely scanned roots (currently implemented as a single list).
392 typedef struct _RootRecord RootRecord;
401 * We're never actually using the first element. It's always set to
402 * NULL to simplify the elimination of consecutive duplicate
405 #define STORE_REMSET_BUFFER_SIZE 1024
407 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
408 struct _GenericStoreRememberedSet {
409 GenericStoreRememberedSet *next;
410 /* We need one entry less because the first entry of store
411 remset buffers is always a dummy and we don't copy it. */
412 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
415 /* we have 4 possible values in the low 2 bits */
417 REMSET_LOCATION, /* just a pointer to the exact location */
418 REMSET_RANGE, /* range of pointer fields */
419 REMSET_OBJECT, /* mark all the object for scanning */
420 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
421 REMSET_TYPE_MASK = 0x3
424 #ifdef HAVE_KW_THREAD
425 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
427 static pthread_key_t remembered_set_key;
428 static RememberedSet *global_remset;
429 static RememberedSet *freed_thread_remsets;
430 static GenericStoreRememberedSet *generic_store_remsets = NULL;
432 /*A two slots cache for recently inserted remsets */
433 static gpointer global_remset_cache [2];
435 /* FIXME: later choose a size that takes into account the RememberedSet struct
436 * and doesn't waste any alloc paddin space.
438 #define DEFAULT_REMSET_SIZE 1024
439 static RememberedSet* alloc_remset (int size, gpointer id);
441 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
442 #define object_is_pinned SGEN_OBJECT_IS_PINNED
443 #define pin_object SGEN_PIN_OBJECT
444 #define unpin_object SGEN_UNPIN_OBJECT
446 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_real_end))
448 #define LOAD_VTABLE SGEN_LOAD_VTABLE
451 safe_name (void* obj)
453 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
454 return vt->klass->name;
457 #define safe_object_get_size mono_sgen_safe_object_get_size
460 mono_sgen_safe_name (void* obj)
462 return safe_name (obj);
466 * ######################################################################
467 * ######## Global data.
468 * ######################################################################
470 static LOCK_DECLARE (gc_mutex);
471 static int gc_disabled = 0;
472 static int num_minor_gcs = 0;
473 static int num_major_gcs = 0;
475 static gboolean use_cardtable;
479 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
480 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
481 static int default_nursery_size = (1 << 22);
482 #ifdef SGEN_ALIGN_NURSERY
483 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
484 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
485 static int default_nursery_bits = 22;
490 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
491 #ifdef SGEN_ALIGN_NURSERY
492 #define DEFAULT_NURSERY_BITS 22
497 #ifndef SGEN_ALIGN_NURSERY
498 #define DEFAULT_NURSERY_BITS -1
501 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
503 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
505 /* the minimum size of a fragment that we consider useful for allocation */
506 #define FRAGMENT_MIN_SIZE (512)
508 static mword pagesize = 4096;
509 static mword nursery_size;
510 static int degraded_mode = 0;
512 static mword total_alloc = 0;
513 /* use this to tune when to do a major/minor collection */
514 static mword memory_pressure = 0;
515 static mword minor_collection_allowance;
516 static int minor_collection_sections_alloced = 0;
518 static GCMemSection *nursery_section = NULL;
519 static mword lowest_heap_address = ~(mword)0;
520 static mword highest_heap_address = 0;
522 static LOCK_DECLARE (interruption_mutex);
523 static LOCK_DECLARE (global_remset_mutex);
524 static LOCK_DECLARE (pin_queue_mutex);
526 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
527 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
529 #define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
530 #define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
532 typedef struct _FinalizeEntry FinalizeEntry;
533 struct _FinalizeEntry {
538 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
539 struct _FinalizeEntryHashTable {
540 FinalizeEntry **table;
545 typedef struct _DisappearingLink DisappearingLink;
546 struct _DisappearingLink {
547 DisappearingLink *next;
551 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
552 struct _DisappearingLinkHashTable {
553 DisappearingLink **table;
558 typedef struct _EphemeronLinkNode EphemeronLinkNode;
560 struct _EphemeronLinkNode {
561 EphemeronLinkNode *next;
570 int current_collection_generation = -1;
573 * The link pointer is hidden by negating each bit. We use the lowest
574 * bit of the link (before negation) to store whether it needs
575 * resurrection tracking.
577 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
578 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
580 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
581 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
584 * The finalizable hash has the object as the key, the
585 * disappearing_link hash, has the link address as key.
587 static FinalizeEntryHashTable minor_finalizable_hash;
588 static FinalizeEntryHashTable major_finalizable_hash;
589 /* objects that are ready to be finalized */
590 static FinalizeEntry *fin_ready_list = NULL;
591 static FinalizeEntry *critical_fin_list = NULL;
593 static DisappearingLinkHashTable minor_disappearing_link_hash;
594 static DisappearingLinkHashTable major_disappearing_link_hash;
596 static EphemeronLinkNode *ephemeron_list;
598 static int num_ready_finalizers = 0;
599 static int no_finalize = 0;
602 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
603 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
604 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
608 /* registered roots: the key to the hash is the root start address */
610 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
612 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
613 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
614 static mword roots_size = 0; /* amount of memory in the root set */
615 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
617 #define GC_ROOT_NUM 32
620 void *objects [GC_ROOT_NUM];
621 int root_types [GC_ROOT_NUM];
622 uintptr_t extra_info [GC_ROOT_NUM];
626 notify_gc_roots (GCRootReport *report)
630 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
635 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
637 if (report->count == GC_ROOT_NUM)
638 notify_gc_roots (report);
639 report->objects [report->count] = object;
640 report->root_types [report->count] = rtype;
641 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
645 * The current allocation cursors
646 * We allocate objects in the nursery.
647 * The nursery is the area between nursery_start and nursery_real_end.
648 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
649 * from nursery fragments.
650 * tlab_next is the pointer to the space inside the TLAB where the next object will
652 * tlab_temp_end is the pointer to the end of the temporary space reserved for
653 * the allocation: it allows us to set the scan starts at reasonable intervals.
654 * tlab_real_end points to the end of the TLAB.
655 * nursery_frag_real_end points to the end of the currently used nursery fragment.
656 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
657 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
658 * At the next allocation, the area of the nursery where objects can be present is
659 * between MIN(nursery_first_pinned_start, first_fragment_start) and
660 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
662 static char *nursery_start = NULL;
664 #ifdef HAVE_KW_THREAD
665 #define TLAB_ACCESS_INIT
666 #define TLAB_START tlab_start
667 #define TLAB_NEXT tlab_next
668 #define TLAB_TEMP_END tlab_temp_end
669 #define TLAB_REAL_END tlab_real_end
670 #define REMEMBERED_SET remembered_set
671 #define STORE_REMSET_BUFFER store_remset_buffer
672 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
673 #define IN_CRITICAL_REGION thread_info->in_critical_region
675 static pthread_key_t thread_info_key;
676 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
677 #define TLAB_START (__thread_info__->tlab_start)
678 #define TLAB_NEXT (__thread_info__->tlab_next)
679 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
680 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
681 #define REMEMBERED_SET (__thread_info__->remset)
682 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
683 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
684 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
687 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
688 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
689 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
692 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
693 * variables for next+temp_end ?
695 #ifdef HAVE_KW_THREAD
696 static __thread SgenThreadInfo *thread_info;
697 static __thread char *tlab_start;
698 static __thread char *tlab_next;
699 static __thread char *tlab_temp_end;
700 static __thread char *tlab_real_end;
701 static __thread gpointer *store_remset_buffer;
702 static __thread long store_remset_buffer_index;
703 /* Used by the managed allocator/wbarrier */
704 static __thread char **tlab_next_addr;
705 static __thread char *stack_end;
706 static __thread long *store_remset_buffer_index_addr;
708 static char *nursery_next = NULL;
709 static char *nursery_frag_real_end = NULL;
710 static char *nursery_real_end = NULL;
711 static char *nursery_last_pinned_end = NULL;
713 /* The size of a TLAB */
714 /* The bigger the value, the less often we have to go to the slow path to allocate a new
715 * one, but the more space is wasted by threads not allocating much memory.
717 * FIXME: Make this self-tuning for each thread.
719 static guint32 tlab_size = (1024 * 4);
721 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
722 #define MAX_NURSERY_TLAB_WASTE 512
724 /* fragments that are free and ready to be used for allocation */
725 static Fragment *nursery_fragments = NULL;
726 /* freeelist of fragment structures */
727 static Fragment *fragment_freelist = NULL;
729 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
731 /* Functions supplied by the runtime to be called by the GC */
732 static MonoGCCallbacks gc_callbacks;
734 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
735 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
737 #define ALIGN_UP SGEN_ALIGN_UP
739 #define MOVED_OBJECTS_NUM 64
740 static void *moved_objects [MOVED_OBJECTS_NUM];
741 static int moved_objects_idx = 0;
743 /* Vtable of the objects used to fill out nursery fragments before a collection */
744 static MonoVTable *array_fill_vtable;
747 * ######################################################################
748 * ######## Heap size accounting
749 * ######################################################################
752 static mword max_heap_size = ((mword)0)- ((mword)1);
753 static mword allocated_heap;
755 /*Object was pinned during the current collection*/
756 static mword objects_pinned;
759 mono_sgen_release_space (mword size, int space)
761 allocated_heap -= size;
765 available_free_space (void)
767 return max_heap_size - MIN (allocated_heap, max_heap_size);
771 mono_sgen_try_alloc_space (mword size, int space)
773 if (available_free_space () < size)
776 allocated_heap += size;
781 init_heap_size_limits (glong max_heap)
786 if (max_heap < nursery_size * 4) {
787 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
790 max_heap_size = max_heap - nursery_size;
794 * ######################################################################
795 * ######## Macros and function declarations.
796 * ######################################################################
800 align_pointer (void *ptr)
802 mword p = (mword)ptr;
803 p += sizeof (gpointer) - 1;
804 p &= ~ (sizeof (gpointer) - 1);
808 typedef SgenGrayQueue GrayQueue;
810 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
811 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
813 /* forward declarations */
814 static int stop_world (int generation);
815 static int restart_world (int generation);
816 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
817 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
818 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
819 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
820 static void report_finalizer_roots (void);
821 static void report_registered_roots (void);
822 static void find_pinning_ref_from_thread (char *obj, size_t size);
823 static void update_current_thread_stack (void *start);
824 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
825 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
826 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
827 static void null_links_for_domain (MonoDomain *domain, int generation);
828 static gboolean search_fragment_for_size (size_t size);
829 static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
830 static void clear_nursery_fragments (char *next);
831 static void pin_from_roots (void *start_nursery, void *end_nursery);
832 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
833 static void optimize_pin_queue (int start_slot);
834 static void clear_remsets (void);
835 static void clear_tlabs (void);
836 static void sort_addresses (void **array, int size);
837 static void drain_gray_stack (GrayQueue *queue);
838 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
839 static gboolean need_major_collection (mword space_needed);
840 static void major_collection (const char *reason);
842 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
844 void describe_ptr (char *ptr);
845 void check_object (char *start);
847 static void check_consistency (void);
848 static void check_major_refs (void);
849 static void check_scan_starts (void);
850 static void check_for_xdomain_refs (void);
851 static void dump_heap (const char *type, int num, const char *reason);
853 void mono_gc_scan_for_specific_ref (MonoObject *key);
855 static void init_stats (void);
857 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
858 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
859 static void null_ephemerons_for_domain (MonoDomain *domain);
861 SgenMajorCollector major_collector;
863 #include "sgen-pinning.c"
864 #include "sgen-pinning-stats.c"
865 #include "sgen-gray.c"
866 #include "sgen-workers.c"
867 #include "sgen-cardtable.c"
869 /* Root bitmap descriptors are simpler: the lower three bits describe the type
870 * and we either have 30/62 bitmap bits or nibble-based run-length,
871 * or a complex descriptor, or a user defined marker function.
874 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
879 ROOT_DESC_TYPE_MASK = 0x7,
880 ROOT_DESC_TYPE_SHIFT = 3,
883 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
885 #define MAX_USER_DESCRIPTORS 16
887 static gsize* complex_descriptors = NULL;
888 static int complex_descriptors_size = 0;
889 static int complex_descriptors_next = 0;
890 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
891 static int user_descriptors_next = 0;
894 alloc_complex_descriptor (gsize *bitmap, int numbits)
898 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
899 nwords = numbits / GC_BITS_PER_WORD + 1;
902 res = complex_descriptors_next;
903 /* linear search, so we don't have duplicates with domain load/unload
904 * this should not be performance critical or we'd have bigger issues
905 * (the number and size of complex descriptors should be small).
907 for (i = 0; i < complex_descriptors_next; ) {
908 if (complex_descriptors [i] == nwords) {
910 for (j = 0; j < nwords - 1; ++j) {
911 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
921 i += complex_descriptors [i];
923 if (complex_descriptors_next + nwords > complex_descriptors_size) {
924 int new_size = complex_descriptors_size * 2 + nwords;
925 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
926 complex_descriptors_size = new_size;
928 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
929 complex_descriptors_next += nwords;
930 complex_descriptors [res] = nwords;
931 for (i = 0; i < nwords - 1; ++i) {
932 complex_descriptors [res + 1 + i] = bitmap [i];
933 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
940 mono_sgen_get_complex_descriptor (GCVTable *vt)
942 return complex_descriptors + (vt->desc >> LOW_TYPE_BITS);
946 * Descriptor builders.
949 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
951 return (void*) DESC_TYPE_RUN_LENGTH;
955 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
957 int first_set = -1, num_set = 0, last_set = -1, i;
959 size_t stored_size = obj_size;
960 for (i = 0; i < numbits; ++i) {
961 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
969 * We don't encode the size of types that don't contain
970 * references because they might not be aligned, i.e. the
971 * bottom two bits might be set, which would clash with the
972 * bits we need to encode the descriptor type. Since we don't
973 * use the encoded size to skip objects, other than for
974 * processing remsets, in which case only the positions of
975 * references are relevant, this is not a problem.
978 return (void*)DESC_TYPE_RUN_LENGTH;
979 g_assert (!(stored_size & 0x3));
980 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
981 /* check run-length encoding first: one byte offset, one byte number of pointers
982 * on 64 bit archs, we can have 3 runs, just one on 32.
983 * It may be better to use nibbles.
986 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
987 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
989 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
990 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
991 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));
994 /* we know the 2-word header is ptr-free */
995 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
996 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
997 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1001 /* we know the 2-word header is ptr-free */
1002 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1003 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1004 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1005 return (void*) desc;
1007 /* it's a complex object ... */
1008 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1009 return (void*) desc;
1012 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1014 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1016 int first_set = -1, num_set = 0, last_set = -1, i;
1017 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1018 for (i = 0; i < numbits; ++i) {
1019 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1026 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
1028 return (void*)DESC_TYPE_RUN_LENGTH;
1029 if (elem_size <= MAX_ELEMENT_SIZE) {
1030 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1032 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1034 /* Note: we also handle structs with just ref fields */
1035 if (num_set * sizeof (gpointer) == elem_size) {
1036 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1038 /* FIXME: try run-len first */
1039 /* Note: we can't skip the object header here, because it's not present */
1040 if (last_set <= SMALL_BITMAP_SIZE) {
1041 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1044 /* it's am array of complex structs ... */
1045 desc = DESC_TYPE_COMPLEX_ARR;
1046 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1047 return (void*) desc;
1050 /* Return the bitmap encoded by a descriptor */
1052 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1054 mword d = (mword)descr;
1058 case DESC_TYPE_RUN_LENGTH: {
1059 int first_set = (d >> 16) & 0xff;
1060 int num_set = (d >> 24) & 0xff;
1063 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1065 for (i = first_set; i < first_set + num_set; ++i)
1066 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1068 *numbits = first_set + num_set;
1072 case DESC_TYPE_SMALL_BITMAP:
1073 bitmap = g_new0 (gsize, 1);
1075 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1077 *numbits = GC_BITS_PER_WORD;
1081 g_assert_not_reached ();
1086 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1088 MonoObject *o = (MonoObject*)(obj);
1089 MonoObject *ref = (MonoObject*)*(ptr);
1090 int offset = (char*)(ptr) - (char*)o;
1092 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1094 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1096 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1097 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1099 /* Thread.cached_culture_info */
1100 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1101 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1102 !strcmp(o->vtable->klass->name_space, "System") &&
1103 !strcmp(o->vtable->klass->name, "Object[]"))
1106 * 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
1107 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1108 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1109 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1110 * 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
1111 * 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
1112 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1113 * 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
1114 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1116 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1117 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1118 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1119 !strcmp (o->vtable->klass->name, "MemoryStream"))
1121 /* append_job() in threadpool.c */
1122 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1123 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1124 !strcmp (o->vtable->klass->name_space, "System") &&
1125 !strcmp (o->vtable->klass->name, "Object[]") &&
1126 mono_thread_pool_is_queue_array ((MonoArray*) o))
1132 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1134 MonoObject *o = (MonoObject*)(obj);
1135 MonoObject *ref = (MonoObject*)*(ptr);
1136 int offset = (char*)(ptr) - (char*)o;
1138 MonoClassField *field;
1141 if (!ref || ref->vtable->domain == domain)
1143 if (is_xdomain_ref_allowed (ptr, obj, domain))
1147 for (class = o->vtable->klass; class; class = class->parent) {
1150 for (i = 0; i < class->field.count; ++i) {
1151 if (class->fields[i].offset == offset) {
1152 field = &class->fields[i];
1160 if (ref->vtable->klass == mono_defaults.string_class)
1161 str = mono_string_to_utf8 ((MonoString*)ref);
1164 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1165 o, o->vtable->klass->name_space, o->vtable->klass->name,
1166 offset, field ? field->name : "",
1167 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1168 mono_gc_scan_for_specific_ref (o);
1174 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1177 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1179 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1181 #include "sgen-scan-object.h"
1185 #define HANDLE_PTR(ptr,obj) do { \
1186 if ((MonoObject*)*(ptr) == key) { \
1187 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1188 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1193 scan_object_for_specific_ref (char *start, MonoObject *key)
1197 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1200 #include "sgen-scan-object.h"
1204 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1206 while (start < end) {
1210 if (!*(void**)start) {
1211 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1216 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1222 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1224 callback (obj, size, data);
1231 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1233 scan_object_for_specific_ref (obj, key);
1237 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1241 g_print ("found ref to %p in root record %p\n", key, root);
1244 static MonoObject *check_key = NULL;
1245 static RootRecord *check_root = NULL;
1248 check_root_obj_specific_ref_from_marker (void **obj)
1250 check_root_obj_specific_ref (check_root, check_key, *obj);
1254 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1259 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1260 for (root = roots_hash [root_type][i]; root; root = root->next) {
1261 void **start_root = (void**)root->start_root;
1262 mword desc = root->root_desc;
1266 switch (desc & ROOT_DESC_TYPE_MASK) {
1267 case ROOT_DESC_BITMAP:
1268 desc >>= ROOT_DESC_TYPE_SHIFT;
1271 check_root_obj_specific_ref (root, key, *start_root);
1276 case ROOT_DESC_COMPLEX: {
1277 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1278 int bwords = (*bitmap_data) - 1;
1279 void **start_run = start_root;
1281 while (bwords-- > 0) {
1282 gsize bmap = *bitmap_data++;
1283 void **objptr = start_run;
1286 check_root_obj_specific_ref (root, key, *objptr);
1290 start_run += GC_BITS_PER_WORD;
1294 case ROOT_DESC_USER: {
1295 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1296 marker (start_root, check_root_obj_specific_ref_from_marker);
1299 case ROOT_DESC_RUN_LEN:
1300 g_assert_not_reached ();
1302 g_assert_not_reached ();
1311 mono_gc_scan_for_specific_ref (MonoObject *key)
1316 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1317 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1319 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1321 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1323 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1324 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1326 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1327 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1328 void **ptr = (void**)root->start_root;
1330 while (ptr < (void**)root->end_root) {
1331 check_root_obj_specific_ref (root, *ptr, key);
1339 clear_current_nursery_fragment (char *next)
1341 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1342 g_assert (next <= nursery_frag_real_end);
1343 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", next, nursery_frag_real_end));
1344 memset (next, 0, nursery_frag_real_end - next);
1348 /* Clear all remaining nursery fragments */
1350 clear_nursery_fragments (char *next)
1353 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1354 clear_current_nursery_fragment (next);
1355 for (frag = nursery_fragments; frag; frag = frag->next) {
1356 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_start, frag->fragment_end));
1357 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1363 need_remove_object_for_domain (char *start, MonoDomain *domain)
1365 if (mono_object_domain (start) == domain) {
1366 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1367 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1374 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1376 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1377 if (vt->klass == mono_defaults.internal_thread_class)
1378 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1379 /* The object could be a proxy for an object in the domain
1381 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1382 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1384 /* The server could already have been zeroed out, so
1385 we need to check for that, too. */
1386 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1387 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1389 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1394 static MonoDomain *check_domain = NULL;
1397 check_obj_not_in_domain (void **o)
1399 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1403 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1407 check_domain = domain;
1408 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1409 for (root = roots_hash [root_type][i]; root; root = root->next) {
1410 void **start_root = (void**)root->start_root;
1411 mword desc = root->root_desc;
1413 /* The MonoDomain struct is allowed to hold
1414 references to objects in its own domain. */
1415 if (start_root == (void**)domain)
1418 switch (desc & ROOT_DESC_TYPE_MASK) {
1419 case ROOT_DESC_BITMAP:
1420 desc >>= ROOT_DESC_TYPE_SHIFT;
1422 if ((desc & 1) && *start_root)
1423 check_obj_not_in_domain (*start_root);
1428 case ROOT_DESC_COMPLEX: {
1429 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1430 int bwords = (*bitmap_data) - 1;
1431 void **start_run = start_root;
1433 while (bwords-- > 0) {
1434 gsize bmap = *bitmap_data++;
1435 void **objptr = start_run;
1437 if ((bmap & 1) && *objptr)
1438 check_obj_not_in_domain (*objptr);
1442 start_run += GC_BITS_PER_WORD;
1446 case ROOT_DESC_USER: {
1447 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1448 marker (start_root, check_obj_not_in_domain);
1451 case ROOT_DESC_RUN_LEN:
1452 g_assert_not_reached ();
1454 g_assert_not_reached ();
1458 check_domain = NULL;
1462 check_for_xdomain_refs (void)
1466 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1467 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1469 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1471 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1472 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1476 clear_domain_process_object (char *obj, MonoDomain *domain)
1480 process_object_for_domain_clearing (obj, domain);
1481 remove = need_remove_object_for_domain (obj, domain);
1483 if (remove && ((MonoObject*)obj)->synchronisation) {
1484 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1486 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1493 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1495 if (clear_domain_process_object (obj, domain))
1496 memset (obj, 0, size);
1500 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1502 clear_domain_process_object (obj, domain);
1506 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1508 if (need_remove_object_for_domain (obj, domain))
1509 major_collector.free_non_pinned_object (obj, size);
1513 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1515 if (need_remove_object_for_domain (obj, domain))
1516 major_collector.free_pinned_object (obj, size);
1520 * When appdomains are unloaded we can easily remove objects that have finalizers,
1521 * but all the others could still be present in random places on the heap.
1522 * We need a sweep to get rid of them even though it's going to be costly
1524 * The reason we need to remove them is because we access the vtable and class
1525 * structures to know the object size and the reference bitmap: once the domain is
1526 * unloaded the point to random memory.
1529 mono_gc_clear_domain (MonoDomain * domain)
1531 LOSObject *bigobj, *prev;
1536 clear_nursery_fragments (nursery_next);
1538 if (xdomain_checks && domain != mono_get_root_domain ()) {
1539 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1540 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1541 check_for_xdomain_refs ();
1544 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1545 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1547 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1548 to memory returned to the OS.*/
1549 null_ephemerons_for_domain (domain);
1551 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1552 null_links_for_domain (domain, i);
1554 /* We need two passes over major and large objects because
1555 freeing such objects might give their memory back to the OS
1556 (in the case of large objects) or obliterate its vtable
1557 (pinned objects with major-copying or pinned and non-pinned
1558 objects with major-mark&sweep), but we might need to
1559 dereference a pointer from an object to another object if
1560 the first object is a proxy. */
1561 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1562 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1563 clear_domain_process_object (bigobj->data, domain);
1566 for (bigobj = los_object_list; bigobj;) {
1567 if (need_remove_object_for_domain (bigobj->data, domain)) {
1568 LOSObject *to_free = bigobj;
1570 prev->next = bigobj->next;
1572 los_object_list = bigobj->next;
1573 bigobj = bigobj->next;
1574 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1576 mono_sgen_los_free_object (to_free);
1580 bigobj = bigobj->next;
1582 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1583 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1589 global_remset_cache_clear (void)
1591 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1595 * Tries to check if a given remset location was already added to the global remset.
1598 * A 2 entry, LRU cache of recently saw location remsets.
1600 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1602 * Returns TRUE is the element was added..
1605 global_remset_location_was_not_added (gpointer ptr)
1608 gpointer first = global_remset_cache [0], second;
1610 HEAVY_STAT (++stat_global_remsets_discarded);
1614 second = global_remset_cache [1];
1616 if (second == ptr) {
1617 /*Move the second to the front*/
1618 global_remset_cache [0] = second;
1619 global_remset_cache [1] = first;
1621 HEAVY_STAT (++stat_global_remsets_discarded);
1625 global_remset_cache [0] = second;
1626 global_remset_cache [1] = ptr;
1631 * mono_sgen_add_to_global_remset:
1633 * The global remset contains locations which point into newspace after
1634 * a minor collection. This can happen if the objects they point to are pinned.
1636 * LOCKING: If called from a parallel collector, the global remset
1637 * lock must be held. For serial collectors that is not necessary.
1640 mono_sgen_add_to_global_remset (gpointer ptr)
1645 if (use_cardtable) {
1646 sgen_card_table_mark_address ((mword)ptr);
1650 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1652 lock = (current_collection_generation == GENERATION_OLD && major_collector.is_parallel);
1656 if (!global_remset_location_was_not_added (ptr))
1659 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1660 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1662 HEAVY_STAT (++stat_global_remsets_added);
1665 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1666 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1668 if (global_remset->store_next + 3 < global_remset->end_set) {
1669 *(global_remset->store_next++) = (mword)ptr;
1672 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1673 rs->next = global_remset;
1675 *(global_remset->store_next++) = (mword)ptr;
1678 int global_rs_size = 0;
1680 for (rs = global_remset; rs; rs = rs->next) {
1681 global_rs_size += rs->store_next - rs->data;
1683 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1688 UNLOCK_GLOBAL_REMSET;
1694 * Scan objects in the gray stack until the stack is empty. This should be called
1695 * frequently after each object is copied, to achieve better locality and cache
1699 drain_gray_stack (GrayQueue *queue)
1703 if (current_collection_generation == GENERATION_NURSERY) {
1705 GRAY_OBJECT_DEQUEUE (queue, obj);
1708 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1709 major_collector.minor_scan_object (obj, queue);
1712 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1716 GRAY_OBJECT_DEQUEUE (queue, obj);
1719 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1720 major_collector.major_scan_object (obj, queue);
1726 * Addresses from start to end are already sorted. This function finds
1727 * the object header for each address and pins the object. The
1728 * addresses must be inside the passed section. The (start of the)
1729 * address array is overwritten with the addresses of the actually
1730 * pinned objects. Return the number of pinned objects.
1733 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1738 void *last_obj = NULL;
1739 size_t last_obj_size = 0;
1742 void **definitely_pinned = start;
1746 * The code below starts the search from an entry in scan_starts, which might point into a nursery
1747 * fragment containing random data. Clearing the nursery fragments takes a lot of time, and searching
1748 * though them too, so lay arrays at each location inside a fragment where a search can start:
1749 * - scan_locations[i]
1751 * - the start of each fragment (the last_obj + last_obj case)
1752 * The third encompasses the first two, since scan_locations [i] can't point inside a nursery fragment.
1754 for (frag = nursery_fragments; frag; frag = frag->next) {
1757 g_assert (frag->fragment_end - frag->fragment_start >= sizeof (MonoArray));
1758 o = (MonoArray*)frag->fragment_start;
1759 memset (o, 0, sizeof (MonoArray));
1760 g_assert (array_fill_vtable);
1761 o->obj.vtable = array_fill_vtable;
1762 /* Mark this as not a real object */
1763 o->obj.synchronisation = GINT_TO_POINTER (-1);
1764 o->max_length = (frag->fragment_end - frag->fragment_start) - sizeof (MonoArray);
1765 g_assert (frag->fragment_start + safe_object_get_size ((MonoObject*)o) == frag->fragment_end);
1768 while (start < end) {
1770 /* the range check should be reduntant */
1771 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1772 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1773 /* multiple pointers to the same object */
1774 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1778 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1779 g_assert (idx < section->num_scan_start);
1780 search_start = (void*)section->scan_starts [idx];
1781 if (!search_start || search_start > addr) {
1784 search_start = section->scan_starts [idx];
1785 if (search_start && search_start <= addr)
1788 if (!search_start || search_start > addr)
1789 search_start = start_nursery;
1791 if (search_start < last_obj)
1792 search_start = (char*)last_obj + last_obj_size;
1793 /* now addr should be in an object a short distance from search_start
1794 * Note that search_start must point to zeroed mem or point to an object.
1798 if (!*(void**)search_start) {
1799 /* Consistency check */
1801 for (frag = nursery_fragments; frag; frag = frag->next) {
1802 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1803 g_assert_not_reached ();
1807 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1810 last_obj = search_start;
1811 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1813 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1814 /* Marks the beginning of a nursery fragment, skip */
1816 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1817 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1818 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));
1819 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1820 pin_object (search_start);
1821 GRAY_OBJECT_ENQUEUE (queue, search_start);
1823 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1824 definitely_pinned [count] = search_start;
1829 /* skip to the next object */
1830 search_start = (void*)((char*)search_start + last_obj_size);
1831 } while (search_start <= addr);
1832 /* we either pinned the correct object or we ignored the addr because
1833 * it points to unused zeroed memory.
1839 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1840 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1841 GCRootReport report;
1843 for (idx = 0; idx < count; ++idx)
1844 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
1845 notify_gc_roots (&report);
1851 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1853 int num_entries = section->pin_queue_num_entries;
1855 void **start = section->pin_queue_start;
1857 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1858 section->data, section->next_data, queue);
1859 section->pin_queue_num_entries = reduced_to;
1861 section->pin_queue_start = NULL;
1867 mono_sgen_pin_object (void *object, GrayQueue *queue)
1869 if (major_collector.is_parallel) {
1871 /*object arrives pinned*/
1872 pin_stage_ptr (object);
1876 SGEN_PIN_OBJECT (object);
1877 pin_stage_ptr (object);
1880 GRAY_OBJECT_ENQUEUE (queue, object);
1883 /* Sort the addresses in array in increasing order.
1884 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1887 sort_addresses (void **array, int size)
1892 for (i = 1; i < size; ++i) {
1895 int parent = (child - 1) / 2;
1897 if (array [parent] >= array [child])
1900 tmp = array [parent];
1901 array [parent] = array [child];
1902 array [child] = tmp;
1908 for (i = size - 1; i > 0; --i) {
1911 array [i] = array [0];
1917 while (root * 2 + 1 <= end) {
1918 int child = root * 2 + 1;
1920 if (child < end && array [child] < array [child + 1])
1922 if (array [root] >= array [child])
1926 array [root] = array [child];
1927 array [child] = tmp;
1934 static G_GNUC_UNUSED void
1935 print_nursery_gaps (void* start_nursery, void *end_nursery)
1938 gpointer first = start_nursery;
1940 for (i = 0; i < next_pin_slot; ++i) {
1941 next = pin_queue [i];
1942 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1946 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1949 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1951 optimize_pin_queue (int start_slot)
1953 void **start, **cur, **end;
1954 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1955 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1956 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1957 if ((next_pin_slot - start_slot) > 1)
1958 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1959 start = cur = pin_queue + start_slot;
1960 end = pin_queue + next_pin_slot;
1963 while (*start == *cur && cur < end)
1967 next_pin_slot = start - pin_queue;
1968 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1969 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1974 * Scan the memory between start and end and queue values which could be pointers
1975 * to the area between start_nursery and end_nursery for later consideration.
1976 * Typically used for thread stacks.
1979 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1982 while (start < end) {
1983 if (*start >= start_nursery && *start < end_nursery) {
1985 * *start can point to the middle of an object
1986 * note: should we handle pointing at the end of an object?
1987 * pinning in C# code disallows pointing at the end of an object
1988 * but there is some small chance that an optimizing C compiler
1989 * may keep the only reference to an object by pointing
1990 * at the end of it. We ignore this small chance for now.
1991 * Pointers to the end of an object are indistinguishable
1992 * from pointers to the start of the next object in memory
1993 * so if we allow that we'd need to pin two objects...
1994 * We queue the pointer in an array, the
1995 * array will then be sorted and uniqued. This way
1996 * we can coalesce several pinning pointers and it should
1997 * be faster since we'd do a memory scan with increasing
1998 * addresses. Note: we can align the address to the allocation
1999 * alignment, so the unique process is more effective.
2001 mword addr = (mword)*start;
2002 addr &= ~(ALLOC_ALIGN - 1);
2003 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2004 pin_stage_ptr ((void*)addr);
2006 pin_stats_register_address ((char*)addr, pin_type);
2007 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
2012 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2016 * Debugging function: find in the conservative roots where @obj is being pinned.
2018 static G_GNUC_UNUSED void
2019 find_pinning_reference (char *obj, size_t size)
2023 char *endobj = obj + size;
2024 for (i = 0; i < roots_hash_size [0]; ++i) {
2025 for (root = roots_hash [0][i]; root; root = root->next) {
2026 /* if desc is non-null it has precise info */
2027 if (!root->root_desc) {
2028 char ** start = (char**)root->start_root;
2029 while (start < (char**)root->end_root) {
2030 if (*start >= obj && *start < endobj) {
2031 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));
2038 find_pinning_ref_from_thread (obj, size);
2042 * The first thing we do in a collection is to identify pinned objects.
2043 * This function considers all the areas of memory that need to be
2044 * conservatively scanned.
2047 pin_from_roots (void *start_nursery, void *end_nursery)
2051 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]));
2052 /* objects pinned from the API are inside these roots */
2053 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2054 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2055 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2056 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2059 /* now deal with the thread stacks
2060 * in the future we should be able to conservatively scan only:
2061 * *) the cpu registers
2062 * *) the unmanaged stack frames
2063 * *) the _last_ managed stack frame
2064 * *) pointers slots in managed frames
2066 scan_thread_data (start_nursery, end_nursery, FALSE);
2068 evacuate_pin_staging_area ();
2071 static CopyOrMarkObjectFunc user_copy_or_mark_func;
2072 static GrayQueue *user_copy_or_mark_queue;
2075 single_arg_user_copy_or_mark (void **obj)
2077 user_copy_or_mark_func (obj, user_copy_or_mark_queue);
2081 * The memory area from start_root to end_root contains pointers to objects.
2082 * Their position is precisely described by @desc (this means that the pointer
2083 * can be either NULL or the pointer to the start of an object).
2084 * This functions copies them to to_space updates them.
2086 * This function is not thread-safe!
2089 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2091 switch (desc & ROOT_DESC_TYPE_MASK) {
2092 case ROOT_DESC_BITMAP:
2093 desc >>= ROOT_DESC_TYPE_SHIFT;
2095 if ((desc & 1) && *start_root) {
2096 copy_func (start_root, queue);
2097 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2098 drain_gray_stack (queue);
2104 case ROOT_DESC_COMPLEX: {
2105 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2106 int bwords = (*bitmap_data) - 1;
2107 void **start_run = start_root;
2109 while (bwords-- > 0) {
2110 gsize bmap = *bitmap_data++;
2111 void **objptr = start_run;
2113 if ((bmap & 1) && *objptr) {
2114 copy_func (objptr, queue);
2115 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2116 drain_gray_stack (queue);
2121 start_run += GC_BITS_PER_WORD;
2125 case ROOT_DESC_USER: {
2126 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2127 user_copy_or_mark_func = copy_func;
2128 user_copy_or_mark_queue = queue;
2129 marker (start_root, single_arg_user_copy_or_mark);
2130 user_copy_or_mark_func = NULL;
2131 user_copy_or_mark_queue = NULL;
2134 case ROOT_DESC_RUN_LEN:
2135 g_assert_not_reached ();
2137 g_assert_not_reached ();
2142 reset_heap_boundaries (void)
2144 lowest_heap_address = ~(mword)0;
2145 highest_heap_address = 0;
2149 mono_sgen_update_heap_boundaries (mword low, mword high)
2154 old = lowest_heap_address;
2157 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2160 old = highest_heap_address;
2163 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2167 alloc_fragment (void)
2169 Fragment *frag = fragment_freelist;
2171 fragment_freelist = frag->next;
2175 frag = mono_sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
2180 /* size must be a power of 2 */
2182 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2184 /* Allocate twice the memory to be able to put the block on an aligned address */
2185 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2190 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2191 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2194 mono_sgen_free_os_memory (mem, aligned - mem);
2195 if (aligned + size < mem + size + alignment)
2196 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2202 * Allocate and setup the data structures needed to be able to allocate objects
2203 * in the nursery. The nursery is stored in nursery_section.
2206 alloc_nursery (void)
2208 GCMemSection *section;
2214 if (nursery_section)
2216 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2217 /* later we will alloc a larger area for the nursery but only activate
2218 * what we need. The rest will be used as expansion if we have too many pinned
2219 * objects in the existing nursery.
2221 /* FIXME: handle OOM */
2222 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2224 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2225 alloc_size = nursery_size;
2226 #ifdef SGEN_ALIGN_NURSERY
2227 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2229 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2231 nursery_start = data;
2232 nursery_real_end = nursery_start + nursery_size;
2233 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
2234 nursery_next = nursery_start;
2235 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));
2236 section->data = section->next_data = data;
2237 section->size = alloc_size;
2238 section->end_data = nursery_real_end;
2239 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2240 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2241 section->num_scan_start = scan_starts;
2242 section->block.role = MEMORY_ROLE_GEN0;
2243 section->block.next = NULL;
2245 nursery_section = section;
2247 /* Setup the single first large fragment */
2248 frag = alloc_fragment ();
2249 frag->fragment_start = nursery_start;
2250 frag->fragment_limit = nursery_start;
2251 frag->fragment_end = nursery_real_end;
2252 nursery_frag_real_end = nursery_real_end;
2253 /* FIXME: frag here is lost */
2257 mono_gc_get_nursery (int *shift_bits, size_t *size)
2259 *size = nursery_size;
2260 #ifdef SGEN_ALIGN_NURSERY
2261 *shift_bits = DEFAULT_NURSERY_BITS;
2265 return nursery_start;
2269 report_finalizer_roots_list (FinalizeEntry *list)
2271 GCRootReport report;
2275 for (fin = list; fin; fin = fin->next) {
2278 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2280 notify_gc_roots (&report);
2284 report_finalizer_roots (void)
2286 report_finalizer_roots_list (fin_ready_list);
2287 report_finalizer_roots_list (critical_fin_list);
2290 static GCRootReport *root_report;
2293 single_arg_report_root (void **obj)
2296 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2300 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2302 switch (desc & ROOT_DESC_TYPE_MASK) {
2303 case ROOT_DESC_BITMAP:
2304 desc >>= ROOT_DESC_TYPE_SHIFT;
2306 if ((desc & 1) && *start_root) {
2307 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2313 case ROOT_DESC_COMPLEX: {
2314 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2315 int bwords = (*bitmap_data) - 1;
2316 void **start_run = start_root;
2318 while (bwords-- > 0) {
2319 gsize bmap = *bitmap_data++;
2320 void **objptr = start_run;
2322 if ((bmap & 1) && *objptr) {
2323 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2328 start_run += GC_BITS_PER_WORD;
2332 case ROOT_DESC_USER: {
2333 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2334 root_report = report;
2335 marker (start_root, single_arg_report_root);
2338 case ROOT_DESC_RUN_LEN:
2339 g_assert_not_reached ();
2341 g_assert_not_reached ();
2346 report_registered_roots_by_type (int root_type)
2348 GCRootReport report;
2352 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2353 for (root = roots_hash [root_type][i]; root; root = root->next) {
2354 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2355 precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
2358 notify_gc_roots (&report);
2362 report_registered_roots (void)
2364 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2365 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2369 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2373 for (fin = list; fin; fin = fin->next) {
2376 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2377 copy_func (&fin->object, queue);
2381 static mword fragment_total = 0;
2383 * We found a fragment of free memory in the nursery: memzero it and if
2384 * it is big enough, add it to the list of fragments that can be used for
2388 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2391 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2392 binary_protocol_empty (frag_start, frag_size);
2393 /* Not worth dealing with smaller fragments: need to tune */
2394 if (frag_size >= FRAGMENT_MIN_SIZE) {
2395 /* memsetting just the first chunk start is bound to provide better cache locality */
2396 if (nursery_clear_policy == CLEAR_AT_GC)
2397 memset (frag_start, 0, frag_size);
2399 fragment = alloc_fragment ();
2400 fragment->fragment_start = frag_start;
2401 fragment->fragment_limit = frag_start;
2402 fragment->fragment_end = frag_end;
2403 fragment->next = nursery_fragments;
2404 nursery_fragments = fragment;
2405 fragment_total += frag_size;
2407 /* Clear unused fragments, pinning depends on this */
2408 /*TODO place an int[] here instead of the memset if size justify it*/
2409 memset (frag_start, 0, frag_size);
2414 generation_name (int generation)
2416 switch (generation) {
2417 case GENERATION_NURSERY: return "nursery";
2418 case GENERATION_OLD: return "old";
2419 default: g_assert_not_reached ();
2423 static DisappearingLinkHashTable*
2424 get_dislink_hash_table (int generation)
2426 switch (generation) {
2427 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2428 case GENERATION_OLD: return &major_disappearing_link_hash;
2429 default: g_assert_not_reached ();
2433 static FinalizeEntryHashTable*
2434 get_finalize_entry_hash_table (int generation)
2436 switch (generation) {
2437 case GENERATION_NURSERY: return &minor_finalizable_hash;
2438 case GENERATION_OLD: return &major_finalizable_hash;
2439 default: g_assert_not_reached ();
2444 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2449 int ephemeron_rounds = 0;
2450 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2453 * We copied all the reachable objects. Now it's the time to copy
2454 * the objects that were not referenced by the roots, but by the copied objects.
2455 * we built a stack of objects pointed to by gray_start: they are
2456 * additional roots and we may add more items as we go.
2457 * We loop until gray_start == gray_objects which means no more objects have
2458 * been added. Note this is iterative: no recursion is involved.
2459 * We need to walk the LO list as well in search of marked big objects
2460 * (use a flag since this is needed only on major collections). We need to loop
2461 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2462 * To achieve better cache locality and cache usage, we drain the gray stack
2463 * frequently, after each object is copied, and just finish the work here.
2465 drain_gray_stack (queue);
2467 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2468 /* walk the finalization queue and move also the objects that need to be
2469 * finalized: use the finalized objects as new roots so the objects they depend
2470 * on are also not reclaimed. As with the roots above, only objects in the nursery
2471 * are marked/copied.
2472 * We need a loop here, since objects ready for finalizers may reference other objects
2473 * that are fin-ready. Speedup with a flag?
2477 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2478 * before processing finalizable objects to avoid finalizing reachable values.
2480 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2481 * while they are been finalized.
2483 int done_with_ephemerons = 0;
2485 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2486 drain_gray_stack (queue);
2488 } while (!done_with_ephemerons);
2490 fin_ready = num_ready_finalizers;
2491 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2492 if (generation == GENERATION_OLD)
2493 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
2495 /* drain the new stack that might have been created */
2496 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2497 drain_gray_stack (queue);
2498 } while (fin_ready != num_ready_finalizers);
2501 * Clear ephemeron pairs with unreachable keys.
2502 * We pass the copy func so we can figure out if an array was promoted or not.
2504 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2507 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));
2510 * handle disappearing links
2511 * Note we do this after checking the finalization queue because if an object
2512 * survives (at least long enough to be finalized) we don't clear the link.
2513 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2514 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2517 g_assert (gray_object_queue_is_empty (queue));
2519 null_link_in_range (copy_func, start_addr, end_addr, generation, queue);
2520 if (generation == GENERATION_OLD)
2521 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, queue);
2522 if (gray_object_queue_is_empty (queue))
2524 drain_gray_stack (queue);
2527 g_assert (gray_object_queue_is_empty (queue));
2531 mono_sgen_check_section_scan_starts (GCMemSection *section)
2534 for (i = 0; i < section->num_scan_start; ++i) {
2535 if (section->scan_starts [i]) {
2536 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2537 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2543 check_scan_starts (void)
2545 if (!do_scan_starts_check)
2547 mono_sgen_check_section_scan_starts (nursery_section);
2548 major_collector.check_scan_starts ();
2551 static int last_num_pinned = 0;
2554 build_nursery_fragments (void **start, int num_entries)
2556 char *frag_start, *frag_end;
2560 while (nursery_fragments) {
2561 Fragment *next = nursery_fragments->next;
2562 nursery_fragments->next = fragment_freelist;
2563 fragment_freelist = nursery_fragments;
2564 nursery_fragments = next;
2566 frag_start = nursery_start;
2568 /* clear scan starts */
2569 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2570 for (i = 0; i < num_entries; ++i) {
2571 frag_end = start [i];
2572 /* remove the pin bit from pinned objects */
2573 unpin_object (frag_end);
2574 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2575 frag_size = frag_end - frag_start;
2577 add_nursery_frag (frag_size, frag_start, frag_end);
2578 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)start [i]));
2579 frag_start = (char*)start [i] + frag_size;
2581 nursery_last_pinned_end = frag_start;
2582 frag_end = nursery_real_end;
2583 frag_size = frag_end - frag_start;
2585 add_nursery_frag (frag_size, frag_start, frag_end);
2586 if (!nursery_fragments) {
2587 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
2588 for (i = 0; i < num_entries; ++i) {
2589 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])));
2594 nursery_next = nursery_frag_real_end = NULL;
2596 /* Clear TLABs for all threads */
2601 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2605 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2606 for (root = roots_hash [root_type][i]; root; root = root->next) {
2607 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2608 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2614 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2616 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2620 mono_sgen_dump_section (GCMemSection *section, const char *type)
2622 char *start = section->data;
2623 char *end = section->data + section->size;
2624 char *occ_start = NULL;
2626 char *old_start = NULL; /* just for debugging */
2628 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2630 while (start < end) {
2634 if (!*(void**)start) {
2636 mono_sgen_dump_occupied (occ_start, start, section->data);
2639 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2642 g_assert (start < section->next_data);
2647 vt = (GCVTable*)LOAD_VTABLE (start);
2650 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2653 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2654 start - section->data,
2655 vt->klass->name_space, vt->klass->name,
2663 mono_sgen_dump_occupied (occ_start, start, section->data);
2665 fprintf (heap_dump_file, "</section>\n");
2669 dump_object (MonoObject *obj, gboolean dump_location)
2671 static char class_name [1024];
2673 MonoClass *class = mono_object_class (obj);
2677 * Python's XML parser is too stupid to parse angle brackets
2678 * in strings, so we just ignore them;
2681 while (class->name [i] && j < sizeof (class_name) - 1) {
2682 if (!strchr ("<>\"", class->name [i]))
2683 class_name [j++] = class->name [i];
2686 g_assert (j < sizeof (class_name));
2689 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2690 class->name_space, class_name,
2691 safe_object_get_size (obj));
2692 if (dump_location) {
2693 const char *location;
2694 if (ptr_in_nursery (obj))
2695 location = "nursery";
2696 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2700 fprintf (heap_dump_file, " location=\"%s\"", location);
2702 fprintf (heap_dump_file, "/>\n");
2706 dump_heap (const char *type, int num, const char *reason)
2711 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2713 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2714 fprintf (heap_dump_file, ">\n");
2715 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2716 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2717 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2718 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2719 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2721 fprintf (heap_dump_file, "<pinned-objects>\n");
2722 for (list = pinned_objects; list; list = list->next)
2723 dump_object (list->obj, TRUE);
2724 fprintf (heap_dump_file, "</pinned-objects>\n");
2726 mono_sgen_dump_section (nursery_section, "nursery");
2728 major_collector.dump_heap (heap_dump_file);
2730 fprintf (heap_dump_file, "<los>\n");
2731 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2732 dump_object ((MonoObject*)bigobj->data, FALSE);
2733 fprintf (heap_dump_file, "</los>\n");
2735 fprintf (heap_dump_file, "</collection>\n");
2739 mono_sgen_register_moved_object (void *obj, void *destination)
2741 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2743 /* FIXME: handle this for parallel collector */
2744 g_assert (!major_collector.is_parallel);
2746 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2747 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2748 moved_objects_idx = 0;
2750 moved_objects [moved_objects_idx++] = obj;
2751 moved_objects [moved_objects_idx++] = destination;
2757 static gboolean inited = FALSE;
2762 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2763 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2764 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2765 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2766 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2767 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2768 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2769 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2770 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2772 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2773 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2774 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2775 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2776 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2777 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2778 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2779 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2780 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2781 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2782 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2783 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2784 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2787 #ifdef HEAVY_STATISTICS
2788 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2789 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2790 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2791 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2792 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2793 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2794 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2795 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2797 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2798 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2799 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2800 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2801 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2803 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2804 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2805 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2806 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2808 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2809 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2811 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2812 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2813 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2815 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
2816 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
2818 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2819 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2820 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2821 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2822 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2823 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2824 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2825 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2826 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2832 static gboolean need_calculate_minor_collection_allowance;
2834 static int last_collection_old_num_major_sections;
2835 static mword last_collection_los_memory_usage = 0;
2836 static mword last_collection_old_los_memory_usage;
2837 static mword last_collection_los_memory_alloced;
2840 reset_minor_collection_allowance (void)
2842 need_calculate_minor_collection_allowance = TRUE;
2846 try_calculate_minor_collection_allowance (gboolean overwrite)
2848 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2849 mword los_memory_saved;
2852 g_assert (need_calculate_minor_collection_allowance);
2854 if (!need_calculate_minor_collection_allowance)
2857 if (!*major_collector.have_swept) {
2859 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2863 num_major_sections = major_collector.get_num_major_sections ();
2865 num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
2866 los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
2868 save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2871 * We aim to allow the allocation of as many sections as is
2872 * necessary to reclaim save_target sections in the next
2873 * collection. We assume the collection pattern won't change.
2874 * In the last cycle, we had num_major_sections_saved for
2875 * minor_collection_sections_alloced. Assuming things won't
2876 * change, this must be the same ratio as save_target for
2877 * allowance_target, i.e.
2879 * num_major_sections_saved save_target
2880 * --------------------------------- == ----------------
2881 * minor_collection_sections_alloced allowance_target
2885 allowance_target = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major_collector.section_size + last_collection_los_memory_alloced) / (double)(num_major_sections_saved * major_collector.section_size + los_memory_saved));
2887 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2889 if (major_collector.have_computed_minor_collection_allowance)
2890 major_collector.have_computed_minor_collection_allowance ();
2892 need_calculate_minor_collection_allowance = FALSE;
2896 need_major_collection (mword space_needed)
2898 mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
2899 return (space_needed > available_free_space ()) ||
2900 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
2904 mono_sgen_need_major_collection (mword space_needed)
2906 return need_major_collection (space_needed);
2910 * Collect objects in the nursery. Returns whether to trigger a major
2914 collect_nursery (size_t requested_size)
2916 gboolean needs_major;
2917 size_t max_garbage_amount;
2918 char *orig_nursery_next;
2919 TV_DECLARE (all_atv);
2920 TV_DECLARE (all_btv);
2924 mono_perfcounters->gc_collections0++;
2926 current_collection_generation = GENERATION_NURSERY;
2928 binary_protocol_collection (GENERATION_NURSERY);
2929 check_scan_starts ();
2933 orig_nursery_next = nursery_next;
2934 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
2935 /* FIXME: optimize later to use the higher address where an object can be present */
2936 nursery_next = MAX (nursery_next, nursery_real_end);
2938 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)));
2939 max_garbage_amount = nursery_next - nursery_start;
2940 g_assert (nursery_section->size >= max_garbage_amount);
2942 /* world must be stopped already */
2943 TV_GETTIME (all_atv);
2946 /* Pinning no longer depends on clearing all nursery fragments */
2947 clear_current_nursery_fragment (orig_nursery_next);
2950 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2953 check_for_xdomain_refs ();
2955 nursery_section->next_data = nursery_next;
2957 major_collector.start_nursery_collection ();
2959 try_calculate_minor_collection_allowance (FALSE);
2961 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
2964 mono_stats.minor_gc_count ++;
2966 global_remset_cache_clear ();
2968 /* pin from pinned handles */
2970 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2971 pin_from_roots (nursery_start, nursery_next);
2972 /* identify pinned objects */
2973 optimize_pin_queue (0);
2974 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, &gray_queue);
2975 nursery_section->pin_queue_start = pin_queue;
2976 nursery_section->pin_queue_num_entries = next_pin_slot;
2978 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
2979 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
2980 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2982 if (consistency_check_at_minor_collection)
2983 check_consistency ();
2986 * walk all the roots and copy the young objects to the old generation,
2987 * starting from to_space
2990 scan_from_remsets (nursery_start, nursery_next, &gray_queue);
2991 /* we don't have complete write barrier yet, so we scan all the old generation sections */
2993 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
2994 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2996 if (use_cardtable) {
2998 card_tables_collect_stats (TRUE);
2999 scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
3001 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
3004 drain_gray_stack (&gray_queue);
3006 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3007 report_registered_roots ();
3008 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3009 report_finalizer_roots ();
3011 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3012 /* registered roots, this includes static fields */
3013 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
3014 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
3016 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3018 scan_thread_data (nursery_start, nursery_next, TRUE);
3020 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3023 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3025 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3026 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3028 if (objects_pinned) {
3029 evacuate_pin_staging_area ();
3030 optimize_pin_queue (0);
3031 nursery_section->pin_queue_start = pin_queue;
3032 nursery_section->pin_queue_num_entries = next_pin_slot;
3035 /* walk the pin_queue, build up the fragment list of free memory, unmark
3036 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3039 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3040 build_nursery_fragments (pin_queue, next_pin_slot);
3041 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3043 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3044 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3046 if (consistency_check_at_minor_collection)
3047 check_major_refs ();
3049 major_collector.finish_nursery_collection ();
3051 TV_GETTIME (all_btv);
3052 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3055 dump_heap ("minor", num_minor_gcs - 1, NULL);
3057 /* prepare the pin queue for the next collection */
3058 last_num_pinned = next_pin_slot;
3060 if (fin_ready_list || critical_fin_list) {
3061 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3062 mono_gc_finalize_notify ();
3066 g_assert (gray_object_queue_is_empty (&gray_queue));
3069 card_tables_collect_stats (FALSE);
3071 check_scan_starts ();
3073 binary_protocol_flush_buffers (FALSE);
3075 /*objects are late pinned because of lack of memory, so a major is a good call*/
3076 needs_major = need_major_collection (0) || objects_pinned;
3077 current_collection_generation = -1;
3084 major_do_collection (const char *reason)
3086 LOSObject *bigobj, *prevbo;
3087 TV_DECLARE (all_atv);
3088 TV_DECLARE (all_btv);
3091 /* FIXME: only use these values for the precise scan
3092 * note that to_space pointers should be excluded anyway...
3094 char *heap_start = NULL;
3095 char *heap_end = (char*)-1;
3096 int old_next_pin_slot;
3098 mono_perfcounters->gc_collections1++;
3100 last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3103 * A domain could have been freed, resulting in
3104 * los_memory_usage being less than last_collection_los_memory_usage.
3106 last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3107 last_collection_old_los_memory_usage = los_memory_usage;
3110 //count_ref_nonref_objs ();
3111 //consistency_check ();
3113 binary_protocol_collection (GENERATION_OLD);
3114 check_scan_starts ();
3115 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3116 if (major_collector.is_parallel)
3117 gray_object_queue_init (&workers_distribute_gray_queue, mono_sgen_get_unmanaged_allocator ());
3120 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3122 mono_stats.major_gc_count ++;
3124 /* world must be stopped already */
3125 TV_GETTIME (all_atv);
3128 /* Pinning depends on this */
3129 clear_nursery_fragments (nursery_next);
3132 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3134 nursery_section->next_data = nursery_real_end;
3135 /* we should also coalesce scanning from sections close to each other
3136 * and deal with pointers outside of the sections later.
3139 if (major_collector.start_major_collection)
3140 major_collector.start_major_collection ();
3142 *major_collector.have_swept = FALSE;
3143 reset_minor_collection_allowance ();
3146 check_for_xdomain_refs ();
3148 /* The remsets are not useful for a major collection */
3150 global_remset_cache_clear ();
3152 card_table_clear ();
3156 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3157 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3158 optimize_pin_queue (0);
3161 * pin_queue now contains all candidate pointers, sorted and
3162 * uniqued. We must do two passes now to figure out which
3163 * objects are pinned.
3165 * The first is to find within the pin_queue the area for each
3166 * section. This requires that the pin_queue be sorted. We
3167 * also process the LOS objects and pinned chunks here.
3169 * The second, destructive, pass is to reduce the section
3170 * areas to pointers to the actually pinned objects.
3172 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3173 /* first pass for the sections */
3174 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3175 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3176 /* identify possible pointers to the insize of large objects */
3177 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3178 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3180 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3181 pin_object (bigobj->data);
3182 /* FIXME: only enqueue if object has references */
3183 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3185 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3186 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));
3189 /* second pass for the sections */
3190 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3191 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3192 old_next_pin_slot = next_pin_slot;
3195 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3196 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3197 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3199 major_collector.init_to_space ();
3201 workers_start_all_workers (1);
3203 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3204 report_registered_roots ();
3206 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3208 /* registered roots, this includes static fields */
3209 scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3210 scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3212 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3215 /* FIXME: This is the wrong place for this, because it does
3217 scan_thread_data (heap_start, heap_end, TRUE);
3219 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3222 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3224 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3225 report_finalizer_roots ();
3226 /* scan the list of objects ready for finalization */
3227 scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3228 scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3230 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3231 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3234 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3236 if (major_collector.is_parallel) {
3237 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3238 workers_distribute_gray_queue_sections ();
3242 workers_change_num_working (-1);
3245 if (major_collector.is_parallel)
3246 g_assert (gray_object_queue_is_empty (&gray_queue));
3248 /* all the objects in the heap */
3249 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3251 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3253 if (objects_pinned) {
3254 /*This is slow, but we just OOM'd*/
3255 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3256 evacuate_pin_staging_area ();
3257 optimize_pin_queue (0);
3258 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3262 reset_heap_boundaries ();
3263 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
3265 /* sweep the big objects list */
3267 for (bigobj = los_object_list; bigobj;) {
3268 if (object_is_pinned (bigobj->data)) {
3269 unpin_object (bigobj->data);
3270 mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3273 /* not referenced anywhere, so we can free it */
3275 prevbo->next = bigobj->next;
3277 los_object_list = bigobj->next;
3279 bigobj = bigobj->next;
3280 mono_sgen_los_free_object (to_free);
3284 bigobj = bigobj->next;
3288 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3290 mono_sgen_los_sweep ();
3293 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3295 major_collector.sweep ();
3298 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3300 /* walk the pin_queue, build up the fragment list of free memory, unmark
3301 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3304 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
3307 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3309 TV_GETTIME (all_btv);
3310 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3313 dump_heap ("major", num_major_gcs - 1, reason);
3315 /* prepare the pin queue for the next collection */
3317 if (fin_ready_list || critical_fin_list) {
3318 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3319 mono_gc_finalize_notify ();
3323 g_assert (gray_object_queue_is_empty (&gray_queue));
3325 try_calculate_minor_collection_allowance (TRUE);
3327 minor_collection_sections_alloced = 0;
3328 last_collection_los_memory_usage = los_memory_usage;
3330 major_collector.finish_major_collection ();
3332 check_scan_starts ();
3334 binary_protocol_flush_buffers (FALSE);
3336 //consistency_check ();
3340 major_collection (const char *reason)
3342 if (g_getenv ("MONO_GC_NO_MAJOR")) {
3343 collect_nursery (0);
3347 current_collection_generation = GENERATION_OLD;
3348 major_do_collection (reason);
3349 current_collection_generation = -1;
3353 sgen_collect_major_no_lock (const char *reason)
3355 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3357 major_collection (reason);
3359 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3363 * When deciding if it's better to collect or to expand, keep track
3364 * of how much garbage was reclaimed with the last collection: if it's too
3366 * This is called when we could not allocate a small object.
3368 static void __attribute__((noinline))
3369 minor_collect_or_expand_inner (size_t size)
3371 int do_minor_collection = 1;
3373 g_assert (nursery_section);
3374 if (do_minor_collection) {
3375 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3377 if (collect_nursery (size)) {
3378 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3379 major_collection ("minor overflow");
3380 /* keep events symmetric */
3381 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3383 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3385 /* this also sets the proper pointers for the next allocation */
3386 if (!search_fragment_for_size (size)) {
3388 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3389 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3390 for (i = 0; i < last_num_pinned; ++i) {
3391 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])));
3395 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3397 //report_internal_mem_usage ();
3401 * ######################################################################
3402 * ######## Memory allocation from the OS
3403 * ######################################################################
3404 * This section of code deals with getting memory from the OS and
3405 * allocating memory for GC-internal data structures.
3406 * Internal memory can be handled with a freelist for small objects.
3412 G_GNUC_UNUSED static void
3413 report_internal_mem_usage (void)
3415 printf ("Internal memory usage:\n");
3416 mono_sgen_report_internal_mem_usage ();
3417 printf ("Pinned memory usage:\n");
3418 major_collector.report_pinned_memory_usage ();
3422 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3423 * This must not require any lock.
3426 mono_sgen_alloc_os_memory (size_t size, int activate)
3429 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3431 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3432 size += pagesize - 1;
3433 size &= ~(pagesize - 1);
3434 ptr = mono_valloc (0, size, prot_flags);
3436 total_alloc += size;
3441 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3444 mono_sgen_free_os_memory (void *addr, size_t size)
3446 mono_vfree (addr, size);
3448 size += pagesize - 1;
3449 size &= ~(pagesize - 1);
3451 total_alloc -= size;
3455 * ######################################################################
3456 * ######## Object allocation
3457 * ######################################################################
3458 * This section of code deals with allocating memory for objects.
3459 * There are several ways:
3460 * *) allocate large objects
3461 * *) allocate normal objects
3462 * *) fast lock-free allocation
3463 * *) allocation of pinned objects
3467 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3469 /* remove from the list */
3471 prev->next = frag->next;
3473 nursery_fragments = frag->next;
3474 nursery_next = frag->fragment_start;
3475 nursery_frag_real_end = frag->fragment_end;
3477 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));
3478 frag->next = fragment_freelist;
3479 fragment_freelist = frag;
3482 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3483 * an object of size @size
3484 * Return FALSE if not found (which means we need a collection)
3487 search_fragment_for_size (size_t size)
3489 Fragment *frag, *prev;
3490 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3492 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3493 /* Clear the remaining space, pinning depends on this */
3494 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3498 for (frag = nursery_fragments; frag; frag = frag->next) {
3499 if (size <= (frag->fragment_end - frag->fragment_start)) {
3500 setup_fragment (frag, prev, size);
3509 * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3510 * This improves nursery usage.
3513 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3515 Fragment *frag, *prev, *min_prev;
3516 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));
3518 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3519 /* Clear the remaining space, pinning depends on this */
3520 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3523 min_prev = GINT_TO_POINTER (-1);
3526 for (frag = nursery_fragments; frag; frag = frag->next) {
3527 int frag_size = frag->fragment_end - frag->fragment_start;
3528 if (desired_size <= frag_size) {
3529 setup_fragment (frag, prev, desired_size);
3530 return desired_size;
3532 if (minimum_size <= frag_size)
3538 if (min_prev != GINT_TO_POINTER (-1)) {
3541 frag = min_prev->next;
3543 frag = nursery_fragments;
3545 frag_size = frag->fragment_end - frag->fragment_start;
3546 HEAVY_STAT (++stat_wasted_fragments_used);
3547 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3549 setup_fragment (frag, min_prev, minimum_size);
3557 alloc_degraded (MonoVTable *vtable, size_t size)
3559 if (need_major_collection (0)) {
3560 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3562 major_collection ("degraded overflow");
3564 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3567 return major_collector.alloc_degraded (vtable, size);
3571 * Provide a variant that takes just the vtable for small fixed-size objects.
3572 * The aligned size is already computed and stored in vt->gc_descr.
3573 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3574 * processing. We can keep track of where objects start, for example,
3575 * so when we scan the thread stacks for pinned objects, we can start
3576 * a search for the pinned object in SCAN_START_SIZE chunks.
3579 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3581 /* FIXME: handle OOM */
3586 HEAVY_STAT (++stat_objects_alloced);
3587 if (size <= MAX_SMALL_OBJ_SIZE)
3588 HEAVY_STAT (stat_bytes_alloced += size);
3590 HEAVY_STAT (stat_bytes_alloced_los += size);
3592 size = ALIGN_UP (size);
3594 g_assert (vtable->gc_descr);
3596 if (G_UNLIKELY (collect_before_allocs)) {
3597 if (nursery_section) {
3598 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3600 collect_nursery (0);
3602 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3603 if (!degraded_mode && !search_fragment_for_size (size)) {
3605 g_assert_not_reached ();
3611 * We must already have the lock here instead of after the
3612 * fast path because we might be interrupted in the fast path
3613 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3614 * and we'll end up allocating an object in a fragment which
3615 * no longer belongs to us.
3617 * The managed allocator does not do this, but it's treated
3618 * specially by the world-stopping code.
3621 if (size > MAX_SMALL_OBJ_SIZE) {
3622 p = mono_sgen_los_alloc_large_inner (vtable, size);
3624 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3626 p = (void**)TLAB_NEXT;
3627 /* FIXME: handle overflow */
3628 new_next = (char*)p + size;
3629 TLAB_NEXT = new_next;
3631 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3635 * FIXME: We might need a memory barrier here so the change to tlab_next is
3636 * visible before the vtable store.
3639 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3640 binary_protocol_alloc (p , vtable, size);
3641 g_assert (*p == NULL);
3644 g_assert (TLAB_NEXT == new_next);
3651 /* there are two cases: the object is too big or we run out of space in the TLAB */
3652 /* we also reach here when the thread does its first allocation after a minor
3653 * collection, since the tlab_ variables are initialized to NULL.
3654 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3655 * objects that need finalizers can have the high bit set in their size
3656 * so the above check fails and we can readily add the object to the queue.
3657 * This avoids taking again the GC lock when registering, but this is moot when
3658 * doing thread-local allocation, so it may not be a good idea.
3660 g_assert (TLAB_NEXT == new_next);
3661 if (TLAB_NEXT >= TLAB_REAL_END) {
3663 * Run out of space in the TLAB. When this happens, some amount of space
3664 * remains in the TLAB, but not enough to satisfy the current allocation
3665 * request. Currently, we retire the TLAB in all cases, later we could
3666 * keep it if the remaining space is above a treshold, and satisfy the
3667 * allocation directly from the nursery.
3670 /* when running in degraded mode, we continue allocing that way
3671 * for a while, to decrease the number of useless nursery collections.
3673 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3674 p = alloc_degraded (vtable, size);
3675 binary_protocol_alloc_degraded (p, vtable, size);
3679 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3680 if (size > tlab_size) {
3681 /* Allocate directly from the nursery */
3682 if (nursery_next + size >= nursery_frag_real_end) {
3683 if (!search_fragment_for_size (size)) {
3684 minor_collect_or_expand_inner (size);
3685 if (degraded_mode) {
3686 p = alloc_degraded (vtable, size);
3687 binary_protocol_alloc_degraded (p, vtable, size);
3693 p = (void*)nursery_next;
3694 nursery_next += size;
3695 if (nursery_next > nursery_frag_real_end) {
3700 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3701 memset (p, 0, size);
3704 int alloc_size = tlab_size;
3705 int available_in_nursery = nursery_frag_real_end - nursery_next;
3707 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3709 if (alloc_size >= available_in_nursery) {
3710 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3711 alloc_size = available_in_nursery;
3713 alloc_size = search_fragment_for_size_range (tlab_size, size);
3715 alloc_size = tlab_size;
3716 minor_collect_or_expand_inner (tlab_size);
3717 if (degraded_mode) {
3718 p = alloc_degraded (vtable, size);
3719 binary_protocol_alloc_degraded (p, vtable, size);
3726 /* Allocate a new TLAB from the current nursery fragment */
3727 TLAB_START = nursery_next;
3728 nursery_next += alloc_size;
3729 TLAB_NEXT = TLAB_START;
3730 TLAB_REAL_END = TLAB_START + alloc_size;
3731 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3733 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3734 memset (TLAB_START, 0, alloc_size);
3737 /* Allocate from the TLAB */
3738 p = (void*)TLAB_NEXT;
3740 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3742 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3745 /* Reached tlab_temp_end */
3747 /* record the scan start so we can find pinned objects more easily */
3748 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3749 /* we just bump tlab_temp_end as well */
3750 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3751 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3756 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3757 binary_protocol_alloc (p, vtable, size);
3765 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3771 size = ALIGN_UP (size);
3773 g_assert (vtable->gc_descr);
3774 if (size <= MAX_SMALL_OBJ_SIZE) {
3775 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3777 p = (void**)TLAB_NEXT;
3778 /* FIXME: handle overflow */
3779 new_next = (char*)p + size;
3780 TLAB_NEXT = new_next;
3782 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3786 * FIXME: We might need a memory barrier here so the change to tlab_next is
3787 * visible before the vtable store.
3790 HEAVY_STAT (++stat_objects_alloced);
3791 HEAVY_STAT (stat_bytes_alloced += size);
3793 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3794 binary_protocol_alloc (p, vtable, size);
3795 g_assert (*p == NULL);
3798 g_assert (TLAB_NEXT == new_next);
3807 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
3810 #ifndef DISABLE_CRITICAL_REGION
3812 ENTER_CRITICAL_REGION;
3813 res = mono_gc_try_alloc_obj_nolock (vtable, size);
3815 EXIT_CRITICAL_REGION;
3818 EXIT_CRITICAL_REGION;
3821 res = mono_gc_alloc_obj_nolock (vtable, size);
3823 if (G_UNLIKELY (!res))
3824 return mono_gc_out_of_memory (size);
3829 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
3832 #ifndef DISABLE_CRITICAL_REGION
3834 ENTER_CRITICAL_REGION;
3835 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
3837 arr->max_length = max_length;
3838 EXIT_CRITICAL_REGION;
3841 EXIT_CRITICAL_REGION;
3846 arr = mono_gc_alloc_obj_nolock (vtable, size);
3847 if (G_UNLIKELY (!arr)) {
3849 return mono_gc_out_of_memory (size);
3852 arr->max_length = max_length;
3860 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
3863 MonoArrayBounds *bounds;
3867 arr = mono_gc_alloc_obj_nolock (vtable, size);
3868 if (G_UNLIKELY (!arr)) {
3870 return mono_gc_out_of_memory (size);
3873 arr->max_length = max_length;
3875 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
3876 arr->bounds = bounds;
3884 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
3887 #ifndef DISABLE_CRITICAL_REGION
3889 ENTER_CRITICAL_REGION;
3890 str = mono_gc_try_alloc_obj_nolock (vtable, size);
3893 EXIT_CRITICAL_REGION;
3896 EXIT_CRITICAL_REGION;
3901 str = mono_gc_alloc_obj_nolock (vtable, size);
3902 if (G_UNLIKELY (!str)) {
3904 return mono_gc_out_of_memory (size);
3915 * To be used for interned strings and possibly MonoThread, reflection handles.
3916 * We may want to explicitly free these objects.
3919 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
3922 size = ALIGN_UP (size);
3925 if (size > MAX_SMALL_OBJ_SIZE) {
3926 /* large objects are always pinned anyway */
3927 p = mono_sgen_los_alloc_large_inner (vtable, size);
3929 DEBUG (9, g_assert (vtable->klass->inited));
3930 p = major_collector.alloc_small_pinned_obj (size, vtable->klass->has_references);
3933 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3934 binary_protocol_alloc_pinned (p, vtable, size);
3942 mono_gc_alloc_mature (MonoVTable *vtable)
3945 size_t size = ALIGN_UP (vtable->klass->instance_size);
3947 res = alloc_degraded (vtable, size);
3954 * ######################################################################
3955 * ######## Finalization support
3956 * ######################################################################
3960 * this is valid for the nursery: if the object has been forwarded it means it's
3961 * still refrenced from a root. If it is pinned it's still alive as well.
3962 * Return TRUE if @obj is ready to be finalized.
3964 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3967 is_critical_finalizer (FinalizeEntry *entry)
3972 if (!mono_defaults.critical_finalizer_object)
3975 obj = entry->object;
3976 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3978 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
3982 queue_finalization_entry (FinalizeEntry *entry) {
3983 if (is_critical_finalizer (entry)) {
3984 entry->next = critical_fin_list;
3985 critical_fin_list = entry;
3987 entry->next = fin_ready_list;
3988 fin_ready_list = entry;
3992 /* LOCKING: requires that the GC lock is held */
3994 rehash_fin_table (FinalizeEntryHashTable *hash_table)
3996 FinalizeEntry **finalizable_hash = hash_table->table;
3997 mword finalizable_hash_size = hash_table->size;
4000 FinalizeEntry **new_hash;
4001 FinalizeEntry *entry, *next;
4002 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4004 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4005 for (i = 0; i < finalizable_hash_size; ++i) {
4006 for (entry = finalizable_hash [i]; entry; entry = next) {
4007 hash = mono_object_hash (entry->object) % new_size;
4009 entry->next = new_hash [hash];
4010 new_hash [hash] = entry;
4013 mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4014 hash_table->table = new_hash;
4015 hash_table->size = new_size;
4018 /* LOCKING: requires that the GC lock is held */
4020 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4022 if (hash_table->num_registered >= hash_table->size * 2)
4023 rehash_fin_table (hash_table);
4026 /* LOCKING: requires that the GC lock is held */
4028 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
4030 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4031 FinalizeEntry *entry, *prev;
4033 FinalizeEntry **finalizable_hash = hash_table->table;
4034 mword finalizable_hash_size = hash_table->size;
4038 for (i = 0; i < finalizable_hash_size; ++i) {
4040 for (entry = finalizable_hash [i]; entry;) {
4041 if ((char*)entry->object >= start && (char*)entry->object < end && !major_collector.is_object_live (entry->object)) {
4042 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4043 char *copy = entry->object;
4044 copy_func ((void**)©, queue);
4047 FinalizeEntry *next;
4048 /* remove and put in fin_ready_list */
4050 prev->next = entry->next;
4052 finalizable_hash [i] = entry->next;
4054 num_ready_finalizers++;
4055 hash_table->num_registered--;
4056 queue_finalization_entry (entry);
4057 /* Make it survive */
4058 from = entry->object;
4059 entry->object = copy;
4060 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));
4064 char *from = entry->object;
4065 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4066 FinalizeEntry *next = entry->next;
4067 unsigned int major_hash;
4068 /* remove from the list */
4070 prev->next = entry->next;
4072 finalizable_hash [i] = entry->next;
4073 hash_table->num_registered--;
4075 entry->object = copy;
4077 /* insert it into the major hash */
4078 rehash_fin_table_if_necessary (&major_finalizable_hash);
4079 major_hash = mono_object_hash ((MonoObject*) copy) %
4080 major_finalizable_hash.size;
4081 entry->next = major_finalizable_hash.table [major_hash];
4082 major_finalizable_hash.table [major_hash] = entry;
4083 major_finalizable_hash.num_registered++;
4085 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4090 /* update pointer */
4091 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4092 entry->object = copy;
4097 entry = entry->next;
4103 object_is_reachable (char *object, char *start, char *end)
4105 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4106 if (object < start || object >= end)
4108 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4111 /* LOCKING: requires that the GC lock is held */
4113 null_ephemerons_for_domain (MonoDomain *domain)
4115 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4118 MonoObject *object = (MonoObject*)current->array;
4120 if (object && !object->vtable) {
4121 EphemeronLinkNode *tmp = current;
4124 prev->next = current->next;
4126 ephemeron_list = current->next;
4128 current = current->next;
4129 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4132 current = current->next;
4137 /* LOCKING: requires that the GC lock is held */
4139 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4141 int was_in_nursery, was_promoted;
4142 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4144 Ephemeron *cur, *array_end;
4148 char *object = current->array;
4150 if (!object_is_reachable (object, start, end)) {
4151 EphemeronLinkNode *tmp = current;
4153 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4156 prev->next = current->next;
4158 ephemeron_list = current->next;
4160 current = current->next;
4161 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4166 was_in_nursery = ptr_in_nursery (object);
4167 copy_func ((void**)&object, queue);
4168 current->array = object;
4170 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4171 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4173 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4175 array = (MonoArray*)object;
4176 cur = mono_array_addr (array, Ephemeron, 0);
4177 array_end = cur + mono_array_length_fast (array);
4178 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4180 for (; cur < array_end; ++cur) {
4181 char *key = (char*)cur->key;
4183 if (!key || key == tombstone)
4186 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4187 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4188 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4190 if (!object_is_reachable (key, start, end)) {
4191 cur->key = tombstone;
4197 if (ptr_in_nursery (key)) {/*key was not promoted*/
4198 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4199 mono_sgen_add_to_global_remset (&cur->key);
4201 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4202 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4203 mono_sgen_add_to_global_remset (&cur->value);
4208 current = current->next;
4212 /* LOCKING: requires that the GC lock is held */
4214 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4216 int nothing_marked = 1;
4217 EphemeronLinkNode *current = ephemeron_list;
4219 Ephemeron *cur, *array_end;
4222 for (current = ephemeron_list; current; current = current->next) {
4223 char *object = current->array;
4224 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4226 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4227 if (object < start || object >= end)
4230 /*It has to be alive*/
4231 if (!object_is_reachable (object, start, end)) {
4232 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4236 copy_func ((void**)&object, queue);
4238 array = (MonoArray*)object;
4239 cur = mono_array_addr (array, Ephemeron, 0);
4240 array_end = cur + mono_array_length_fast (array);
4241 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4243 for (; cur < array_end; ++cur) {
4244 char *key = cur->key;
4246 if (!key || key == tombstone)
4249 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4250 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4251 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4253 if (object_is_reachable (key, start, end)) {
4254 char *value = cur->value;
4256 copy_func ((void**)&cur->key, queue);
4258 if (!object_is_reachable (value, start, end))
4260 copy_func ((void**)&cur->value, queue);
4266 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4267 return nothing_marked;
4270 /* LOCKING: requires that the GC lock is held */
4272 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
4274 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4275 DisappearingLink **disappearing_link_hash = hash->table;
4276 int disappearing_link_hash_size = hash->size;
4277 DisappearingLink *entry, *prev;
4279 if (!hash->num_links)
4281 for (i = 0; i < disappearing_link_hash_size; ++i) {
4283 for (entry = disappearing_link_hash [i]; entry;) {
4284 char *object = DISLINK_OBJECT (entry);
4285 if (object >= start && object < end && !major_collector.is_object_live (object)) {
4286 gboolean track = DISLINK_TRACK (entry);
4287 if (!track && object_is_fin_ready (object)) {
4288 void **p = entry->link;
4289 DisappearingLink *old;
4291 /* remove from list */
4293 prev->next = entry->next;
4295 disappearing_link_hash [i] = entry->next;
4296 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4298 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4303 char *copy = object;
4304 copy_func ((void**)©, queue);
4306 /* Update pointer if it's moved. If the object
4307 * has been moved out of the nursery, we need to
4308 * remove the link from the minor hash table to
4311 * FIXME: what if an object is moved earlier?
4314 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4315 void **link = entry->link;
4316 DisappearingLink *old;
4317 /* remove from list */
4319 prev->next = entry->next;
4321 disappearing_link_hash [i] = entry->next;
4323 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4327 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4328 track, GENERATION_OLD);
4330 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4334 /* We set the track resurrection bit to
4335 * FALSE if the object is to be finalized
4336 * so that the object can be collected in
4337 * the next cycle (i.e. after it was
4340 *entry->link = HIDE_POINTER (copy,
4341 object_is_fin_ready (object) ? FALSE : track);
4342 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4347 entry = entry->next;
4352 /* LOCKING: requires that the GC lock is held */
4354 null_links_for_domain (MonoDomain *domain, int generation)
4356 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4357 DisappearingLink **disappearing_link_hash = hash->table;
4358 int disappearing_link_hash_size = hash->size;
4359 DisappearingLink *entry, *prev;
4361 for (i = 0; i < disappearing_link_hash_size; ++i) {
4363 for (entry = disappearing_link_hash [i]; entry; ) {
4364 char *object = DISLINK_OBJECT (entry);
4365 if (object && !((MonoObject*)object)->vtable) {
4366 DisappearingLink *next = entry->next;
4371 disappearing_link_hash [i] = next;
4373 if (*(entry->link)) {
4374 *(entry->link) = NULL;
4375 g_warning ("Disappearing link %p not freed", entry->link);
4377 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4384 entry = entry->next;
4389 /* LOCKING: requires that the GC lock is held */
4391 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4392 FinalizeEntryHashTable *hash_table)
4394 FinalizeEntry **finalizable_hash = hash_table->table;
4395 mword finalizable_hash_size = hash_table->size;
4396 FinalizeEntry *entry, *prev;
4399 if (no_finalize || !out_size || !out_array)
4402 for (i = 0; i < finalizable_hash_size; ++i) {
4404 for (entry = finalizable_hash [i]; entry;) {
4405 if (mono_object_domain (entry->object) == domain) {
4406 FinalizeEntry *next;
4407 /* remove and put in out_array */
4409 prev->next = entry->next;
4411 finalizable_hash [i] = entry->next;
4413 hash_table->num_registered--;
4414 out_array [count ++] = entry->object;
4415 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));
4417 if (count == out_size)
4422 entry = entry->next;
4429 * mono_gc_finalizers_for_domain:
4430 * @domain: the unloading appdomain
4431 * @out_array: output array
4432 * @out_size: size of output array
4434 * Store inside @out_array up to @out_size objects that belong to the unloading
4435 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4436 * until it returns 0.
4437 * The items are removed from the finalizer data structure, so the caller is supposed
4439 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4442 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4447 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4448 if (result < out_size) {
4449 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4450 &major_finalizable_hash);
4458 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4460 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4461 FinalizeEntry **finalizable_hash;
4462 mword finalizable_hash_size;
4463 FinalizeEntry *entry, *prev;
4467 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4468 hash = mono_object_hash (obj);
4470 rehash_fin_table_if_necessary (hash_table);
4471 finalizable_hash = hash_table->table;
4472 finalizable_hash_size = hash_table->size;
4473 hash %= finalizable_hash_size;
4475 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4476 if (entry->object == obj) {
4478 /* remove from the list */
4480 prev->next = entry->next;
4482 finalizable_hash [hash] = entry->next;
4483 hash_table->num_registered--;
4484 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));
4485 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4493 /* request to deregister, but already out of the list */
4497 entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4498 entry->object = obj;
4499 entry->next = finalizable_hash [hash];
4500 finalizable_hash [hash] = entry;
4501 hash_table->num_registered++;
4502 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)));
4507 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4509 if (ptr_in_nursery (obj))
4510 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4512 register_for_finalization (obj, user_data, GENERATION_OLD);
4516 rehash_dislink (DisappearingLinkHashTable *hash_table)
4518 DisappearingLink **disappearing_link_hash = hash_table->table;
4519 int disappearing_link_hash_size = hash_table->size;
4522 DisappearingLink **new_hash;
4523 DisappearingLink *entry, *next;
4524 int new_size = g_spaced_primes_closest (hash_table->num_links);
4526 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4527 for (i = 0; i < disappearing_link_hash_size; ++i) {
4528 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4529 hash = mono_aligned_addr_hash (entry->link) % new_size;
4531 entry->next = new_hash [hash];
4532 new_hash [hash] = entry;
4535 mono_sgen_free_internal_dynamic (disappearing_link_hash,
4536 disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4537 hash_table->table = new_hash;
4538 hash_table->size = new_size;
4541 /* LOCKING: assumes the GC lock is held */
4543 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4545 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4546 DisappearingLink *entry, *prev;
4548 DisappearingLink **disappearing_link_hash = hash_table->table;
4549 int disappearing_link_hash_size = hash_table->size;
4551 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4552 rehash_dislink (hash_table);
4553 disappearing_link_hash = hash_table->table;
4554 disappearing_link_hash_size = hash_table->size;
4556 /* FIXME: add check that link is not in the heap */
4557 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4558 entry = disappearing_link_hash [hash];
4560 for (; entry; entry = entry->next) {
4561 /* link already added */
4562 if (link == entry->link) {
4563 /* NULL obj means remove */
4566 prev->next = entry->next;
4568 disappearing_link_hash [hash] = entry->next;
4569 hash_table->num_links--;
4570 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4571 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4574 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4582 entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4583 *link = HIDE_POINTER (obj, track);
4585 entry->next = disappearing_link_hash [hash];
4586 disappearing_link_hash [hash] = entry;
4587 hash_table->num_links++;
4588 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)));
4591 /* LOCKING: assumes the GC lock is held */
4593 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4595 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4596 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4598 if (ptr_in_nursery (obj))
4599 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4601 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4606 mono_gc_invoke_finalizers (void)
4608 FinalizeEntry *entry = NULL;
4609 gboolean entry_is_critical = FALSE;
4612 /* FIXME: batch to reduce lock contention */
4613 while (fin_ready_list || critical_fin_list) {
4617 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4619 /* We have finalized entry in the last
4620 interation, now we need to remove it from
4623 *list = entry->next;
4625 FinalizeEntry *e = *list;
4626 while (e->next != entry)
4628 e->next = entry->next;
4630 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4634 /* Now look for the first non-null entry. */
4635 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4638 entry_is_critical = FALSE;
4640 entry_is_critical = TRUE;
4641 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4646 g_assert (entry->object);
4647 num_ready_finalizers--;
4648 obj = entry->object;
4649 entry->object = NULL;
4650 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4658 g_assert (entry->object == NULL);
4660 /* the object is on the stack so it is pinned */
4661 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4662 mono_gc_run_finalize (obj, NULL);
4669 mono_gc_pending_finalizers (void)
4671 return fin_ready_list || critical_fin_list;
4674 /* Negative value to remove */
4676 mono_gc_add_memory_pressure (gint64 value)
4678 /* FIXME: Use interlocked functions */
4680 memory_pressure += value;
4685 mono_sgen_register_major_sections_alloced (int num_sections)
4687 minor_collection_sections_alloced += num_sections;
4691 mono_sgen_get_minor_collection_allowance (void)
4693 return minor_collection_allowance;
4697 * ######################################################################
4698 * ######## registered roots support
4699 * ######################################################################
4703 rehash_roots (gboolean pinned)
4707 RootRecord **new_hash;
4708 RootRecord *entry, *next;
4711 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4712 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4713 for (i = 0; i < roots_hash_size [pinned]; ++i) {
4714 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4715 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4717 entry->next = new_hash [hash];
4718 new_hash [hash] = entry;
4721 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4722 roots_hash [pinned] = new_hash;
4723 roots_hash_size [pinned] = new_size;
4727 find_root (int root_type, char *start, guint32 addr_hash)
4729 RootRecord *new_root;
4731 guint32 hash = addr_hash % roots_hash_size [root_type];
4732 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4733 /* we allow changing the size and the descriptor (for thread statics etc) */
4734 if (new_root->start_root == start) {
4743 * We do not coalesce roots.
4746 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4748 RootRecord *new_root;
4749 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
4752 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4753 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
4756 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4757 new_root = find_root (i, start, addr_hash);
4758 /* we allow changing the size and the descriptor (for thread statics etc) */
4760 size_t old_size = new_root->end_root - new_root->start_root;
4761 new_root->end_root = new_root->start_root + size;
4762 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
4763 ((new_root->root_desc == 0) && (descr == NULL)));
4764 new_root->root_desc = (mword)descr;
4766 roots_size -= old_size;
4771 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
4773 new_root->start_root = start;
4774 new_root->end_root = new_root->start_root + size;
4775 new_root->root_desc = (mword)descr;
4777 hash = addr_hash % roots_hash_size [root_type];
4778 num_roots_entries [root_type]++;
4779 new_root->next = roots_hash [root_type] [hash];
4780 roots_hash [root_type][hash] = new_root;
4781 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));
4791 mono_gc_register_root (char *start, size_t size, void *descr)
4793 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4797 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4799 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4803 mono_gc_deregister_root (char* addr)
4805 RootRecord *tmp, *prev;
4806 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
4810 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4811 hash = addr_hash % roots_hash_size [root_type];
4812 tmp = roots_hash [root_type][hash];
4815 if (tmp->start_root == (char*)addr) {
4817 prev->next = tmp->next;
4819 roots_hash [root_type][hash] = tmp->next;
4820 roots_size -= (tmp->end_root - tmp->start_root);
4821 num_roots_entries [root_type]--;
4822 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
4823 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
4834 * ######################################################################
4835 * ######## Thread handling (stop/start code)
4836 * ######################################################################
4839 /* FIXME: handle large/small config */
4840 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
4842 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
4844 #if USE_SIGNAL_BASED_START_STOP_WORLD
4846 static MonoSemType suspend_ack_semaphore;
4847 static MonoSemType *suspend_ack_semaphore_ptr;
4848 static unsigned int global_stop_count = 0;
4850 static sigset_t suspend_signal_mask;
4851 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4853 /* LOCKING: assumes the GC lock is held */
4855 mono_sgen_get_thread_table (void)
4857 return thread_table;
4861 mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
4863 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
4864 SgenThreadInfo *info;
4866 info = thread_table [hash];
4867 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
4874 update_current_thread_stack (void *start)
4876 void *ptr = cur_thread_regs;
4877 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
4879 info->stack_start = align_pointer (&ptr);
4880 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
4881 ARCH_STORE_REGS (ptr);
4882 info->stopped_regs = ptr;
4883 if (gc_callbacks.thread_suspend_func)
4884 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
4888 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
4889 * have cross-domain checks in the write barrier.
4891 //#define XDOMAIN_CHECKS_IN_WBARRIER
4893 #ifndef SGEN_BINARY_PROTOCOL
4894 #ifndef HEAVY_STATISTICS
4895 #define MANAGED_ALLOCATION
4896 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
4897 #define MANAGED_WBARRIER
4903 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
4906 mono_sgen_wait_for_suspend_ack (int count)
4910 for (i = 0; i < count; ++i) {
4911 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
4912 if (errno != EINTR) {
4913 g_error ("sem_wait ()");
4920 restart_threads_until_none_in_managed_allocator (void)
4922 SgenThreadInfo *info;
4923 int i, result, num_threads_died = 0;
4924 int sleep_duration = -1;
4927 int restart_count = 0, restarted_count = 0;
4928 /* restart all threads that stopped in the
4930 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4931 for (info = thread_table [i]; info; info = info->next) {
4934 if (!info->stack_start || info->in_critical_region ||
4935 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
4936 binary_protocol_thread_restart ((gpointer)info->id);
4937 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4938 result = thread_resume (pthread_mach_thread_np (info->id));
4940 result = pthread_kill (info->id, restart_signal_num);
4948 /* we set the stopped_ip to
4949 NULL for threads which
4950 we're not restarting so
4951 that we can easily identify
4953 info->stopped_ip = NULL;
4954 info->stopped_domain = NULL;
4958 /* if no threads were restarted, we're done */
4959 if (restart_count == 0)
4962 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4963 /* mach thread_resume is synchronous so we dont need to wait for them */
4965 /* wait for the threads to signal their restart */
4966 mono_sgen_wait_for_suspend_ack (restart_count);
4969 if (sleep_duration < 0) {
4973 g_usleep (sleep_duration);
4974 sleep_duration += 10;
4977 /* stop them again */
4978 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4979 for (info = thread_table [i]; info; info = info->next) {
4980 if (info->skip || info->stopped_ip == NULL)
4982 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4983 result = thread_suspend (pthread_mach_thread_np (info->id));
4985 result = pthread_kill (info->id, suspend_signal_num);
4994 /* some threads might have died */
4995 num_threads_died += restart_count - restarted_count;
4996 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4997 /* mach thread_resume is synchronous so we dont need to wait for them */
4999 /* wait for the threads to signal their suspension
5001 mono_sgen_wait_for_suspend_ack (restart_count);
5005 return num_threads_died;
5008 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5010 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5012 SgenThreadInfo *info;
5015 int old_errno = errno;
5016 gpointer regs [ARCH_NUM_REGS];
5017 gpointer stack_start;
5019 id = pthread_self ();
5020 info = mono_sgen_thread_info_lookup (id);
5021 info->stopped_domain = mono_domain_get ();
5022 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5023 stop_count = global_stop_count;
5024 /* duplicate signal */
5025 if (0 && info->stop_count == stop_count) {
5029 #ifdef HAVE_KW_THREAD
5030 /* update the remset info in the thread data structure */
5031 info->remset = remembered_set;
5033 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5034 /* If stack_start is not within the limits, then don't set it
5035 in info and we will be restarted. */
5036 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5037 info->stack_start = stack_start;
5039 ARCH_COPY_SIGCTX_REGS (regs, context);
5040 info->stopped_regs = regs;
5042 g_assert (!info->stack_start);
5045 /* Notify the JIT */
5046 if (gc_callbacks.thread_suspend_func)
5047 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5049 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5050 /* notify the waiting thread */
5051 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5052 info->stop_count = stop_count;
5054 /* wait until we receive the restart signal */
5057 sigsuspend (&suspend_signal_mask);
5058 } while (info->signal != restart_signal_num);
5060 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5061 /* notify the waiting thread */
5062 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5068 restart_handler (int sig)
5070 SgenThreadInfo *info;
5071 int old_errno = errno;
5073 info = mono_sgen_thread_info_lookup (pthread_self ());
5074 info->signal = restart_signal_num;
5075 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5081 acquire_gc_locks (void)
5087 release_gc_locks (void)
5089 UNLOCK_INTERRUPTION;
5092 static TV_DECLARE (stop_world_time);
5093 static unsigned long max_pause_usec = 0;
5095 /* LOCKING: assumes the GC lock is held */
5097 stop_world (int generation)
5101 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
5102 acquire_gc_locks ();
5104 update_current_thread_stack (&count);
5106 global_stop_count++;
5107 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 ()));
5108 TV_GETTIME (stop_world_time);
5109 count = mono_sgen_thread_handshake (suspend_signal_num);
5110 count -= restart_threads_until_none_in_managed_allocator ();
5111 g_assert (count >= 0);
5112 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5113 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
5117 /* LOCKING: assumes the GC lock is held */
5119 restart_world (int generation)
5122 SgenThreadInfo *info;
5123 TV_DECLARE (end_sw);
5126 /* notify the profiler of the leftovers */
5127 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5128 if (moved_objects_idx) {
5129 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5130 moved_objects_idx = 0;
5133 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
5134 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5135 for (info = thread_table [i]; info; info = info->next) {
5136 info->stack_start = NULL;
5137 info->stopped_regs = NULL;
5141 release_gc_locks ();
5143 count = mono_sgen_thread_handshake (restart_signal_num);
5144 TV_GETTIME (end_sw);
5145 usec = TV_ELAPSED (stop_world_time, end_sw);
5146 max_pause_usec = MAX (usec, max_pause_usec);
5147 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5148 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
5152 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5155 mono_sgen_get_current_collection_generation (void)
5157 return current_collection_generation;
5161 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5163 gc_callbacks = *callbacks;
5167 mono_gc_get_gc_callbacks ()
5169 return &gc_callbacks;
5172 /* Variables holding start/end nursery so it won't have to be passed at every call */
5173 static void *scan_area_arg_start, *scan_area_arg_end;
5176 mono_gc_conservatively_scan_area (void *start, void *end)
5178 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5182 mono_gc_scan_object (void *obj)
5184 g_assert_not_reached ();
5185 if (current_collection_generation == GENERATION_NURSERY)
5186 major_collector.copy_object (&obj, &gray_queue);
5188 major_collector.copy_or_mark_object (&obj, &gray_queue);
5193 * Mark from thread stacks and registers.
5196 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5199 SgenThreadInfo *info;
5201 scan_area_arg_start = start_nursery;
5202 scan_area_arg_end = end_nursery;
5204 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5205 for (info = thread_table [i]; info; info = info->next) {
5207 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));
5210 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));
5211 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
5212 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5214 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5217 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5218 start_nursery, end_nursery, PIN_TYPE_STACK);
5224 find_pinning_ref_from_thread (char *obj, size_t size)
5227 SgenThreadInfo *info;
5228 char *endobj = obj + size;
5230 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5231 for (info = thread_table [i]; info; info = info->next) {
5232 char **start = (char**)info->stack_start;
5235 while (start < (char**)info->stack_end) {
5236 if (*start >= obj && *start < endobj) {
5237 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));
5242 /* FIXME: check info->stopped_regs */
5248 ptr_on_stack (void *ptr)
5250 gpointer stack_start = &stack_start;
5251 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5253 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5259 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
5266 HEAVY_STAT (++stat_global_remsets_processed);
5268 HEAVY_STAT (++stat_local_remsets_processed);
5270 /* FIXME: exclude stack locations */
5271 switch ((*p) & REMSET_TYPE_MASK) {
5272 case REMSET_LOCATION:
5274 //__builtin_prefetch (ptr);
5275 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5276 gpointer old = *ptr;
5277 major_collector.copy_object (ptr, queue);
5278 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5280 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5281 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5283 * If the object is pinned, each reference to it from nonpinned objects
5284 * becomes part of the global remset, which can grow very large.
5286 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5287 mono_sgen_add_to_global_remset (ptr);
5290 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5294 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5295 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5298 while (count-- > 0) {
5299 major_collector.copy_object (ptr, queue);
5300 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5301 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5302 mono_sgen_add_to_global_remset (ptr);
5307 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5308 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5310 major_collector.minor_scan_object ((char*)ptr, queue);
5312 case REMSET_VTYPE: {
5313 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5314 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5319 ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
5323 g_assert_not_reached ();
5328 #ifdef HEAVY_STATISTICS
5330 collect_store_remsets (RememberedSet *remset, mword *bumper)
5332 mword *p = remset->data;
5337 while (p < remset->store_next) {
5338 switch ((*p) & REMSET_TYPE_MASK) {
5339 case REMSET_LOCATION:
5342 ++stat_saved_remsets_1;
5344 if (*p == last1 || *p == last2) {
5345 ++stat_saved_remsets_2;
5362 g_assert_not_reached ();
5372 RememberedSet *remset;
5374 SgenThreadInfo *info;
5376 mword *addresses, *bumper, *p, *r;
5378 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5379 for (info = thread_table [i]; info; info = info->next) {
5380 for (remset = info->remset; remset; remset = remset->next)
5381 size += remset->store_next - remset->data;
5384 for (remset = freed_thread_remsets; remset; remset = remset->next)
5385 size += remset->store_next - remset->data;
5386 for (remset = global_remset; remset; remset = remset->next)
5387 size += remset->store_next - remset->data;
5389 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5391 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5392 for (info = thread_table [i]; info; info = info->next) {
5393 for (remset = info->remset; remset; remset = remset->next)
5394 bumper = collect_store_remsets (remset, bumper);
5397 for (remset = global_remset; remset; remset = remset->next)
5398 bumper = collect_store_remsets (remset, bumper);
5399 for (remset = freed_thread_remsets; remset; remset = remset->next)
5400 bumper = collect_store_remsets (remset, bumper);
5402 g_assert (bumper <= addresses + size);
5404 stat_store_remsets += bumper - addresses;
5406 sort_addresses ((void**)addresses, bumper - addresses);
5409 while (r < bumper) {
5415 stat_store_remsets_unique += p - addresses;
5417 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5422 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5424 *info->store_remset_buffer_index_addr = 0;
5425 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5429 remset_byte_size (RememberedSet *remset)
5431 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5435 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5438 SgenThreadInfo *info;
5439 RememberedSet *remset;
5440 GenericStoreRememberedSet *store_remset;
5441 mword *p, *next_p, *store_pos;
5443 #ifdef HEAVY_STATISTICS
5447 /* the global one */
5448 for (remset = global_remset; remset; remset = remset->next) {
5449 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));
5450 store_pos = remset->data;
5451 for (p = remset->data; p < remset->store_next; p = next_p) {
5452 void **ptr = (void**)p [0];
5454 /*Ignore previously processed remset.*/
5455 if (!global_remset_location_was_not_added (ptr)) {
5460 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5463 * Clear global remsets of locations which no longer point to the
5464 * nursery. Otherwise, they could grow indefinitely between major
5467 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5469 if (ptr_in_nursery (*ptr)) {
5470 *store_pos ++ = p [0];
5471 HEAVY_STAT (++stat_global_remsets_readded);
5475 /* Truncate the remset */
5476 remset->store_next = store_pos;
5479 /* the generic store ones */
5480 store_remset = generic_store_remsets;
5481 while (store_remset) {
5482 GenericStoreRememberedSet *next = store_remset->next;
5484 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5485 gpointer addr = store_remset->data [i];
5487 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5490 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5492 store_remset = next;
5494 generic_store_remsets = NULL;
5496 /* the per-thread ones */
5497 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5498 for (info = thread_table [i]; info; info = info->next) {
5499 RememberedSet *next;
5501 for (remset = info->remset; remset; remset = next) {
5502 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));
5503 for (p = remset->data; p < remset->store_next;)
5504 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5505 remset->store_next = remset->data;
5506 next = remset->next;
5507 remset->next = NULL;
5508 if (remset != info->remset) {
5509 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5510 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5513 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5514 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5515 clear_thread_store_remset_buffer (info);
5519 /* the freed thread ones */
5520 while (freed_thread_remsets) {
5521 RememberedSet *next;
5522 remset = freed_thread_remsets;
5523 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));
5524 for (p = remset->data; p < remset->store_next;)
5525 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5526 next = remset->next;
5527 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5528 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5529 freed_thread_remsets = next;
5534 * Clear the info in the remembered sets: we're doing a major collection, so
5535 * the per-thread ones are not needed and the global ones will be reconstructed
5539 clear_remsets (void)
5542 SgenThreadInfo *info;
5543 RememberedSet *remset, *next;
5545 /* the global list */
5546 for (remset = global_remset; remset; remset = next) {
5547 remset->store_next = remset->data;
5548 next = remset->next;
5549 remset->next = NULL;
5550 if (remset != global_remset) {
5551 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5552 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5555 /* the generic store ones */
5556 while (generic_store_remsets) {
5557 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5558 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5559 generic_store_remsets = gs_next;
5561 /* the per-thread ones */
5562 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5563 for (info = thread_table [i]; info; info = info->next) {
5564 for (remset = info->remset; remset; remset = next) {
5565 remset->store_next = remset->data;
5566 next = remset->next;
5567 remset->next = NULL;
5568 if (remset != info->remset) {
5569 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5570 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5573 clear_thread_store_remset_buffer (info);
5577 /* the freed thread ones */
5578 while (freed_thread_remsets) {
5579 next = freed_thread_remsets->next;
5580 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5581 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5582 freed_thread_remsets = next;
5587 * Clear the thread local TLAB variables for all threads.
5592 SgenThreadInfo *info;
5595 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5596 for (info = thread_table [i]; info; info = info->next) {
5597 /* A new TLAB will be allocated when the thread does its first allocation */
5598 *info->tlab_start_addr = NULL;
5599 *info->tlab_next_addr = NULL;
5600 *info->tlab_temp_end_addr = NULL;
5601 *info->tlab_real_end_addr = NULL;
5606 /* LOCKING: assumes the GC lock is held */
5607 static SgenThreadInfo*
5608 gc_register_current_thread (void *addr)
5611 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5612 #ifndef HAVE_KW_THREAD
5613 SgenThreadInfo *__thread_info__ = info;
5619 memset (info, 0, sizeof (SgenThreadInfo));
5620 #ifndef HAVE_KW_THREAD
5621 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5623 g_assert (!pthread_getspecific (thread_info_key));
5624 pthread_setspecific (thread_info_key, info);
5629 info->id = ARCH_GET_THREAD ();
5630 info->stop_count = -1;
5633 info->stack_start = NULL;
5634 info->tlab_start_addr = &TLAB_START;
5635 info->tlab_next_addr = &TLAB_NEXT;
5636 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5637 info->tlab_real_end_addr = &TLAB_REAL_END;
5638 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5639 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5640 info->stopped_ip = NULL;
5641 info->stopped_domain = NULL;
5642 info->stopped_regs = NULL;
5644 binary_protocol_thread_register ((gpointer)info->id);
5646 #ifdef HAVE_KW_THREAD
5647 tlab_next_addr = &tlab_next;
5648 store_remset_buffer_index_addr = &store_remset_buffer_index;
5651 /* try to get it with attributes first */
5652 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5656 pthread_attr_t attr;
5657 pthread_getattr_np (pthread_self (), &attr);
5658 pthread_attr_getstack (&attr, &sstart, &size);
5659 info->stack_start_limit = sstart;
5660 info->stack_end = (char*)sstart + size;
5661 pthread_attr_destroy (&attr);
5663 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5664 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5665 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5668 /* FIXME: we assume the stack grows down */
5669 gsize stack_bottom = (gsize)addr;
5670 stack_bottom += 4095;
5671 stack_bottom &= ~4095;
5672 info->stack_end = (char*)stack_bottom;
5676 #ifdef HAVE_KW_THREAD
5677 stack_end = info->stack_end;
5680 /* hash into the table */
5681 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
5682 info->next = thread_table [hash];
5683 thread_table [hash] = info;
5685 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
5686 pthread_setspecific (remembered_set_key, info->remset);
5687 #ifdef HAVE_KW_THREAD
5688 remembered_set = info->remset;
5691 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5692 STORE_REMSET_BUFFER_INDEX = 0;
5694 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
5696 if (gc_callbacks.thread_attach_func)
5697 info->runtime_data = gc_callbacks.thread_attach_func ();
5703 add_generic_store_remset_from_buffer (gpointer *buffer)
5705 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5706 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5707 remset->next = generic_store_remsets;
5708 generic_store_remsets = remset;
5712 unregister_current_thread (void)
5715 SgenThreadInfo *prev = NULL;
5717 RememberedSet *rset;
5718 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
5720 binary_protocol_thread_unregister ((gpointer)id);
5722 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5723 p = thread_table [hash];
5725 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
5726 while (!ARCH_THREAD_EQUALS (p->id, id)) {
5731 thread_table [hash] = p->next;
5733 prev->next = p->next;
5736 if (freed_thread_remsets) {
5737 for (rset = p->remset; rset->next; rset = rset->next)
5739 rset->next = freed_thread_remsets;
5740 freed_thread_remsets = p->remset;
5742 freed_thread_remsets = p->remset;
5745 if (*p->store_remset_buffer_index_addr)
5746 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5747 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5752 unregister_thread (void *k)
5754 g_assert (!mono_domain_get ());
5756 unregister_current_thread ();
5761 mono_gc_register_thread (void *baseptr)
5763 SgenThreadInfo *info;
5767 info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5769 info = gc_register_current_thread (baseptr);
5772 /* Need a better place to initialize this */
5773 if (!array_fill_vtable && mono_get_root_domain ()) {
5774 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
5777 return info != NULL;
5780 #if USE_PTHREAD_INTERCEPT
5783 void *(*start_routine) (void *);
5786 MonoSemType registered;
5787 } SgenThreadStartInfo;
5790 gc_start_thread (void *arg)
5792 SgenThreadStartInfo *start_info = arg;
5793 SgenThreadInfo* info;
5794 void *t_arg = start_info->arg;
5795 void *(*start_func) (void*) = start_info->start_routine;
5800 info = gc_register_current_thread (&result);
5802 post_result = MONO_SEM_POST (&(start_info->registered));
5803 g_assert (!post_result);
5804 result = start_func (t_arg);
5805 g_assert (!mono_domain_get ());
5807 * this is done by the pthread key dtor
5809 unregister_current_thread ();
5817 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5819 SgenThreadStartInfo *start_info;
5822 start_info = malloc (sizeof (SgenThreadStartInfo));
5825 MONO_SEM_INIT (&(start_info->registered), 0);
5826 start_info->arg = arg;
5827 start_info->start_routine = start_routine;
5829 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
5831 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
5832 /*if (EINTR != errno) ABORT("sem_wait failed"); */
5835 MONO_SEM_DESTROY (&(start_info->registered));
5841 mono_gc_pthread_join (pthread_t thread, void **retval)
5843 return pthread_join (thread, retval);
5847 mono_gc_pthread_detach (pthread_t thread)
5849 return pthread_detach (thread);
5852 #endif /* USE_PTHREAD_INTERCEPT */
5855 * ######################################################################
5856 * ######## Write barriers
5857 * ######################################################################
5861 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
5864 dummy_use (gpointer v) {
5865 __asm__ volatile ("" : "=r"(v) : "r"(v));
5869 static RememberedSet*
5870 alloc_remset (int size, gpointer id) {
5871 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
5872 res->store_next = res->data;
5873 res->end_set = res->data + size;
5875 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
5880 * Note: the write barriers first do the needed GC work and then do the actual store:
5881 * this way the value is visible to the conservative GC scan after the write barrier
5882 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
5883 * the conservative scan, otherwise by the remembered set scan.
5886 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
5888 HEAVY_STAT (++stat_wbarrier_set_field);
5889 if (ptr_in_nursery (field_ptr)) {
5890 *(void**)field_ptr = value;
5893 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
5894 if (use_cardtable) {
5895 *(void**)field_ptr = value;
5896 if (ptr_in_nursery (value))
5897 sgen_card_table_mark_address ((mword)field_ptr);
5904 rs = REMEMBERED_SET;
5905 if (rs->store_next < rs->end_set) {
5906 *(rs->store_next++) = (mword)field_ptr;
5907 *(void**)field_ptr = value;
5911 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5912 rs->next = REMEMBERED_SET;
5913 REMEMBERED_SET = rs;
5914 #ifdef HAVE_KW_THREAD
5915 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5917 *(rs->store_next++) = (mword)field_ptr;
5918 *(void**)field_ptr = value;
5924 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
5926 HEAVY_STAT (++stat_wbarrier_set_arrayref);
5927 if (ptr_in_nursery (slot_ptr)) {
5928 *(void**)slot_ptr = value;
5931 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
5932 if (use_cardtable) {
5933 *(void**)slot_ptr = value;
5934 if (ptr_in_nursery (value))
5935 sgen_card_table_mark_address ((mword)slot_ptr);
5942 rs = REMEMBERED_SET;
5943 if (rs->store_next < rs->end_set) {
5944 *(rs->store_next++) = (mword)slot_ptr;
5945 *(void**)slot_ptr = value;
5949 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5950 rs->next = REMEMBERED_SET;
5951 REMEMBERED_SET = rs;
5952 #ifdef HAVE_KW_THREAD
5953 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5955 *(rs->store_next++) = (mword)slot_ptr;
5956 *(void**)slot_ptr = value;
5962 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
5964 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
5965 /*This check can be done without taking a lock since dest_ptr array is pinned*/
5966 if (ptr_in_nursery (dest_ptr) || count <= 0) {
5967 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5971 if (use_cardtable) {
5972 gpointer *dest = dest_ptr;
5973 gpointer *src = src_ptr;
5975 /*overlapping that required backward copying*/
5976 if (src < dest && (src + count) > dest) {
5977 gpointer *start = dest;
5981 for (; dest >= start; --src, --dest) {
5982 gpointer value = *src;
5984 if (ptr_in_nursery (value))
5985 sgen_card_table_mark_address ((mword)dest);
5989 gpointer *end = dest + count;
5990 for (; dest < end; ++src, ++dest) {
5991 gpointer value = *src;
5993 if (ptr_in_nursery (value))
5994 sgen_card_table_mark_address ((mword)dest);
6002 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6004 rs = REMEMBERED_SET;
6005 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6006 if (rs->store_next + 1 < rs->end_set) {
6007 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6008 *(rs->store_next++) = count;
6012 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6013 rs->next = REMEMBERED_SET;
6014 REMEMBERED_SET = rs;
6015 #ifdef HAVE_KW_THREAD
6016 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6018 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6019 *(rs->store_next++) = count;
6025 static char *found_obj;
6028 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
6030 char *ptr = user_data;
6032 if (ptr >= obj && ptr < obj + size) {
6033 g_assert (!found_obj);
6038 /* for use in the debugger */
6039 char* find_object_for_ptr (char *ptr);
6041 find_object_for_ptr (char *ptr)
6043 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6045 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6046 find_object_for_ptr_callback, ptr, TRUE);
6052 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
6057 * Very inefficient, but this is debugging code, supposed to
6058 * be called from gdb, so we don't care.
6061 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
6066 evacuate_remset_buffer (void)
6071 buffer = STORE_REMSET_BUFFER;
6073 add_generic_store_remset_from_buffer (buffer);
6074 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6076 STORE_REMSET_BUFFER_INDEX = 0;
6080 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6086 HEAVY_STAT (++stat_wbarrier_generic_store);
6088 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6089 /* FIXME: ptr_in_heap must be called with the GC lock held */
6090 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6091 char *start = find_object_for_ptr (ptr);
6092 MonoObject *value = *(MonoObject**)ptr;
6096 MonoObject *obj = (MonoObject*)start;
6097 if (obj->vtable->domain != value->vtable->domain)
6098 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6104 if (*(gpointer*)ptr)
6105 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6107 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6108 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6112 if (use_cardtable) {
6113 if (ptr_in_nursery(*(gpointer*)ptr))
6114 sgen_card_table_mark_address ((mword)ptr);
6120 buffer = STORE_REMSET_BUFFER;
6121 index = STORE_REMSET_BUFFER_INDEX;
6122 /* This simple optimization eliminates a sizable portion of
6123 entries. Comparing it to the last but one entry as well
6124 doesn't eliminate significantly more entries. */
6125 if (buffer [index] == ptr) {
6130 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6131 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6134 if (index >= STORE_REMSET_BUFFER_SIZE) {
6135 evacuate_remset_buffer ();
6136 index = STORE_REMSET_BUFFER_INDEX;
6137 g_assert (index == 0);
6140 buffer [index] = ptr;
6141 STORE_REMSET_BUFFER_INDEX = index;
6147 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6149 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6150 *(void**)ptr = value;
6151 if (ptr_in_nursery (value))
6152 mono_gc_wbarrier_generic_nostore (ptr);
6156 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
6158 mword *dest = _dest;
6163 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
6168 size -= SIZEOF_VOID_P;
6175 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6178 size_t size = count * mono_class_value_size (klass, NULL);
6180 HEAVY_STAT (++stat_wbarrier_value_copy);
6181 g_assert (klass->valuetype);
6183 memmove (dest, src, size);
6184 if (use_cardtable) {
6185 sgen_card_table_mark_range ((mword)dest, size);
6187 rs = REMEMBERED_SET;
6188 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !klass->has_references) {
6192 g_assert (klass->gc_descr_inited);
6193 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));
6195 if (rs->store_next + 3 < rs->end_set) {
6196 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6197 *(rs->store_next++) = (mword)klass->gc_descr;
6198 *(rs->store_next++) = (mword)count;
6202 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6203 rs->next = REMEMBERED_SET;
6204 REMEMBERED_SET = rs;
6205 #ifdef HAVE_KW_THREAD
6206 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6208 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6209 *(rs->store_next++) = (mword)klass->gc_descr;
6210 *(rs->store_next++) = (mword)count;
6216 * mono_gc_wbarrier_object_copy:
6218 * Write barrier to call when obj is the result of a clone or copy of an object.
6221 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6227 HEAVY_STAT (++stat_wbarrier_object_copy);
6228 rs = REMEMBERED_SET;
6229 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6230 size = mono_object_class (obj)->instance_size;
6232 /* do not copy the sync state */
6233 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6234 size - sizeof (MonoObject));
6235 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6239 if (rs->store_next < rs->end_set) {
6240 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6244 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6245 rs->next = REMEMBERED_SET;
6246 REMEMBERED_SET = rs;
6247 #ifdef HAVE_KW_THREAD
6248 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6250 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6255 * ######################################################################
6256 * ######## Collector debugging
6257 * ######################################################################
6260 const char*descriptor_types [] = {
6272 describe_ptr (char *ptr)
6279 if (ptr_in_nursery (ptr)) {
6280 printf ("Pointer inside nursery.\n");
6282 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
6284 printf ("Pointer is the start of object %p in LOS space.\n", start);
6286 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
6288 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
6289 printf ("Pointer inside oldspace.\n");
6290 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
6291 printf ("Pointer is inside a pinned chunk.\n");
6293 printf ("Pointer unknown.\n");
6298 if (object_is_pinned (ptr))
6299 printf ("Object is pinned.\n");
6301 if (object_is_forwarded (ptr))
6302 printf ("Object is forwared.\n");
6304 // FIXME: Handle pointers to the inside of objects
6305 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6307 printf ("VTable: %p\n", vtable);
6308 if (vtable == NULL) {
6309 printf ("VTable is invalid (empty).\n");
6312 if (ptr_in_nursery (vtable)) {
6313 printf ("VTable is invalid (points inside nursery).\n");
6316 printf ("Class: %s\n", vtable->klass->name);
6318 desc = ((GCVTable*)vtable)->desc;
6319 printf ("Descriptor: %lx\n", (long)desc);
6322 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6326 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6332 switch ((*p) & REMSET_TYPE_MASK) {
6333 case REMSET_LOCATION:
6334 if (*p == (mword)addr)
6338 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6340 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6344 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6345 count = safe_object_get_size ((MonoObject*)ptr);
6346 count = ALIGN_UP (count);
6347 count /= sizeof (mword);
6348 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6352 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6356 switch (desc & 0x7) {
6357 case DESC_TYPE_RUN_LENGTH:
6358 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6360 case DESC_TYPE_SMALL_BITMAP:
6361 OBJ_BITMAP_SIZE (skip_size, desc, start);
6365 g_assert_not_reached ();
6368 /* The descriptor includes the size of MonoObject */
6369 skip_size -= sizeof (MonoObject);
6371 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6376 g_assert_not_reached ();
6382 * Return whenever ADDR occurs in the remembered sets
6385 find_in_remsets (char *addr)
6388 SgenThreadInfo *info;
6389 RememberedSet *remset;
6390 GenericStoreRememberedSet *store_remset;
6392 gboolean found = FALSE;
6394 /* the global one */
6395 for (remset = global_remset; remset; remset = remset->next) {
6396 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));
6397 for (p = remset->data; p < remset->store_next;) {
6398 p = find_in_remset_loc (p, addr, &found);
6404 /* the generic store ones */
6405 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6406 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6407 if (store_remset->data [i] == addr)
6412 /* the per-thread ones */
6413 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6414 for (info = thread_table [i]; info; info = info->next) {
6416 for (remset = info->remset; remset; remset = remset->next) {
6417 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));
6418 for (p = remset->data; p < remset->store_next;) {
6419 p = find_in_remset_loc (p, addr, &found);
6424 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6425 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6431 /* the freed thread ones */
6432 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6433 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));
6434 for (p = remset->data; p < remset->store_next;) {
6435 p = find_in_remset_loc (p, addr, &found);
6444 static gboolean missing_remsets;
6447 * We let a missing remset slide if the target object is pinned,
6448 * because the store might have happened but the remset not yet added,
6449 * but in that case the target must be pinned. We might theoretically
6450 * miss some missing remsets this way, but it's very unlikely.
6453 #define HANDLE_PTR(ptr,obj) do { \
6454 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6455 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6456 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); \
6457 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6458 if (!object_is_pinned (*(ptr))) \
6459 missing_remsets = TRUE; \
6465 * Check that each object reference which points into the nursery can
6466 * be found in the remembered sets.
6469 check_consistency_callback (char *start, size_t size, void *dummy)
6471 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6472 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6474 #define SCAN_OBJECT_ACTION
6475 #include "sgen-scan-object.h"
6479 * Perform consistency check of the heap.
6481 * Assumes the world is stopped.
6484 check_consistency (void)
6486 // Need to add more checks
6488 missing_remsets = FALSE;
6490 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6492 // Check that oldspace->newspace pointers are registered with the collector
6493 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6495 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6497 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6499 #ifdef SGEN_BINARY_PROTOCOL
6500 if (!binary_protocol_file)
6502 g_assert (!missing_remsets);
6507 #define HANDLE_PTR(ptr,obj) do { \
6508 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6509 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6513 check_major_refs_callback (char *start, size_t size, void *dummy)
6515 #define SCAN_OBJECT_ACTION
6516 #include "sgen-scan-object.h"
6520 check_major_refs (void)
6522 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6523 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6526 /* Check that the reference is valid */
6528 #define HANDLE_PTR(ptr,obj) do { \
6530 g_assert (safe_name (*(ptr)) != NULL); \
6537 * Perform consistency check on an object. Currently we only check that the
6538 * reference fields are valid.
6541 check_object (char *start)
6546 #include "sgen-scan-object.h"
6550 * ######################################################################
6551 * ######## Other mono public interface functions.
6552 * ######################################################################
6555 #define REFS_SIZE 128
6558 MonoGCReferences callback;
6562 MonoObject *refs [REFS_SIZE];
6563 uintptr_t offsets [REFS_SIZE];
6567 #define HANDLE_PTR(ptr,obj) do { \
6569 if (hwi->count == REFS_SIZE) { \
6570 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
6574 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
6575 hwi->refs [hwi->count++] = *(ptr); \
6580 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6582 #include "sgen-scan-object.h"
6586 walk_references (char *start, size_t size, void *data)
6588 HeapWalkInfo *hwi = data;
6591 collect_references (hwi, start, size);
6592 if (hwi->count || !hwi->called)
6593 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6597 * mono_gc_walk_heap:
6598 * @flags: flags for future use
6599 * @callback: a function pointer called for each object in the heap
6600 * @data: a user data pointer that is passed to callback
6602 * This function can be used to iterate over all the live objects in the heap:
6603 * for each object, @callback is invoked, providing info about the object's
6604 * location in memory, its class, its size and the objects it references.
6605 * For each referenced object it's offset from the object address is
6606 * reported in the offsets array.
6607 * The object references may be buffered, so the callback may be invoked
6608 * multiple times for the same object: in all but the first call, the size
6609 * argument will be zero.
6610 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6611 * profiler event handler.
6613 * Returns: a non-zero value if the GC doesn't support heap walking
6616 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6621 hwi.callback = callback;
6624 clear_nursery_fragments (nursery_next);
6625 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
6627 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
6628 mono_sgen_los_iterate_objects (walk_references, &hwi);
6634 mono_gc_collect (int generation)
6639 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6640 stop_world (generation);
6641 if (generation == 0) {
6642 collect_nursery (0);
6644 major_collection ("user request");
6646 restart_world (generation);
6647 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6652 mono_gc_max_generation (void)
6658 mono_gc_collection_count (int generation)
6660 if (generation == 0)
6661 return num_minor_gcs;
6662 return num_major_gcs;
6666 mono_gc_get_used_size (void)
6670 tot = los_memory_usage;
6671 tot += nursery_section->next_data - nursery_section->data;
6672 tot += major_collector.get_used_size ();
6673 /* FIXME: account for pinned objects */
6679 mono_gc_get_heap_size (void)
6685 mono_gc_disable (void)
6693 mono_gc_enable (void)
6701 mono_gc_get_los_limit (void)
6703 return MAX_SMALL_OBJ_SIZE;
6707 mono_object_is_alive (MonoObject* o)
6713 mono_gc_get_generation (MonoObject *obj)
6715 if (ptr_in_nursery (obj))
6721 mono_gc_enable_events (void)
6726 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6729 mono_gc_register_disappearing_link (obj, link_addr, track);
6734 mono_gc_weak_link_remove (void **link_addr)
6737 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
6742 mono_gc_weak_link_get (void **link_addr)
6746 return (MonoObject*) REVEAL_POINTER (*link_addr);
6750 mono_gc_ephemeron_array_add (MonoObject *obj)
6752 EphemeronLinkNode *node;
6756 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
6761 node->array = (char*)obj;
6762 node->next = ephemeron_list;
6763 ephemeron_list = node;
6765 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6772 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6775 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
6776 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6777 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6779 mword complex = alloc_complex_descriptor (bitmap, numbits);
6780 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6784 static void *all_ref_root_descrs [32];
6787 mono_gc_make_root_descr_all_refs (int numbits)
6792 if (numbits < 32 && all_ref_root_descrs [numbits])
6793 return all_ref_root_descrs [numbits];
6795 gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
6796 memset (gc_bitmap, 0xff, numbits / 8);
6798 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
6799 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
6803 all_ref_root_descrs [numbits] = descr;
6809 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6813 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6814 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6815 user_descriptors [user_descriptors_next ++] = marker;
6821 mono_gc_alloc_fixed (size_t size, void *descr)
6823 /* FIXME: do a single allocation */
6824 void *res = calloc (1, size);
6827 if (!mono_gc_register_root (res, size, descr)) {
6835 mono_gc_free_fixed (void* addr)
6837 mono_gc_deregister_root (addr);
6842 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6846 result = func (data);
6847 UNLOCK_INTERRUPTION;
6852 mono_gc_is_gc_thread (void)
6856 result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
6862 mono_gc_base_init (void)
6866 char *major_collector_opt = NULL;
6867 struct sigaction sinfo;
6870 #ifdef PLATFORM_ANDROID
6871 g_assert_not_reached ();
6874 /* the gc_initialized guard seems to imply this method is
6875 idempotent, but LOCK_INIT(gc_mutex) might not be. It's
6876 defined in sgen-gc.h as nothing, so there's no danger at
6878 LOCK_INIT (gc_mutex);
6880 if (gc_initialized) {
6884 pagesize = mono_pagesize ();
6885 gc_debug_file = stderr;
6887 LOCK_INIT (interruption_mutex);
6888 LOCK_INIT (global_remset_mutex);
6889 LOCK_INIT (pin_queue_mutex);
6891 if ((env = getenv ("MONO_GC_PARAMS"))) {
6892 opts = g_strsplit (env, ",", -1);
6893 for (ptr = opts; *ptr; ++ptr) {
6895 if (g_str_has_prefix (opt, "major=")) {
6896 opt = strchr (opt, '=') + 1;
6897 major_collector_opt = g_strdup (opt);
6905 mono_sgen_init_internal_allocator ();
6907 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
6908 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
6909 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
6910 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
6911 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
6912 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
6913 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6914 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
6915 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
6917 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
6918 mono_sgen_marksweep_init (&major_collector);
6919 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
6920 mono_sgen_marksweep_fixed_init (&major_collector);
6921 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
6922 mono_sgen_marksweep_par_init (&major_collector);
6923 workers_init (mono_cpu_count ());
6924 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
6925 mono_sgen_marksweep_fixed_par_init (&major_collector);
6926 workers_init (mono_cpu_count ());
6927 } else if (!strcmp (major_collector_opt, "copying")) {
6928 mono_sgen_copying_init (&major_collector);
6930 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
6934 #ifdef SGEN_HAVE_CARDTABLE
6935 use_cardtable = major_collector.supports_cardtable;
6937 use_cardtable = FALSE;
6941 for (ptr = opts; *ptr; ++ptr) {
6943 if (g_str_has_prefix (opt, "major="))
6945 if (g_str_has_prefix (opt, "wbarrier=")) {
6946 opt = strchr (opt, '=') + 1;
6947 if (strcmp (opt, "remset") == 0) {
6948 use_cardtable = FALSE;
6949 } else if (strcmp (opt, "cardtable") == 0) {
6950 if (!use_cardtable) {
6951 if (major_collector.supports_cardtable)
6952 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
6954 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
6960 if (g_str_has_prefix (opt, "max-heap-size=")) {
6961 opt = strchr (opt, '=') + 1;
6962 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
6963 if ((max_heap & (mono_pagesize () - 1))) {
6964 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
6968 fprintf (stderr, "max-heap-size must be an integer.\n");
6974 if (g_str_has_prefix (opt, "nursery-size=")) {
6976 opt = strchr (opt, '=') + 1;
6977 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
6978 default_nursery_size = val;
6979 #ifdef SGEN_ALIGN_NURSERY
6980 if ((val & (val - 1))) {
6981 fprintf (stderr, "The nursery size must be a power of two.\n");
6985 default_nursery_bits = 0;
6986 while (1 << (++ default_nursery_bits) != default_nursery_size)
6990 fprintf (stderr, "nursery-size must be an integer.\n");
6996 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
6997 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
6998 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6999 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7000 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
7001 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
7002 if (major_collector.print_gc_param_usage)
7003 major_collector.print_gc_param_usage ();
7010 if (major_collector_opt)
7011 g_free (major_collector_opt);
7013 nursery_size = DEFAULT_NURSERY_SIZE;
7014 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7015 init_heap_size_limits (max_heap);
7019 if ((env = getenv ("MONO_GC_DEBUG"))) {
7020 opts = g_strsplit (env, ",", -1);
7021 for (ptr = opts; ptr && *ptr; ptr ++) {
7023 if (opt [0] >= '0' && opt [0] <= '9') {
7024 gc_debug_level = atoi (opt);
7029 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7030 gc_debug_file = fopen (rf, "wb");
7032 gc_debug_file = stderr;
7035 } else if (!strcmp (opt, "collect-before-allocs")) {
7036 collect_before_allocs = TRUE;
7037 } else if (!strcmp (opt, "check-at-minor-collections")) {
7038 consistency_check_at_minor_collection = TRUE;
7039 nursery_clear_policy = CLEAR_AT_GC;
7040 } else if (!strcmp (opt, "xdomain-checks")) {
7041 xdomain_checks = TRUE;
7042 } else if (!strcmp (opt, "clear-at-gc")) {
7043 nursery_clear_policy = CLEAR_AT_GC;
7044 } else if (!strcmp (opt, "conservative-stack-mark")) {
7045 conservative_stack_mark = TRUE;
7046 } else if (!strcmp (opt, "check-scan-starts")) {
7047 do_scan_starts_check = TRUE;
7048 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7049 char *filename = strchr (opt, '=') + 1;
7050 nursery_clear_policy = CLEAR_AT_GC;
7051 heap_dump_file = fopen (filename, "w");
7053 fprintf (heap_dump_file, "<sgen-dump>\n");
7054 #ifdef SGEN_BINARY_PROTOCOL
7055 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7056 char *filename = strchr (opt, '=') + 1;
7057 binary_protocol_file = fopen (filename, "w");
7060 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7061 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7062 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
7069 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7070 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7072 sigfillset (&sinfo.sa_mask);
7073 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7074 sinfo.sa_sigaction = suspend_handler;
7075 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7076 g_error ("failed sigaction");
7079 sinfo.sa_handler = restart_handler;
7080 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7081 g_error ("failed sigaction");
7084 sigfillset (&suspend_signal_mask);
7085 sigdelset (&suspend_signal_mask, restart_signal_num);
7087 global_remset = alloc_remset (1024, NULL);
7088 global_remset->next = NULL;
7090 pthread_key_create (&remembered_set_key, unregister_thread);
7092 #ifndef HAVE_KW_THREAD
7093 pthread_key_create (&thread_info_key, NULL);
7099 gc_initialized = TRUE;
7101 mono_gc_register_thread (&sinfo);
7105 mono_gc_get_suspend_signal (void)
7107 return suspend_signal_num;
7117 #ifdef HAVE_KW_THREAD
7118 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7119 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7120 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7121 mono_mb_emit_i4 ((mb), (offset)); \
7124 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7125 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7126 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7127 mono_mb_emit_i4 ((mb), thread_info_key); \
7128 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7129 mono_mb_emit_byte ((mb), CEE_ADD); \
7130 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7134 #ifdef MANAGED_ALLOCATION
7135 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7136 * for each class. This is currently not easy to do, as it is hard to generate basic
7137 * blocks + branches, but it is easy with the linear IL codebase.
7139 * For this to work we'd need to solve the TLAB race, first. Now we
7140 * require the allocator to be in a few known methods to make sure
7141 * that they are executed atomically via the restart mechanism.
7144 create_allocator (int atype)
7146 int p_var, size_var;
7147 guint32 slowpath_branch, max_size_branch;
7148 MonoMethodBuilder *mb;
7150 MonoMethodSignature *csig;
7151 static gboolean registered = FALSE;
7152 int tlab_next_addr_var, new_next_var;
7154 const char *name = NULL;
7155 AllocatorWrapperInfo *info;
7157 #ifdef HAVE_KW_THREAD
7158 int tlab_next_addr_offset = -1;
7159 int tlab_temp_end_offset = -1;
7161 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7162 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7164 g_assert (tlab_next_addr_offset != -1);
7165 g_assert (tlab_temp_end_offset != -1);
7169 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7170 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7174 if (atype == ATYPE_SMALL) {
7176 name = "AllocSmall";
7177 } else if (atype == ATYPE_NORMAL) {
7180 } else if (atype == ATYPE_VECTOR) {
7182 name = "AllocVector";
7184 g_assert_not_reached ();
7187 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7188 csig->ret = &mono_defaults.object_class->byval_arg;
7189 for (i = 0; i < num_params; ++i)
7190 csig->params [i] = &mono_defaults.int_class->byval_arg;
7192 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7193 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7194 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7195 /* size = vtable->klass->instance_size; */
7196 mono_mb_emit_ldarg (mb, 0);
7197 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7198 mono_mb_emit_byte (mb, CEE_ADD);
7199 mono_mb_emit_byte (mb, CEE_LDIND_I);
7200 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7201 mono_mb_emit_byte (mb, CEE_ADD);
7202 /* FIXME: assert instance_size stays a 4 byte integer */
7203 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7204 mono_mb_emit_stloc (mb, size_var);
7205 } else if (atype == ATYPE_VECTOR) {
7206 MonoExceptionClause *clause;
7208 MonoClass *oom_exc_class;
7211 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7212 mono_mb_emit_ldarg (mb, 1);
7213 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7214 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7215 mono_mb_emit_exception (mb, "OverflowException", NULL);
7216 mono_mb_patch_short_branch (mb, pos);
7218 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7219 clause->try_offset = mono_mb_get_label (mb);
7221 /* vtable->klass->sizes.element_size */
7222 mono_mb_emit_ldarg (mb, 0);
7223 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7224 mono_mb_emit_byte (mb, CEE_ADD);
7225 mono_mb_emit_byte (mb, CEE_LDIND_I);
7226 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7227 mono_mb_emit_byte (mb, CEE_ADD);
7228 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7231 mono_mb_emit_ldarg (mb, 1);
7232 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7233 /* + sizeof (MonoArray) */
7234 mono_mb_emit_icon (mb, sizeof (MonoArray));
7235 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7236 mono_mb_emit_stloc (mb, size_var);
7238 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7241 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7242 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7243 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7244 "System", "OverflowException");
7245 g_assert (clause->data.catch_class);
7246 clause->handler_offset = mono_mb_get_label (mb);
7248 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7249 "System", "OutOfMemoryException");
7250 g_assert (oom_exc_class);
7251 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7254 mono_mb_emit_byte (mb, CEE_POP);
7255 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7256 mono_mb_emit_byte (mb, CEE_THROW);
7258 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7259 mono_mb_set_clauses (mb, 1, clause);
7260 mono_mb_patch_branch (mb, pos_leave);
7263 g_assert_not_reached ();
7266 /* size += ALLOC_ALIGN - 1; */
7267 mono_mb_emit_ldloc (mb, size_var);
7268 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7269 mono_mb_emit_byte (mb, CEE_ADD);
7270 /* size &= ~(ALLOC_ALIGN - 1); */
7271 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7272 mono_mb_emit_byte (mb, CEE_AND);
7273 mono_mb_emit_stloc (mb, size_var);
7275 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7276 if (atype != ATYPE_SMALL) {
7277 mono_mb_emit_ldloc (mb, size_var);
7278 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7279 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7283 * We need to modify tlab_next, but the JIT only supports reading, so we read
7284 * another tls var holding its address instead.
7287 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7288 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7289 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7290 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7292 /* p = (void**)tlab_next; */
7293 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7294 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7295 mono_mb_emit_byte (mb, CEE_LDIND_I);
7296 mono_mb_emit_stloc (mb, p_var);
7298 /* new_next = (char*)p + size; */
7299 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7300 mono_mb_emit_ldloc (mb, p_var);
7301 mono_mb_emit_ldloc (mb, size_var);
7302 mono_mb_emit_byte (mb, CEE_CONV_I);
7303 mono_mb_emit_byte (mb, CEE_ADD);
7304 mono_mb_emit_stloc (mb, new_next_var);
7306 /* tlab_next = new_next */
7307 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7308 mono_mb_emit_ldloc (mb, new_next_var);
7309 mono_mb_emit_byte (mb, CEE_STIND_I);
7311 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7312 mono_mb_emit_ldloc (mb, new_next_var);
7313 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7314 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7317 if (atype != ATYPE_SMALL)
7318 mono_mb_patch_short_branch (mb, max_size_branch);
7320 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7321 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7323 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7324 mono_mb_emit_ldarg (mb, 0);
7325 mono_mb_emit_ldloc (mb, size_var);
7326 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7327 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7328 } else if (atype == ATYPE_VECTOR) {
7329 mono_mb_emit_ldarg (mb, 1);
7330 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7332 g_assert_not_reached ();
7334 mono_mb_emit_byte (mb, CEE_RET);
7337 mono_mb_patch_short_branch (mb, slowpath_branch);
7339 /* FIXME: Memory barrier */
7342 mono_mb_emit_ldloc (mb, p_var);
7343 mono_mb_emit_ldarg (mb, 0);
7344 mono_mb_emit_byte (mb, CEE_STIND_I);
7346 if (atype == ATYPE_VECTOR) {
7347 /* arr->max_length = max_length; */
7348 mono_mb_emit_ldloc (mb, p_var);
7349 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7350 mono_mb_emit_ldarg (mb, 1);
7351 mono_mb_emit_byte (mb, CEE_STIND_I);
7355 mono_mb_emit_ldloc (mb, p_var);
7356 mono_mb_emit_byte (mb, CEE_RET);
7358 res = mono_mb_create_method (mb, csig, 8);
7360 mono_method_get_header (res)->init_locals = FALSE;
7362 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7363 info->gc_name = "sgen";
7364 info->alloc_type = atype;
7365 mono_marshal_set_wrapper_info (res, info);
7372 mono_gc_get_gc_name (void)
7377 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7378 static MonoMethod *write_barrier_method;
7381 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7387 if (!mono_thread_internal_current ())
7388 /* Happens during thread attach */
7393 ji = mono_jit_info_table_find (domain, ip);
7396 method = ji->method;
7398 if (method == write_barrier_method)
7400 for (i = 0; i < ATYPE_NUM; ++i)
7401 if (method == alloc_method_cache [i])
7407 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7408 * The signature of the called method is:
7409 * object allocate (MonoVTable *vtable)
7412 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7414 #ifdef MANAGED_ALLOCATION
7415 MonoClass *klass = vtable->klass;
7417 #ifdef HAVE_KW_THREAD
7418 int tlab_next_offset = -1;
7419 int tlab_temp_end_offset = -1;
7420 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7421 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7423 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7427 if (!mono_runtime_has_tls_get ())
7429 if (klass->instance_size > tlab_size)
7431 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7435 if (klass->byval_arg.type == MONO_TYPE_STRING)
7437 if (collect_before_allocs)
7440 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7441 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7443 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7450 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7452 #ifdef MANAGED_ALLOCATION
7453 MonoClass *klass = vtable->klass;
7455 #ifdef HAVE_KW_THREAD
7456 int tlab_next_offset = -1;
7457 int tlab_temp_end_offset = -1;
7458 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7459 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7461 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7467 if (!mono_runtime_has_tls_get ())
7469 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7471 if (collect_before_allocs)
7473 g_assert (!klass->has_finalize && !klass->marshalbyref);
7475 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7482 mono_gc_get_managed_allocator_by_type (int atype)
7484 #ifdef MANAGED_ALLOCATION
7487 if (!mono_runtime_has_tls_get ())
7490 mono_loader_lock ();
7491 res = alloc_method_cache [atype];
7493 res = alloc_method_cache [atype] = create_allocator (atype);
7494 mono_loader_unlock ();
7502 mono_gc_get_managed_allocator_types (void)
7509 mono_gc_get_write_barrier (void)
7512 MonoMethodBuilder *mb;
7513 MonoMethodSignature *sig;
7514 #ifdef MANAGED_WBARRIER
7515 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7516 #ifndef SGEN_ALIGN_NURSERY
7517 int label_continue_1, label_continue_2, label_no_wb_5;
7518 int dereferenced_var;
7520 int buffer_var, buffer_index_var, dummy_var;
7522 #ifdef HAVE_KW_THREAD
7523 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7524 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7526 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7527 g_assert (stack_end_offset != -1);
7528 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7529 g_assert (store_remset_buffer_offset != -1);
7530 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7531 g_assert (store_remset_buffer_index_offset != -1);
7532 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7533 g_assert (store_remset_buffer_index_addr_offset != -1);
7537 g_assert (!use_cardtable);
7539 // FIXME: Maybe create a separate version for ctors (the branch would be
7540 // correctly predicted more times)
7541 if (write_barrier_method)
7542 return write_barrier_method;
7544 /* Create the IL version of mono_gc_barrier_generic_store () */
7545 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7546 sig->ret = &mono_defaults.void_class->byval_arg;
7547 sig->params [0] = &mono_defaults.int_class->byval_arg;
7549 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7551 #ifdef MANAGED_WBARRIER
7552 if (mono_runtime_has_tls_get ()) {
7553 #ifdef SGEN_ALIGN_NURSERY
7554 // if (ptr_in_nursery (ptr)) return;
7556 * Masking out the bits might be faster, but we would have to use 64 bit
7557 * immediates, which might be slower.
7559 mono_mb_emit_ldarg (mb, 0);
7560 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7561 mono_mb_emit_byte (mb, CEE_SHR_UN);
7562 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7563 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7565 // if (!ptr_in_nursery (*ptr)) return;
7566 mono_mb_emit_ldarg (mb, 0);
7567 mono_mb_emit_byte (mb, CEE_LDIND_I);
7568 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7569 mono_mb_emit_byte (mb, CEE_SHR_UN);
7570 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7571 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7574 // if (ptr < (nursery_start)) goto continue;
7575 mono_mb_emit_ldarg (mb, 0);
7576 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7577 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7579 // if (ptr >= nursery_real_end)) goto continue;
7580 mono_mb_emit_ldarg (mb, 0);
7581 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7582 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7585 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7588 mono_mb_patch_branch (mb, label_continue_1);
7589 mono_mb_patch_branch (mb, label_continue_2);
7591 // Dereference and store in local var
7592 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7593 mono_mb_emit_ldarg (mb, 0);
7594 mono_mb_emit_byte (mb, CEE_LDIND_I);
7595 mono_mb_emit_stloc (mb, dereferenced_var);
7597 // if (*ptr < nursery_start) return;
7598 mono_mb_emit_ldloc (mb, dereferenced_var);
7599 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7600 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7602 // if (*ptr >= nursery_end) return;
7603 mono_mb_emit_ldloc (mb, dereferenced_var);
7604 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7605 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7608 // if (ptr >= stack_end) goto need_wb;
7609 mono_mb_emit_ldarg (mb, 0);
7610 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7611 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7613 // if (ptr >= stack_start) return;
7614 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7615 mono_mb_emit_ldarg (mb, 0);
7616 mono_mb_emit_ldloc_addr (mb, dummy_var);
7617 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7620 mono_mb_patch_branch (mb, label_need_wb);
7622 // buffer = STORE_REMSET_BUFFER;
7623 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7624 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7625 mono_mb_emit_stloc (mb, buffer_var);
7627 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7628 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7629 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7630 mono_mb_emit_stloc (mb, buffer_index_var);
7632 // if (buffer [buffer_index] == ptr) return;
7633 mono_mb_emit_ldloc (mb, buffer_var);
7634 mono_mb_emit_ldloc (mb, buffer_index_var);
7635 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7636 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7637 mono_mb_emit_byte (mb, CEE_SHL);
7638 mono_mb_emit_byte (mb, CEE_ADD);
7639 mono_mb_emit_byte (mb, CEE_LDIND_I);
7640 mono_mb_emit_ldarg (mb, 0);
7641 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7644 mono_mb_emit_ldloc (mb, buffer_index_var);
7645 mono_mb_emit_icon (mb, 1);
7646 mono_mb_emit_byte (mb, CEE_ADD);
7647 mono_mb_emit_stloc (mb, buffer_index_var);
7649 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7650 mono_mb_emit_ldloc (mb, buffer_index_var);
7651 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7652 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7654 // buffer [buffer_index] = ptr;
7655 mono_mb_emit_ldloc (mb, buffer_var);
7656 mono_mb_emit_ldloc (mb, buffer_index_var);
7657 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7658 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7659 mono_mb_emit_byte (mb, CEE_SHL);
7660 mono_mb_emit_byte (mb, CEE_ADD);
7661 mono_mb_emit_ldarg (mb, 0);
7662 mono_mb_emit_byte (mb, CEE_STIND_I);
7664 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7665 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7666 mono_mb_emit_ldloc (mb, buffer_index_var);
7667 mono_mb_emit_byte (mb, CEE_STIND_I);
7670 mono_mb_patch_branch (mb, label_no_wb_1);
7671 mono_mb_patch_branch (mb, label_no_wb_2);
7672 mono_mb_patch_branch (mb, label_no_wb_3);
7673 mono_mb_patch_branch (mb, label_no_wb_4);
7674 #ifndef SGEN_ALIGN_NURSERY
7675 mono_mb_patch_branch (mb, label_no_wb_5);
7677 mono_mb_emit_byte (mb, CEE_RET);
7680 mono_mb_patch_branch (mb, label_slow_path);
7684 mono_mb_emit_ldarg (mb, 0);
7685 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7686 mono_mb_emit_byte (mb, CEE_RET);
7688 res = mono_mb_create_method (mb, sig, 16);
7691 mono_loader_lock ();
7692 if (write_barrier_method) {
7693 /* Already created */
7694 mono_free_method (res);
7696 /* double-checked locking */
7697 mono_memory_barrier ();
7698 write_barrier_method = res;
7700 mono_loader_unlock ();
7702 return write_barrier_method;
7706 mono_gc_get_description (void)
7708 return g_strdup ("sgen");
7712 mono_gc_set_desktop_mode (void)
7717 mono_gc_is_moving (void)
7723 mono_gc_is_disabled (void)
7729 mono_sgen_debug_printf (int level, const char *format, ...)
7733 if (level > gc_debug_level)
7736 va_start (ap, format);
7737 vfprintf (gc_debug_file, format, ap);
7741 #endif /* HAVE_SGEN_GC */