2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
15 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
18 * Permission is hereby granted to use or copy this program
19 * for any purpose, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
25 * Copyright 2001-2003 Ximian, Inc
26 * Copyright 2003-2010 Novell, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining
29 * a copy of this software and associated documentation files (the
30 * "Software"), to deal in the Software without restriction, including
31 * without limitation the rights to use, copy, modify, merge, publish,
32 * distribute, sublicense, and/or sell copies of the Software, and to
33 * permit persons to whom the Software is furnished to do so, subject to
34 * the following conditions:
36 * The above copyright notice and this permission notice shall be
37 * included in all copies or substantial portions of the Software.
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 * Important: allocation provides always zeroed memory, having to do
49 * a memset after allocation is deadly for performance.
50 * Memory usage at startup is currently as follows:
52 * 64 KB internal space
54 * We should provide a small memory config with half the sizes
56 * We currently try to make as few mono assumptions as possible:
57 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
59 * 2) gc descriptor is the second word in the vtable (first word in the class)
60 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
61 * 4) there is a function to get an object's size and the number of
62 * elements in an array.
63 * 5) we know the special way bounds are allocated for complex arrays
64 * 6) we know about proxies and how to treat them when domains are unloaded
66 * Always try to keep stack usage to a minimum: no recursive behaviour
67 * and no large stack allocs.
69 * General description.
70 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
71 * When the nursery is full we start a nursery collection: this is performed with a
73 * When the old generation is full we start a copying GC of the old generation as well:
74 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
75 * in the future. Maybe we'll even do both during the same collection like IMMIX.
77 * The things that complicate this description are:
78 * *) pinned objects: we can't move them so we need to keep track of them
79 * *) no precise info of the thread stacks and registers: we need to be able to
80 * quickly find the objects that may be referenced conservatively and pin them
81 * (this makes the first issues more important)
82 * *) large objects are too expensive to be dealt with using copying GC: we handle them
83 * with mark/sweep during major collections
84 * *) some objects need to not move even if they are small (interned strings, Type handles):
85 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
86 * PinnedChunks regions
92 *) we could have a function pointer in MonoClass to implement
93 customized write barriers for value types
95 *) investigate the stuff needed to advance a thread to a GC-safe
96 point (single-stepping, read from unmapped memory etc) and implement it.
97 This would enable us to inline allocations and write barriers, for example,
98 or at least parts of them, like the write barrier checks.
99 We may need this also for handling precise info on stacks, even simple things
100 as having uninitialized data on the stack and having to wait for the prolog
101 to zero it. Not an issue for the last frame that we scan conservatively.
102 We could always not trust the value in the slots anyway.
104 *) modify the jit to save info about references in stack locations:
105 this can be done just for locals as a start, so that at least
106 part of the stack is handled precisely.
108 *) test/fix endianess issues
110 *) Implement a card table as the write barrier instead of remembered
111 sets? Card tables are not easy to implement with our current
112 memory layout. We have several different kinds of major heap
113 objects: Small objects in regular blocks, small objects in pinned
114 chunks and LOS objects. If we just have a pointer we have no way
115 to tell which kind of object it points into, therefore we cannot
116 know where its card table is. The least we have to do to make
117 this happen is to get rid of write barriers for indirect stores.
120 *) Get rid of write barriers for indirect stores. We can do this by
121 telling the GC to wbarrier-register an object once we do an ldloca
122 or ldelema on it, and to unregister it once it's not used anymore
123 (it can only travel downwards on the stack). The problem with
124 unregistering is that it needs to happen eventually no matter
125 what, even if exceptions are thrown, the thread aborts, etc.
126 Rodrigo suggested that we could do only the registering part and
127 let the collector find out (pessimistically) when it's safe to
128 unregister, namely when the stack pointer of the thread that
129 registered the object is higher than it was when the registering
130 happened. This might make for a good first implementation to get
131 some data on performance.
133 *) Some sort of blacklist support? Blacklists is a concept from the
134 Boehm GC: if during a conservative scan we find pointers to an
135 area which we might use as heap, we mark that area as unusable, so
136 pointer retention by random pinning pointers is reduced.
138 *) experiment with max small object size (very small right now - 2kb,
139 because it's tied to the max freelist size)
141 *) add an option to mmap the whole heap in one chunk: it makes for many
142 simplifications in the checks (put the nursery at the top and just use a single
143 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
144 not flexible (too much of the address space may be used by default or we can't
145 increase the heap as needed) and we'd need a race-free mechanism to return memory
146 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
147 was written to, munmap is needed, but the following mmap may not find the same segment
150 *) memzero the major fragments after restarting the world and optionally a smaller
153 *) investigate having fragment zeroing threads
155 *) separate locks for finalization and other minor stuff to reduce
158 *) try a different copying order to improve memory locality
160 *) a thread abort after a store but before the write barrier will
161 prevent the write barrier from executing
163 *) specialized dynamically generated markers/copiers
165 *) Dynamically adjust TLAB size to the number of threads. If we have
166 too many threads that do allocation, we might need smaller TLABs,
167 and we might get better performance with larger TLABs if we only
168 have a handful of threads. We could sum up the space left in all
169 assigned TLABs and if that's more than some percentage of the
170 nursery size, reduce the TLAB size.
172 *) Explore placing unreachable objects on unused nursery memory.
173 Instead of memset'ng a region to zero, place an int[] covering it.
174 A good place to start is add_nursery_frag. The tricky thing here is
175 placing those objects atomically outside of a collection.
185 #include <semaphore.h>
194 #define _XOPEN_SOURCE
196 #include "metadata/metadata-internals.h"
197 #include "metadata/class-internals.h"
198 #include "metadata/gc-internal.h"
199 #include "metadata/object-internals.h"
200 #include "metadata/threads.h"
201 #include "metadata/sgen-gc.h"
202 #include "metadata/sgen-cardtable.h"
203 #include "metadata/sgen-protocol.h"
204 #include "metadata/sgen-archdep.h"
205 #include "metadata/sgen-bridge.h"
206 #include "metadata/mono-gc.h"
207 #include "metadata/method-builder.h"
208 #include "metadata/profiler-private.h"
209 #include "metadata/monitor.h"
210 #include "metadata/threadpool-internals.h"
211 #include "metadata/mempool-internals.h"
212 #include "metadata/marshal.h"
213 #include "utils/mono-mmap.h"
214 #include "utils/mono-time.h"
215 #include "utils/mono-semaphore.h"
216 #include "utils/mono-counters.h"
217 #include "utils/mono-proclib.h"
219 #include <mono/utils/memcheck.h>
221 #if defined(__MACH__)
222 #include "utils/mach-support.h"
225 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
229 #include "mono/cil/opcode.def"
235 #undef pthread_create
237 #undef pthread_detach
240 * ######################################################################
241 * ######## Types and constants used by the GC.
242 * ######################################################################
245 static int gc_initialized = 0;
246 /* If set, do a minor collection before every X allocation */
247 static guint32 collect_before_allocs = 0;
248 /* If set, do a heap consistency check before each minor collection */
249 static gboolean consistency_check_at_minor_collection = FALSE;
250 /* If set, check that there are no references to the domain left at domain unload */
251 static gboolean xdomain_checks = FALSE;
252 /* If not null, dump the heap after each collection into this file */
253 static FILE *heap_dump_file = NULL;
254 /* If set, mark stacks conservatively, even if precise marking is possible */
255 static gboolean conservative_stack_mark = FALSE;
256 /* If set, do a plausibility check on the scan_starts before and after
258 static gboolean do_scan_starts_check = FALSE;
259 static gboolean disable_minor_collections = FALSE;
260 static gboolean disable_major_collections = FALSE;
262 #ifdef HEAVY_STATISTICS
263 static long long stat_objects_alloced = 0;
264 static long long stat_bytes_alloced = 0;
265 long long stat_objects_alloced_degraded = 0;
266 long long stat_bytes_alloced_degraded = 0;
267 static long long stat_bytes_alloced_los = 0;
269 long long stat_copy_object_called_nursery = 0;
270 long long stat_objects_copied_nursery = 0;
271 long long stat_copy_object_called_major = 0;
272 long long stat_objects_copied_major = 0;
274 long long stat_scan_object_called_nursery = 0;
275 long long stat_scan_object_called_major = 0;
277 long long stat_nursery_copy_object_failed_from_space = 0;
278 long long stat_nursery_copy_object_failed_forwarded = 0;
279 long long stat_nursery_copy_object_failed_pinned = 0;
281 static long long stat_store_remsets = 0;
282 static long long stat_store_remsets_unique = 0;
283 static long long stat_saved_remsets_1 = 0;
284 static long long stat_saved_remsets_2 = 0;
285 static long long stat_local_remsets_processed = 0;
286 static long long stat_global_remsets_added = 0;
287 static long long stat_global_remsets_readded = 0;
288 static long long stat_global_remsets_processed = 0;
289 static long long stat_global_remsets_discarded = 0;
291 static long long stat_wasted_fragments_used = 0;
292 static long long stat_wasted_fragments_bytes = 0;
294 static int stat_wbarrier_set_field = 0;
295 static int stat_wbarrier_set_arrayref = 0;
296 static int stat_wbarrier_arrayref_copy = 0;
297 static int stat_wbarrier_generic_store = 0;
298 static int stat_wbarrier_generic_store_remset = 0;
299 static int stat_wbarrier_set_root = 0;
300 static int stat_wbarrier_value_copy = 0;
301 static int stat_wbarrier_object_copy = 0;
304 static long long stat_pinned_objects = 0;
306 static long long time_minor_pre_collection_fragment_clear = 0;
307 static long long time_minor_pinning = 0;
308 static long long time_minor_scan_remsets = 0;
309 static long long time_minor_scan_card_table = 0;
310 static long long time_minor_scan_pinned = 0;
311 static long long time_minor_scan_registered_roots = 0;
312 static long long time_minor_scan_thread_data = 0;
313 static long long time_minor_finish_gray_stack = 0;
314 static long long time_minor_fragment_creation = 0;
316 static long long time_major_pre_collection_fragment_clear = 0;
317 static long long time_major_pinning = 0;
318 static long long time_major_scan_pinned = 0;
319 static long long time_major_scan_registered_roots = 0;
320 static long long time_major_scan_thread_data = 0;
321 static long long time_major_scan_alloc_pinned = 0;
322 static long long time_major_scan_finalized = 0;
323 static long long time_major_scan_big_objects = 0;
324 static long long time_major_finish_gray_stack = 0;
325 static long long time_major_free_bigobjs = 0;
326 static long long time_major_los_sweep = 0;
327 static long long time_major_sweep = 0;
328 static long long time_major_fragment_creation = 0;
330 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
332 int gc_debug_level = 0;
337 mono_gc_flush_info (void)
339 fflush (gc_debug_file);
344 * Define this to allow the user to change the nursery size by
345 * specifying its value in the MONO_GC_PARAMS environmental
346 * variable. See mono_gc_base_init for details.
348 #define USER_CONFIG 1
350 #define TV_DECLARE SGEN_TV_DECLARE
351 #define TV_GETTIME SGEN_TV_GETTIME
352 #define TV_ELAPSED SGEN_TV_ELAPSED
353 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
355 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
357 /* The method used to clear the nursery */
358 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
359 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
364 CLEAR_AT_TLAB_CREATION
365 } NurseryClearPolicy;
367 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
370 * The young generation is divided into fragments. This is because
371 * we can hand one fragments to a thread for lock-less fast alloc and
372 * because the young generation ends up fragmented anyway by pinned objects.
373 * Once a collection is done, a list of fragments is created. When doing
374 * thread local alloc we use smallish nurseries so we allow new threads to
375 * allocate memory from gen0 without triggering a collection. Threads that
376 * are found to allocate lots of memory are given bigger fragments. This
377 * should make the finalizer thread use little nursery memory after a while.
378 * We should start assigning threads very small fragments: if there are many
379 * threads the nursery will be full of reserved space that the threads may not
380 * use at all, slowing down allocation speed.
381 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
382 * Allocation Buffers (TLABs).
384 typedef struct _Fragment Fragment;
388 char *fragment_start;
389 char *fragment_limit; /* the current soft limit for allocation */
393 /* the runtime can register areas of memory as roots: we keep two lists of roots,
394 * a pinned root set for conservatively scanned roots and a normal one for
395 * precisely scanned roots (currently implemented as a single list).
397 typedef struct _RootRecord RootRecord;
406 * We're never actually using the first element. It's always set to
407 * NULL to simplify the elimination of consecutive duplicate
410 #define STORE_REMSET_BUFFER_SIZE 1024
412 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
413 struct _GenericStoreRememberedSet {
414 GenericStoreRememberedSet *next;
415 /* We need one entry less because the first entry of store
416 remset buffers is always a dummy and we don't copy it. */
417 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
420 /* we have 4 possible values in the low 2 bits */
422 REMSET_LOCATION, /* just a pointer to the exact location */
423 REMSET_RANGE, /* range of pointer fields */
424 REMSET_OBJECT, /* mark all the object for scanning */
425 REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
426 REMSET_TYPE_MASK = 0x3
429 #ifdef HAVE_KW_THREAD
430 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
432 static pthread_key_t remembered_set_key;
433 static RememberedSet *global_remset;
434 static RememberedSet *freed_thread_remsets;
435 static GenericStoreRememberedSet *generic_store_remsets = NULL;
437 /*A two slots cache for recently inserted remsets */
438 static gpointer global_remset_cache [2];
440 /* FIXME: later choose a size that takes into account the RememberedSet struct
441 * and doesn't waste any alloc paddin space.
443 #define DEFAULT_REMSET_SIZE 1024
444 static RememberedSet* alloc_remset (int size, gpointer id);
445 static RememberedSet* alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id);
447 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
448 #define object_is_pinned SGEN_OBJECT_IS_PINNED
449 #define pin_object SGEN_PIN_OBJECT
450 #define unpin_object SGEN_UNPIN_OBJECT
452 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_end))
454 #define LOAD_VTABLE SGEN_LOAD_VTABLE
457 safe_name (void* obj)
459 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
460 return vt->klass->name;
463 #define safe_object_get_size mono_sgen_safe_object_get_size
466 mono_sgen_safe_name (void* obj)
468 return safe_name (obj);
472 * ######################################################################
473 * ######## Global data.
474 * ######################################################################
476 static LOCK_DECLARE (gc_mutex);
477 static int gc_disabled = 0;
478 static int num_minor_gcs = 0;
479 static int num_major_gcs = 0;
481 static gboolean use_cardtable;
485 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
486 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
487 static int default_nursery_size = (1 << 22);
488 #ifdef SGEN_ALIGN_NURSERY
489 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
490 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
491 static int default_nursery_bits = 22;
496 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
497 #ifdef SGEN_ALIGN_NURSERY
498 #define DEFAULT_NURSERY_BITS 22
503 #ifndef SGEN_ALIGN_NURSERY
504 #define DEFAULT_NURSERY_BITS -1
507 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
509 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
511 /* the minimum size of a fragment that we consider useful for allocation */
512 #define FRAGMENT_MIN_SIZE (512)
514 static mword pagesize = 4096;
515 static mword nursery_size;
516 static int degraded_mode = 0;
518 static mword total_alloc = 0;
519 /* use this to tune when to do a major/minor collection */
520 static mword memory_pressure = 0;
521 static mword minor_collection_allowance;
522 static int minor_collection_sections_alloced = 0;
524 static GCMemSection *nursery_section = NULL;
525 static mword lowest_heap_address = ~(mword)0;
526 static mword highest_heap_address = 0;
528 static LOCK_DECLARE (interruption_mutex);
529 static LOCK_DECLARE (global_remset_mutex);
530 static LOCK_DECLARE (pin_queue_mutex);
532 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
533 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
535 #define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
536 #define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
538 typedef struct _FinalizeEntry FinalizeEntry;
539 struct _FinalizeEntry {
544 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
545 struct _FinalizeEntryHashTable {
546 FinalizeEntry **table;
551 typedef struct _DisappearingLink DisappearingLink;
552 struct _DisappearingLink {
553 DisappearingLink *next;
557 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
558 struct _DisappearingLinkHashTable {
559 DisappearingLink **table;
564 typedef struct _EphemeronLinkNode EphemeronLinkNode;
566 struct _EphemeronLinkNode {
567 EphemeronLinkNode *next;
576 int current_collection_generation = -1;
579 * The link pointer is hidden by negating each bit. We use the lowest
580 * bit of the link (before negation) to store whether it needs
581 * resurrection tracking.
583 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
584 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
586 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
587 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
590 * The finalizable hash has the object as the key, the
591 * disappearing_link hash, has the link address as key.
593 static FinalizeEntryHashTable minor_finalizable_hash;
594 static FinalizeEntryHashTable major_finalizable_hash;
595 /* objects that are ready to be finalized */
596 static FinalizeEntry *fin_ready_list = NULL;
597 static FinalizeEntry *critical_fin_list = NULL;
599 static DisappearingLinkHashTable minor_disappearing_link_hash;
600 static DisappearingLinkHashTable major_disappearing_link_hash;
602 static EphemeronLinkNode *ephemeron_list;
604 static int num_ready_finalizers = 0;
605 static int no_finalize = 0;
608 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
609 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
610 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
614 /* registered roots: the key to the hash is the root start address */
616 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
618 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
619 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
620 static mword roots_size = 0; /* amount of memory in the root set */
621 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
623 #define GC_ROOT_NUM 32
626 void *objects [GC_ROOT_NUM];
627 int root_types [GC_ROOT_NUM];
628 uintptr_t extra_info [GC_ROOT_NUM];
632 notify_gc_roots (GCRootReport *report)
636 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
641 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
643 if (report->count == GC_ROOT_NUM)
644 notify_gc_roots (report);
645 report->objects [report->count] = object;
646 report->root_types [report->count] = rtype;
647 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
651 * The current allocation cursors
652 * We allocate objects in the nursery.
653 * The nursery is the area between nursery_start and nursery_end.
654 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
655 * from nursery fragments.
656 * tlab_next is the pointer to the space inside the TLAB where the next object will
658 * tlab_temp_end is the pointer to the end of the temporary space reserved for
659 * the allocation: it allows us to set the scan starts at reasonable intervals.
660 * tlab_real_end points to the end of the TLAB.
661 * nursery_frag_real_end points to the end of the currently used nursery fragment.
662 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
663 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
664 * At the next allocation, the area of the nursery where objects can be present is
665 * between MIN(nursery_first_pinned_start, first_fragment_start) and
666 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
668 static char *nursery_start = NULL;
669 static char *nursery_next = NULL;
670 static char *nursery_frag_real_end = NULL;
671 static char *nursery_end = NULL;
672 static char *nursery_last_pinned_end = NULL;
674 #ifdef HAVE_KW_THREAD
675 #define TLAB_ACCESS_INIT
676 #define TLAB_START tlab_start
677 #define TLAB_NEXT tlab_next
678 #define TLAB_TEMP_END tlab_temp_end
679 #define TLAB_REAL_END tlab_real_end
680 #define REMEMBERED_SET remembered_set
681 #define STORE_REMSET_BUFFER store_remset_buffer
682 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
683 #define IN_CRITICAL_REGION thread_info->in_critical_region
685 static pthread_key_t thread_info_key;
686 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
687 #define TLAB_START (__thread_info__->tlab_start)
688 #define TLAB_NEXT (__thread_info__->tlab_next)
689 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
690 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
691 #define REMEMBERED_SET (__thread_info__->remset)
692 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
693 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
694 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
697 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
698 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
699 #define EXIT_CRITICAL_REGION do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
702 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
703 * variables for next+temp_end ?
705 #ifdef HAVE_KW_THREAD
706 static __thread SgenThreadInfo *thread_info;
707 static __thread char *tlab_start;
708 static __thread char *tlab_next;
709 static __thread char *tlab_temp_end;
710 static __thread char *tlab_real_end;
711 static __thread gpointer *store_remset_buffer;
712 static __thread long store_remset_buffer_index;
713 /* Used by the managed allocator/wbarrier */
714 static __thread char **tlab_next_addr;
715 static __thread char *stack_end;
716 static __thread long *store_remset_buffer_index_addr;
719 /* The size of a TLAB */
720 /* The bigger the value, the less often we have to go to the slow path to allocate a new
721 * one, but the more space is wasted by threads not allocating much memory.
723 * FIXME: Make this self-tuning for each thread.
725 static guint32 tlab_size = (1024 * 4);
727 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
728 #define MAX_NURSERY_TLAB_WASTE 512
730 /* fragments that are free and ready to be used for allocation */
731 static Fragment *nursery_fragments = NULL;
732 /* freeelist of fragment structures */
733 static Fragment *fragment_freelist = NULL;
735 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
737 /* Functions supplied by the runtime to be called by the GC */
738 static MonoGCCallbacks gc_callbacks;
740 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
741 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
743 #define ALIGN_UP SGEN_ALIGN_UP
745 #define MOVED_OBJECTS_NUM 64
746 static void *moved_objects [MOVED_OBJECTS_NUM];
747 static int moved_objects_idx = 0;
749 /* Vtable of the objects used to fill out nursery fragments before a collection */
750 static MonoVTable *array_fill_vtable;
752 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
753 pthread_t main_gc_thread = NULL;
757 * ######################################################################
758 * ######## Heap size accounting
759 * ######################################################################
762 static mword max_heap_size = ((mword)0)- ((mword)1);
763 static mword allocated_heap;
765 /*Object was pinned during the current collection*/
766 static mword objects_pinned;
769 mono_sgen_release_space (mword size, int space)
771 allocated_heap -= size;
775 available_free_space (void)
777 return max_heap_size - MIN (allocated_heap, max_heap_size);
781 mono_sgen_try_alloc_space (mword size, int space)
783 if (available_free_space () < size)
786 allocated_heap += size;
791 init_heap_size_limits (glong max_heap)
796 if (max_heap < nursery_size * 4) {
797 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
800 max_heap_size = max_heap - nursery_size;
804 * ######################################################################
805 * ######## Macros and function declarations.
806 * ######################################################################
810 align_pointer (void *ptr)
812 mword p = (mword)ptr;
813 p += sizeof (gpointer) - 1;
814 p &= ~ (sizeof (gpointer) - 1);
818 typedef SgenGrayQueue GrayQueue;
820 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
821 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
823 /* forward declarations */
824 static int stop_world (int generation);
825 static int restart_world (int generation);
826 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
827 static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
828 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
829 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
830 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
831 static void report_finalizer_roots (void);
832 static void report_registered_roots (void);
833 static void find_pinning_ref_from_thread (char *obj, size_t size);
834 static void update_current_thread_stack (void *start);
835 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
836 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
837 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
838 static void null_links_for_domain (MonoDomain *domain, int generation);
839 static gboolean alloc_fragment_for_size (size_t size);
840 static int alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size);
841 static void clear_nursery_fragments (char *next);
842 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
843 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
844 static void optimize_pin_queue (int start_slot);
845 static void clear_remsets (void);
846 static void clear_tlabs (void);
847 static void sort_addresses (void **array, int size);
848 static gboolean drain_gray_stack (GrayQueue *queue, int max_objs);
849 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
850 static gboolean need_major_collection (mword space_needed);
851 static void major_collection (const char *reason);
853 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
855 void describe_ptr (char *ptr);
856 void check_object (char *start);
858 static void check_consistency (void);
859 static void check_major_refs (void);
860 static void check_scan_starts (void);
861 static void check_for_xdomain_refs (void);
862 static void dump_heap (const char *type, int num, const char *reason);
864 void mono_gc_scan_for_specific_ref (MonoObject *key);
866 static void init_stats (void);
868 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
869 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
870 static void null_ephemerons_for_domain (MonoDomain *domain);
872 SgenMajorCollector major_collector;
874 #include "sgen-pinning.c"
875 #include "sgen-pinning-stats.c"
876 #include "sgen-gray.c"
877 #include "sgen-workers.c"
878 #include "sgen-cardtable.c"
880 /* Root bitmap descriptors are simpler: the lower three bits describe the type
881 * and we either have 30/62 bitmap bits or nibble-based run-length,
882 * or a complex descriptor, or a user defined marker function.
885 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
890 ROOT_DESC_TYPE_MASK = 0x7,
891 ROOT_DESC_TYPE_SHIFT = 3,
894 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
896 #define MAX_USER_DESCRIPTORS 16
898 static gsize* complex_descriptors = NULL;
899 static int complex_descriptors_size = 0;
900 static int complex_descriptors_next = 0;
901 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
902 static int user_descriptors_next = 0;
905 alloc_complex_descriptor (gsize *bitmap, int numbits)
909 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
910 nwords = numbits / GC_BITS_PER_WORD + 1;
913 res = complex_descriptors_next;
914 /* linear search, so we don't have duplicates with domain load/unload
915 * this should not be performance critical or we'd have bigger issues
916 * (the number and size of complex descriptors should be small).
918 for (i = 0; i < complex_descriptors_next; ) {
919 if (complex_descriptors [i] == nwords) {
921 for (j = 0; j < nwords - 1; ++j) {
922 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
932 i += complex_descriptors [i];
934 if (complex_descriptors_next + nwords > complex_descriptors_size) {
935 int new_size = complex_descriptors_size * 2 + nwords;
936 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
937 complex_descriptors_size = new_size;
939 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
940 complex_descriptors_next += nwords;
941 complex_descriptors [res] = nwords;
942 for (i = 0; i < nwords - 1; ++i) {
943 complex_descriptors [res + 1 + i] = bitmap [i];
944 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
951 mono_sgen_get_complex_descriptor (GCVTable *vt)
953 return complex_descriptors + (vt->desc >> LOW_TYPE_BITS);
957 * Descriptor builders.
960 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
962 return (void*) DESC_TYPE_RUN_LENGTH;
966 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
968 int first_set = -1, num_set = 0, last_set = -1, i;
970 size_t stored_size = obj_size;
971 for (i = 0; i < numbits; ++i) {
972 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
980 * We don't encode the size of types that don't contain
981 * references because they might not be aligned, i.e. the
982 * bottom two bits might be set, which would clash with the
983 * bits we need to encode the descriptor type. Since we don't
984 * use the encoded size to skip objects, other than for
985 * processing remsets, in which case only the positions of
986 * references are relevant, this is not a problem.
989 return (void*)DESC_TYPE_RUN_LENGTH;
990 g_assert (!(stored_size & 0x3));
991 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
992 /* check run-length encoding first: one byte offset, one byte number of pointers
993 * on 64 bit archs, we can have 3 runs, just one on 32.
994 * It may be better to use nibbles.
997 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
998 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1000 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1001 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
1002 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));
1003 return (void*) desc;
1005 /* we know the 2-word header is ptr-free */
1006 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1007 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1008 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1009 return (void*) desc;
1012 /* we know the 2-word header is ptr-free */
1013 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1014 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1015 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1016 return (void*) desc;
1018 /* it's a complex object ... */
1019 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1020 return (void*) desc;
1023 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1025 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1027 int first_set = -1, num_set = 0, last_set = -1, i;
1028 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1029 for (i = 0; i < numbits; ++i) {
1030 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1037 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
1039 return (void*)DESC_TYPE_RUN_LENGTH;
1040 if (elem_size <= MAX_ELEMENT_SIZE) {
1041 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1043 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1045 /* Note: we also handle structs with just ref fields */
1046 if (num_set * sizeof (gpointer) == elem_size) {
1047 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1049 /* FIXME: try run-len first */
1050 /* Note: we can't skip the object header here, because it's not present */
1051 if (last_set <= SMALL_BITMAP_SIZE) {
1052 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1055 /* it's am array of complex structs ... */
1056 desc = DESC_TYPE_COMPLEX_ARR;
1057 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1058 return (void*) desc;
1061 /* Return the bitmap encoded by a descriptor */
1063 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1065 mword d = (mword)descr;
1069 case DESC_TYPE_RUN_LENGTH: {
1070 int first_set = (d >> 16) & 0xff;
1071 int num_set = (d >> 24) & 0xff;
1074 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1076 for (i = first_set; i < first_set + num_set; ++i)
1077 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1079 *numbits = first_set + num_set;
1083 case DESC_TYPE_SMALL_BITMAP:
1084 bitmap = g_new0 (gsize, 1);
1086 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1088 *numbits = GC_BITS_PER_WORD;
1092 g_assert_not_reached ();
1097 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1099 MonoObject *o = (MonoObject*)(obj);
1100 MonoObject *ref = (MonoObject*)*(ptr);
1101 int offset = (char*)(ptr) - (char*)o;
1103 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1105 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1107 if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1108 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1110 /* Thread.cached_culture_info */
1111 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1112 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1113 !strcmp(o->vtable->klass->name_space, "System") &&
1114 !strcmp(o->vtable->klass->name, "Object[]"))
1117 * 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
1118 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1119 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1120 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1121 * 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
1122 * 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
1123 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1124 * 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
1125 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1127 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1128 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1129 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1130 !strcmp (o->vtable->klass->name, "MemoryStream"))
1132 /* append_job() in threadpool.c */
1133 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1134 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1135 !strcmp (o->vtable->klass->name_space, "System") &&
1136 !strcmp (o->vtable->klass->name, "Object[]") &&
1137 mono_thread_pool_is_queue_array ((MonoArray*) o))
1143 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1145 MonoObject *o = (MonoObject*)(obj);
1146 MonoObject *ref = (MonoObject*)*(ptr);
1147 int offset = (char*)(ptr) - (char*)o;
1149 MonoClassField *field;
1152 if (!ref || ref->vtable->domain == domain)
1154 if (is_xdomain_ref_allowed (ptr, obj, domain))
1158 for (class = o->vtable->klass; class; class = class->parent) {
1161 for (i = 0; i < class->field.count; ++i) {
1162 if (class->fields[i].offset == offset) {
1163 field = &class->fields[i];
1171 if (ref->vtable->klass == mono_defaults.string_class)
1172 str = mono_string_to_utf8 ((MonoString*)ref);
1175 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1176 o, o->vtable->klass->name_space, o->vtable->klass->name,
1177 offset, field ? field->name : "",
1178 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1179 mono_gc_scan_for_specific_ref (o);
1185 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1188 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1190 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1192 #include "sgen-scan-object.h"
1196 #define HANDLE_PTR(ptr,obj) do { \
1197 if ((MonoObject*)*(ptr) == key) { \
1198 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1199 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1204 scan_object_for_specific_ref (char *start, MonoObject *key)
1208 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1211 #include "sgen-scan-object.h"
1215 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1217 while (start < end) {
1221 if (!*(void**)start) {
1222 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1227 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1233 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1235 callback (obj, size, data);
1242 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1244 scan_object_for_specific_ref (obj, key);
1248 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1252 g_print ("found ref to %p in root record %p\n", key, root);
1255 static MonoObject *check_key = NULL;
1256 static RootRecord *check_root = NULL;
1259 check_root_obj_specific_ref_from_marker (void **obj)
1261 check_root_obj_specific_ref (check_root, check_key, *obj);
1265 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1270 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1271 for (root = roots_hash [root_type][i]; root; root = root->next) {
1272 void **start_root = (void**)root->start_root;
1273 mword desc = root->root_desc;
1277 switch (desc & ROOT_DESC_TYPE_MASK) {
1278 case ROOT_DESC_BITMAP:
1279 desc >>= ROOT_DESC_TYPE_SHIFT;
1282 check_root_obj_specific_ref (root, key, *start_root);
1287 case ROOT_DESC_COMPLEX: {
1288 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1289 int bwords = (*bitmap_data) - 1;
1290 void **start_run = start_root;
1292 while (bwords-- > 0) {
1293 gsize bmap = *bitmap_data++;
1294 void **objptr = start_run;
1297 check_root_obj_specific_ref (root, key, *objptr);
1301 start_run += GC_BITS_PER_WORD;
1305 case ROOT_DESC_USER: {
1306 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1307 marker (start_root, check_root_obj_specific_ref_from_marker);
1310 case ROOT_DESC_RUN_LEN:
1311 g_assert_not_reached ();
1313 g_assert_not_reached ();
1322 mono_gc_scan_for_specific_ref (MonoObject *key)
1327 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1328 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1330 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1332 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1334 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1335 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1337 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1338 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1339 void **ptr = (void**)root->start_root;
1341 while (ptr < (void**)root->end_root) {
1342 check_root_obj_specific_ref (root, *ptr, key);
1350 clear_current_nursery_fragment (char *next)
1352 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1353 g_assert (next <= nursery_frag_real_end);
1354 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", next, nursery_frag_real_end));
1355 memset (next, 0, nursery_frag_real_end - next);
1359 /* Clear all remaining nursery fragments */
1361 clear_nursery_fragments (char *next)
1364 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1365 clear_current_nursery_fragment (next);
1366 for (frag = nursery_fragments; frag; frag = frag->next) {
1367 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_start, frag->fragment_end));
1368 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1374 need_remove_object_for_domain (char *start, MonoDomain *domain)
1376 if (mono_object_domain (start) == domain) {
1377 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1378 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1385 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1387 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1388 if (vt->klass == mono_defaults.internal_thread_class)
1389 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1390 /* The object could be a proxy for an object in the domain
1392 if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1393 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1395 /* The server could already have been zeroed out, so
1396 we need to check for that, too. */
1397 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1398 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1400 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1405 static MonoDomain *check_domain = NULL;
1408 check_obj_not_in_domain (void **o)
1410 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1414 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1418 check_domain = domain;
1419 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1420 for (root = roots_hash [root_type][i]; root; root = root->next) {
1421 void **start_root = (void**)root->start_root;
1422 mword desc = root->root_desc;
1424 /* The MonoDomain struct is allowed to hold
1425 references to objects in its own domain. */
1426 if (start_root == (void**)domain)
1429 switch (desc & ROOT_DESC_TYPE_MASK) {
1430 case ROOT_DESC_BITMAP:
1431 desc >>= ROOT_DESC_TYPE_SHIFT;
1433 if ((desc & 1) && *start_root)
1434 check_obj_not_in_domain (*start_root);
1439 case ROOT_DESC_COMPLEX: {
1440 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1441 int bwords = (*bitmap_data) - 1;
1442 void **start_run = start_root;
1444 while (bwords-- > 0) {
1445 gsize bmap = *bitmap_data++;
1446 void **objptr = start_run;
1448 if ((bmap & 1) && *objptr)
1449 check_obj_not_in_domain (*objptr);
1453 start_run += GC_BITS_PER_WORD;
1457 case ROOT_DESC_USER: {
1458 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1459 marker (start_root, check_obj_not_in_domain);
1462 case ROOT_DESC_RUN_LEN:
1463 g_assert_not_reached ();
1465 g_assert_not_reached ();
1469 check_domain = NULL;
1473 check_for_xdomain_refs (void)
1477 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1478 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1480 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1482 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1483 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1487 clear_domain_process_object (char *obj, MonoDomain *domain)
1491 process_object_for_domain_clearing (obj, domain);
1492 remove = need_remove_object_for_domain (obj, domain);
1494 if (remove && ((MonoObject*)obj)->synchronisation) {
1495 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1497 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1504 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1506 if (clear_domain_process_object (obj, domain))
1507 memset (obj, 0, size);
1511 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1513 clear_domain_process_object (obj, domain);
1517 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1519 if (need_remove_object_for_domain (obj, domain))
1520 major_collector.free_non_pinned_object (obj, size);
1524 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1526 if (need_remove_object_for_domain (obj, domain))
1527 major_collector.free_pinned_object (obj, size);
1531 * When appdomains are unloaded we can easily remove objects that have finalizers,
1532 * but all the others could still be present in random places on the heap.
1533 * We need a sweep to get rid of them even though it's going to be costly
1535 * The reason we need to remove them is because we access the vtable and class
1536 * structures to know the object size and the reference bitmap: once the domain is
1537 * unloaded the point to random memory.
1540 mono_gc_clear_domain (MonoDomain * domain)
1542 LOSObject *bigobj, *prev;
1547 clear_nursery_fragments (nursery_next);
1549 if (xdomain_checks && domain != mono_get_root_domain ()) {
1550 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1551 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1552 check_for_xdomain_refs ();
1555 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1556 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1558 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1559 to memory returned to the OS.*/
1560 null_ephemerons_for_domain (domain);
1562 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1563 null_links_for_domain (domain, i);
1565 /* We need two passes over major and large objects because
1566 freeing such objects might give their memory back to the OS
1567 (in the case of large objects) or obliterate its vtable
1568 (pinned objects with major-copying or pinned and non-pinned
1569 objects with major-mark&sweep), but we might need to
1570 dereference a pointer from an object to another object if
1571 the first object is a proxy. */
1572 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1573 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1574 clear_domain_process_object (bigobj->data, domain);
1577 for (bigobj = los_object_list; bigobj;) {
1578 if (need_remove_object_for_domain (bigobj->data, domain)) {
1579 LOSObject *to_free = bigobj;
1581 prev->next = bigobj->next;
1583 los_object_list = bigobj->next;
1584 bigobj = bigobj->next;
1585 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1587 mono_sgen_los_free_object (to_free);
1591 bigobj = bigobj->next;
1593 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1594 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1600 global_remset_cache_clear (void)
1602 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1606 * Tries to check if a given remset location was already added to the global remset.
1609 * A 2 entry, LRU cache of recently saw location remsets.
1611 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1613 * Returns TRUE is the element was added..
1616 global_remset_location_was_not_added (gpointer ptr)
1619 gpointer first = global_remset_cache [0], second;
1621 HEAVY_STAT (++stat_global_remsets_discarded);
1625 second = global_remset_cache [1];
1627 if (second == ptr) {
1628 /*Move the second to the front*/
1629 global_remset_cache [0] = second;
1630 global_remset_cache [1] = first;
1632 HEAVY_STAT (++stat_global_remsets_discarded);
1636 global_remset_cache [0] = second;
1637 global_remset_cache [1] = ptr;
1642 * mono_sgen_add_to_global_remset:
1644 * The global remset contains locations which point into newspace after
1645 * a minor collection. This can happen if the objects they point to are pinned.
1647 * LOCKING: If called from a parallel collector, the global remset
1648 * lock must be held. For serial collectors that is not necessary.
1651 mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr)
1654 gboolean lock = major_collector.is_parallel;
1656 if (use_cardtable) {
1657 sgen_card_table_mark_address ((mword)ptr);
1661 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1666 if (!global_remset_location_was_not_added (ptr))
1669 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1670 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1672 HEAVY_STAT (++stat_global_remsets_added);
1675 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1676 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1678 if (global_remset->store_next + 3 < global_remset->end_set) {
1679 *(global_remset->store_next++) = (mword)ptr;
1682 rs = alloc_global_remset (alc, global_remset->end_set - global_remset->data, NULL);
1683 rs->next = global_remset;
1685 *(global_remset->store_next++) = (mword)ptr;
1688 int global_rs_size = 0;
1690 for (rs = global_remset; rs; rs = rs->next) {
1691 global_rs_size += rs->store_next - rs->data;
1693 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1698 UNLOCK_GLOBAL_REMSET;
1704 * Scan objects in the gray stack until the stack is empty. This should be called
1705 * frequently after each object is copied, to achieve better locality and cache
1709 drain_gray_stack (GrayQueue *queue, int max_objs)
1713 if (current_collection_generation == GENERATION_NURSERY) {
1715 GRAY_OBJECT_DEQUEUE (queue, obj);
1718 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1719 major_collector.minor_scan_object (obj, queue);
1724 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1728 for (i = 0; i != max_objs; ++i) {
1729 GRAY_OBJECT_DEQUEUE (queue, obj);
1732 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1733 major_collector.major_scan_object (obj, queue);
1735 } while (max_objs < 0);
1741 * Addresses from start to end are already sorted. This function finds
1742 * the object header for each address and pins the object. The
1743 * addresses must be inside the passed section. The (start of the)
1744 * address array is overwritten with the addresses of the actually
1745 * pinned objects. Return the number of pinned objects.
1748 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1753 void *last_obj = NULL;
1754 size_t last_obj_size = 0;
1757 void **definitely_pinned = start;
1761 * The code below starts the search from an entry in scan_starts, which might point into a nursery
1762 * fragment containing random data. Clearing the nursery fragments takes a lot of time, and searching
1763 * though them too, so lay arrays at each location inside a fragment where a search can start:
1764 * - scan_locations[i]
1766 * - the start of each fragment (the last_obj + last_obj case)
1767 * The third encompasses the first two, since scan_locations [i] can't point inside a nursery fragment.
1769 for (frag = nursery_fragments; frag; frag = frag->next) {
1772 g_assert (frag->fragment_end - frag->fragment_start >= sizeof (MonoArray));
1773 o = (MonoArray*)frag->fragment_start;
1774 memset (o, 0, sizeof (MonoArray));
1775 g_assert (array_fill_vtable);
1776 o->obj.vtable = array_fill_vtable;
1777 /* Mark this as not a real object */
1778 o->obj.synchronisation = GINT_TO_POINTER (-1);
1779 o->max_length = (frag->fragment_end - frag->fragment_start) - sizeof (MonoArray);
1780 g_assert (frag->fragment_start + safe_object_get_size ((MonoObject*)o) == frag->fragment_end);
1783 while (start < end) {
1785 /* the range check should be reduntant */
1786 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1787 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1788 /* multiple pointers to the same object */
1789 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1793 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1794 g_assert (idx < section->num_scan_start);
1795 search_start = (void*)section->scan_starts [idx];
1796 if (!search_start || search_start > addr) {
1799 search_start = section->scan_starts [idx];
1800 if (search_start && search_start <= addr)
1803 if (!search_start || search_start > addr)
1804 search_start = start_nursery;
1806 if (search_start < last_obj)
1807 search_start = (char*)last_obj + last_obj_size;
1808 /* now addr should be in an object a short distance from search_start
1809 * Note that search_start must point to zeroed mem or point to an object.
1813 if (!*(void**)search_start) {
1814 /* Consistency check */
1816 for (frag = nursery_fragments; frag; frag = frag->next) {
1817 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1818 g_assert_not_reached ();
1822 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1825 last_obj = search_start;
1826 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1828 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1829 /* Marks the beginning of a nursery fragment, skip */
1831 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1832 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1833 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));
1834 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1835 pin_object (search_start);
1836 GRAY_OBJECT_ENQUEUE (queue, search_start);
1838 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1839 definitely_pinned [count] = search_start;
1844 /* skip to the next object */
1845 search_start = (void*)((char*)search_start + last_obj_size);
1846 } while (search_start <= addr);
1847 /* we either pinned the correct object or we ignored the addr because
1848 * it points to unused zeroed memory.
1854 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1855 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1856 GCRootReport report;
1858 for (idx = 0; idx < count; ++idx)
1859 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
1860 notify_gc_roots (&report);
1862 stat_pinned_objects += count;
1867 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1869 int num_entries = section->pin_queue_num_entries;
1871 void **start = section->pin_queue_start;
1873 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1874 section->data, section->next_data, queue);
1875 section->pin_queue_num_entries = reduced_to;
1877 section->pin_queue_start = NULL;
1883 mono_sgen_pin_object (void *object, GrayQueue *queue)
1885 if (major_collector.is_parallel) {
1887 /*object arrives pinned*/
1888 pin_stage_ptr (object);
1892 SGEN_PIN_OBJECT (object);
1893 pin_stage_ptr (object);
1896 GRAY_OBJECT_ENQUEUE (queue, object);
1899 /* Sort the addresses in array in increasing order.
1900 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1903 sort_addresses (void **array, int size)
1908 for (i = 1; i < size; ++i) {
1911 int parent = (child - 1) / 2;
1913 if (array [parent] >= array [child])
1916 tmp = array [parent];
1917 array [parent] = array [child];
1918 array [child] = tmp;
1924 for (i = size - 1; i > 0; --i) {
1927 array [i] = array [0];
1933 while (root * 2 + 1 <= end) {
1934 int child = root * 2 + 1;
1936 if (child < end && array [child] < array [child + 1])
1938 if (array [root] >= array [child])
1942 array [root] = array [child];
1943 array [child] = tmp;
1950 static G_GNUC_UNUSED void
1951 print_nursery_gaps (void* start_nursery, void *end_nursery)
1954 gpointer first = start_nursery;
1956 for (i = 0; i < next_pin_slot; ++i) {
1957 next = pin_queue [i];
1958 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1962 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1965 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1967 optimize_pin_queue (int start_slot)
1969 void **start, **cur, **end;
1970 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1971 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1972 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1973 if ((next_pin_slot - start_slot) > 1)
1974 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1975 start = cur = pin_queue + start_slot;
1976 end = pin_queue + next_pin_slot;
1979 while (*start == *cur && cur < end)
1983 next_pin_slot = start - pin_queue;
1984 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1985 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1990 * Scan the memory between start and end and queue values which could be pointers
1991 * to the area between start_nursery and end_nursery for later consideration.
1992 * Typically used for thread stacks.
1995 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1998 while (start < end) {
1999 if (*start >= start_nursery && *start < end_nursery) {
2001 * *start can point to the middle of an object
2002 * note: should we handle pointing at the end of an object?
2003 * pinning in C# code disallows pointing at the end of an object
2004 * but there is some small chance that an optimizing C compiler
2005 * may keep the only reference to an object by pointing
2006 * at the end of it. We ignore this small chance for now.
2007 * Pointers to the end of an object are indistinguishable
2008 * from pointers to the start of the next object in memory
2009 * so if we allow that we'd need to pin two objects...
2010 * We queue the pointer in an array, the
2011 * array will then be sorted and uniqued. This way
2012 * we can coalesce several pinning pointers and it should
2013 * be faster since we'd do a memory scan with increasing
2014 * addresses. Note: we can align the address to the allocation
2015 * alignment, so the unique process is more effective.
2017 mword addr = (mword)*start;
2018 addr &= ~(ALLOC_ALIGN - 1);
2019 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2020 pin_stage_ptr ((void*)addr);
2022 pin_stats_register_address ((char*)addr, pin_type);
2023 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
2028 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2032 * Debugging function: find in the conservative roots where @obj is being pinned.
2034 static G_GNUC_UNUSED void
2035 find_pinning_reference (char *obj, size_t size)
2039 char *endobj = obj + size;
2040 for (i = 0; i < roots_hash_size [0]; ++i) {
2041 for (root = roots_hash [0][i]; root; root = root->next) {
2042 /* if desc is non-null it has precise info */
2043 if (!root->root_desc) {
2044 char ** start = (char**)root->start_root;
2045 while (start < (char**)root->end_root) {
2046 if (*start >= obj && *start < endobj) {
2047 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));
2054 find_pinning_ref_from_thread (obj, size);
2058 * The first thing we do in a collection is to identify pinned objects.
2059 * This function considers all the areas of memory that need to be
2060 * conservatively scanned.
2063 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
2067 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]));
2068 /* objects pinned from the API are inside these roots */
2069 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2070 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2071 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2072 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2075 /* now deal with the thread stacks
2076 * in the future we should be able to conservatively scan only:
2077 * *) the cpu registers
2078 * *) the unmanaged stack frames
2079 * *) the _last_ managed stack frame
2080 * *) pointers slots in managed frames
2082 scan_thread_data (start_nursery, end_nursery, FALSE, queue);
2084 evacuate_pin_staging_area ();
2088 CopyOrMarkObjectFunc func;
2090 } UserCopyOrMarkData;
2092 static pthread_key_t user_copy_or_mark_key;
2095 init_user_copy_or_mark_key (void)
2097 pthread_key_create (&user_copy_or_mark_key, NULL);
2101 set_user_copy_or_mark_data (UserCopyOrMarkData *data)
2103 static pthread_once_t init_control = PTHREAD_ONCE_INIT;
2104 pthread_once (&init_control, init_user_copy_or_mark_key);
2106 pthread_setspecific (user_copy_or_mark_key, data);
2110 single_arg_user_copy_or_mark (void **obj)
2112 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
2114 data->func (obj, data->queue);
2118 * The memory area from start_root to end_root contains pointers to objects.
2119 * Their position is precisely described by @desc (this means that the pointer
2120 * can be either NULL or the pointer to the start of an object).
2121 * This functions copies them to to_space updates them.
2123 * This function is not thread-safe!
2126 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2128 switch (desc & ROOT_DESC_TYPE_MASK) {
2129 case ROOT_DESC_BITMAP:
2130 desc >>= ROOT_DESC_TYPE_SHIFT;
2132 if ((desc & 1) && *start_root) {
2133 copy_func (start_root, queue);
2134 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2135 drain_gray_stack (queue, -1);
2141 case ROOT_DESC_COMPLEX: {
2142 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2143 int bwords = (*bitmap_data) - 1;
2144 void **start_run = start_root;
2146 while (bwords-- > 0) {
2147 gsize bmap = *bitmap_data++;
2148 void **objptr = start_run;
2150 if ((bmap & 1) && *objptr) {
2151 copy_func (objptr, queue);
2152 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2153 drain_gray_stack (queue, -1);
2158 start_run += GC_BITS_PER_WORD;
2162 case ROOT_DESC_USER: {
2163 UserCopyOrMarkData data = { copy_func, queue };
2164 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2165 set_user_copy_or_mark_data (&data);
2166 marker (start_root, single_arg_user_copy_or_mark);
2167 set_user_copy_or_mark_data (NULL);
2170 case ROOT_DESC_RUN_LEN:
2171 g_assert_not_reached ();
2173 g_assert_not_reached ();
2178 reset_heap_boundaries (void)
2180 lowest_heap_address = ~(mword)0;
2181 highest_heap_address = 0;
2185 mono_sgen_update_heap_boundaries (mword low, mword high)
2190 old = lowest_heap_address;
2193 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2196 old = highest_heap_address;
2199 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2203 alloc_fragment (void)
2205 Fragment *frag = fragment_freelist;
2207 fragment_freelist = frag->next;
2211 frag = mono_sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
2217 add_fragment (char *start, char *end)
2221 fragment = alloc_fragment ();
2222 fragment->fragment_start = start;
2223 fragment->fragment_limit = start;
2224 fragment->fragment_end = end;
2225 fragment->next = nursery_fragments;
2226 nursery_fragments = fragment;
2229 /* size must be a power of 2 */
2231 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2233 /* Allocate twice the memory to be able to put the block on an aligned address */
2234 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2239 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2240 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2243 mono_sgen_free_os_memory (mem, aligned - mem);
2244 if (aligned + size < mem + size + alignment)
2245 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2251 * Allocate and setup the data structures needed to be able to allocate objects
2252 * in the nursery. The nursery is stored in nursery_section.
2255 alloc_nursery (void)
2257 GCMemSection *section;
2262 if (nursery_section)
2264 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2265 /* later we will alloc a larger area for the nursery but only activate
2266 * what we need. The rest will be used as expansion if we have too many pinned
2267 * objects in the existing nursery.
2269 /* FIXME: handle OOM */
2270 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2272 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2273 alloc_size = nursery_size;
2274 #ifdef SGEN_ALIGN_NURSERY
2275 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2277 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2279 nursery_start = data;
2280 nursery_end = nursery_start + nursery_size;
2281 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
2282 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));
2283 section->data = section->next_data = data;
2284 section->size = alloc_size;
2285 section->end_data = nursery_end;
2286 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2287 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2288 section->num_scan_start = scan_starts;
2289 section->block.role = MEMORY_ROLE_GEN0;
2290 section->block.next = NULL;
2292 nursery_section = section;
2294 /* Setup the single first large fragment */
2295 add_fragment (nursery_start, nursery_end);
2299 mono_gc_get_nursery (int *shift_bits, size_t *size)
2301 *size = nursery_size;
2302 #ifdef SGEN_ALIGN_NURSERY
2303 *shift_bits = DEFAULT_NURSERY_BITS;
2307 return nursery_start;
2311 mono_gc_precise_stack_mark_enabled (void)
2313 return !conservative_stack_mark;
2317 mono_gc_get_logfile (void)
2319 return mono_sgen_get_logfile ();
2323 report_finalizer_roots_list (FinalizeEntry *list)
2325 GCRootReport report;
2329 for (fin = list; fin; fin = fin->next) {
2332 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2334 notify_gc_roots (&report);
2338 report_finalizer_roots (void)
2340 report_finalizer_roots_list (fin_ready_list);
2341 report_finalizer_roots_list (critical_fin_list);
2344 static GCRootReport *root_report;
2347 single_arg_report_root (void **obj)
2350 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2354 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2356 switch (desc & ROOT_DESC_TYPE_MASK) {
2357 case ROOT_DESC_BITMAP:
2358 desc >>= ROOT_DESC_TYPE_SHIFT;
2360 if ((desc & 1) && *start_root) {
2361 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2367 case ROOT_DESC_COMPLEX: {
2368 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2369 int bwords = (*bitmap_data) - 1;
2370 void **start_run = start_root;
2372 while (bwords-- > 0) {
2373 gsize bmap = *bitmap_data++;
2374 void **objptr = start_run;
2376 if ((bmap & 1) && *objptr) {
2377 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2382 start_run += GC_BITS_PER_WORD;
2386 case ROOT_DESC_USER: {
2387 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2388 root_report = report;
2389 marker (start_root, single_arg_report_root);
2392 case ROOT_DESC_RUN_LEN:
2393 g_assert_not_reached ();
2395 g_assert_not_reached ();
2400 report_registered_roots_by_type (int root_type)
2402 GCRootReport report;
2406 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2407 for (root = roots_hash [root_type][i]; root; root = root->next) {
2408 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2409 precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
2412 notify_gc_roots (&report);
2416 report_registered_roots (void)
2418 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2419 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2423 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2427 for (fin = list; fin; fin = fin->next) {
2430 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2431 copy_func (&fin->object, queue);
2435 static mword fragment_total = 0;
2437 * We found a fragment of free memory in the nursery: memzero it and if
2438 * it is big enough, add it to the list of fragments that can be used for
2442 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2444 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2445 binary_protocol_empty (frag_start, frag_size);
2446 /* Not worth dealing with smaller fragments: need to tune */
2447 if (frag_size >= FRAGMENT_MIN_SIZE) {
2448 /* memsetting just the first chunk start is bound to provide better cache locality */
2449 if (nursery_clear_policy == CLEAR_AT_GC)
2450 memset (frag_start, 0, frag_size);
2452 add_fragment (frag_start, frag_end);
2453 fragment_total += frag_size;
2455 /* Clear unused fragments, pinning depends on this */
2456 /*TODO place an int[] here instead of the memset if size justify it*/
2457 memset (frag_start, 0, frag_size);
2462 generation_name (int generation)
2464 switch (generation) {
2465 case GENERATION_NURSERY: return "nursery";
2466 case GENERATION_OLD: return "old";
2467 default: g_assert_not_reached ();
2471 static DisappearingLinkHashTable*
2472 get_dislink_hash_table (int generation)
2474 switch (generation) {
2475 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2476 case GENERATION_OLD: return &major_disappearing_link_hash;
2477 default: g_assert_not_reached ();
2481 static FinalizeEntryHashTable*
2482 get_finalize_entry_hash_table (int generation)
2484 switch (generation) {
2485 case GENERATION_NURSERY: return &minor_finalizable_hash;
2486 case GENERATION_OLD: return &major_finalizable_hash;
2487 default: g_assert_not_reached ();
2491 static MonoObject **finalized_array = NULL;
2492 static int finalized_array_capacity = 0;
2493 static int finalized_array_entries = 0;
2496 bridge_register_finalized_object (MonoObject *object)
2498 if (!finalized_array)
2501 if (finalized_array_entries >= finalized_array_capacity) {
2502 MonoObject **new_array;
2503 g_assert (finalized_array_entries == finalized_array_capacity);
2504 finalized_array_capacity *= 2;
2505 new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2506 memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
2507 mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
2508 finalized_array = new_array;
2510 finalized_array [finalized_array_entries++] = object;
2514 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2519 int ephemeron_rounds = 0;
2521 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2524 * We copied all the reachable objects. Now it's the time to copy
2525 * the objects that were not referenced by the roots, but by the copied objects.
2526 * we built a stack of objects pointed to by gray_start: they are
2527 * additional roots and we may add more items as we go.
2528 * We loop until gray_start == gray_objects which means no more objects have
2529 * been added. Note this is iterative: no recursion is involved.
2530 * We need to walk the LO list as well in search of marked big objects
2531 * (use a flag since this is needed only on major collections). We need to loop
2532 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2533 * To achieve better cache locality and cache usage, we drain the gray stack
2534 * frequently, after each object is copied, and just finish the work here.
2536 drain_gray_stack (queue, -1);
2538 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2541 We must clear weak links that don't track resurrection before processing object ready for
2542 finalization so they can be cleared before that.
2544 null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
2545 if (generation == GENERATION_OLD)
2546 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
2548 if (finalized_array == NULL && mono_sgen_need_bridge_processing ()) {
2549 finalized_array_capacity = 32;
2550 finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2552 finalized_array_entries = 0;
2554 /* walk the finalization queue and move also the objects that need to be
2555 * finalized: use the finalized objects as new roots so the objects they depend
2556 * on are also not reclaimed. As with the roots above, only objects in the nursery
2557 * are marked/copied.
2558 * We need a loop here, since objects ready for finalizers may reference other objects
2559 * that are fin-ready. Speedup with a flag?
2564 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2565 * before processing finalizable objects to avoid finalizing reachable values.
2567 * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2568 * while they are been finalized.
2570 int done_with_ephemerons = 0;
2572 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2573 drain_gray_stack (queue, -1);
2575 } while (!done_with_ephemerons);
2577 fin_ready = num_ready_finalizers;
2578 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2579 if (generation == GENERATION_OLD)
2580 finalize_in_range (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
2582 if (fin_ready != num_ready_finalizers) {
2584 if (finalized_array != NULL)
2585 mono_sgen_bridge_processing (finalized_array_entries, finalized_array);
2588 /* drain the new stack that might have been created */
2589 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2590 drain_gray_stack (queue, -1);
2591 } while (fin_ready != num_ready_finalizers);
2593 if (mono_sgen_need_bridge_processing ())
2594 g_assert (num_loops <= 1);
2597 * Clear ephemeron pairs with unreachable keys.
2598 * We pass the copy func so we can figure out if an array was promoted or not.
2600 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2603 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));
2606 * handle disappearing links
2607 * Note we do this after checking the finalization queue because if an object
2608 * survives (at least long enough to be finalized) we don't clear the link.
2609 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2610 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2613 g_assert (gray_object_queue_is_empty (queue));
2615 null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
2616 if (generation == GENERATION_OLD)
2617 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
2618 if (gray_object_queue_is_empty (queue))
2620 drain_gray_stack (queue, -1);
2623 g_assert (gray_object_queue_is_empty (queue));
2627 mono_sgen_check_section_scan_starts (GCMemSection *section)
2630 for (i = 0; i < section->num_scan_start; ++i) {
2631 if (section->scan_starts [i]) {
2632 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2633 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2639 check_scan_starts (void)
2641 if (!do_scan_starts_check)
2643 mono_sgen_check_section_scan_starts (nursery_section);
2644 major_collector.check_scan_starts ();
2647 static int last_num_pinned = 0;
2650 build_nursery_fragments (void **start, int num_entries)
2652 char *frag_start, *frag_end;
2656 while (nursery_fragments) {
2657 Fragment *next = nursery_fragments->next;
2658 nursery_fragments->next = fragment_freelist;
2659 fragment_freelist = nursery_fragments;
2660 nursery_fragments = next;
2662 frag_start = nursery_start;
2664 /* clear scan starts */
2665 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2666 for (i = 0; i < num_entries; ++i) {
2667 frag_end = start [i];
2668 /* remove the pin bit from pinned objects */
2669 unpin_object (frag_end);
2670 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2671 frag_size = frag_end - frag_start;
2673 add_nursery_frag (frag_size, frag_start, frag_end);
2674 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)start [i]));
2675 frag_start = (char*)start [i] + frag_size;
2677 nursery_last_pinned_end = frag_start;
2678 frag_end = nursery_end;
2679 frag_size = frag_end - frag_start;
2681 add_nursery_frag (frag_size, frag_start, frag_end);
2682 if (!nursery_fragments) {
2683 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
2684 for (i = 0; i < num_entries; ++i) {
2685 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])));
2690 nursery_next = nursery_frag_real_end = NULL;
2692 /* Clear TLABs for all threads */
2697 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2701 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2702 for (root = roots_hash [root_type][i]; root; root = root->next) {
2703 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2704 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2710 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2712 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2716 mono_sgen_dump_section (GCMemSection *section, const char *type)
2718 char *start = section->data;
2719 char *end = section->data + section->size;
2720 char *occ_start = NULL;
2722 char *old_start = NULL; /* just for debugging */
2724 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2726 while (start < end) {
2730 if (!*(void**)start) {
2732 mono_sgen_dump_occupied (occ_start, start, section->data);
2735 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2738 g_assert (start < section->next_data);
2743 vt = (GCVTable*)LOAD_VTABLE (start);
2746 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2749 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2750 start - section->data,
2751 vt->klass->name_space, vt->klass->name,
2759 mono_sgen_dump_occupied (occ_start, start, section->data);
2761 fprintf (heap_dump_file, "</section>\n");
2765 dump_object (MonoObject *obj, gboolean dump_location)
2767 static char class_name [1024];
2769 MonoClass *class = mono_object_class (obj);
2773 * Python's XML parser is too stupid to parse angle brackets
2774 * in strings, so we just ignore them;
2777 while (class->name [i] && j < sizeof (class_name) - 1) {
2778 if (!strchr ("<>\"", class->name [i]))
2779 class_name [j++] = class->name [i];
2782 g_assert (j < sizeof (class_name));
2785 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2786 class->name_space, class_name,
2787 safe_object_get_size (obj));
2788 if (dump_location) {
2789 const char *location;
2790 if (ptr_in_nursery (obj))
2791 location = "nursery";
2792 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2796 fprintf (heap_dump_file, " location=\"%s\"", location);
2798 fprintf (heap_dump_file, "/>\n");
2802 dump_heap (const char *type, int num, const char *reason)
2807 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2809 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2810 fprintf (heap_dump_file, ">\n");
2811 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2812 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2813 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2814 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2815 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2817 fprintf (heap_dump_file, "<pinned-objects>\n");
2818 for (list = pinned_objects; list; list = list->next)
2819 dump_object (list->obj, TRUE);
2820 fprintf (heap_dump_file, "</pinned-objects>\n");
2822 mono_sgen_dump_section (nursery_section, "nursery");
2824 major_collector.dump_heap (heap_dump_file);
2826 fprintf (heap_dump_file, "<los>\n");
2827 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2828 dump_object ((MonoObject*)bigobj->data, FALSE);
2829 fprintf (heap_dump_file, "</los>\n");
2831 fprintf (heap_dump_file, "</collection>\n");
2835 mono_sgen_register_moved_object (void *obj, void *destination)
2837 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2839 /* FIXME: handle this for parallel collector */
2840 g_assert (!major_collector.is_parallel);
2842 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2843 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2844 moved_objects_idx = 0;
2846 moved_objects [moved_objects_idx++] = obj;
2847 moved_objects [moved_objects_idx++] = destination;
2853 static gboolean inited = FALSE;
2858 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2859 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2860 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2861 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2862 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2863 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2864 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2865 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2866 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2868 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2869 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2870 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2871 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2872 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2873 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2874 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2875 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2876 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2877 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2878 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2879 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2880 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2882 mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2884 #ifdef HEAVY_STATISTICS
2885 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2886 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2887 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2888 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2889 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2890 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2891 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2892 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2894 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2895 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2896 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2897 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2898 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2900 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2901 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2902 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2903 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2905 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2906 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2908 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2909 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2910 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2912 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
2913 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
2915 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2916 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2917 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2918 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2919 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2920 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2921 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2922 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2923 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2929 static gboolean need_calculate_minor_collection_allowance;
2931 static int last_collection_old_num_major_sections;
2932 static mword last_collection_los_memory_usage = 0;
2933 static mword last_collection_old_los_memory_usage;
2934 static mword last_collection_los_memory_alloced;
2937 reset_minor_collection_allowance (void)
2939 need_calculate_minor_collection_allowance = TRUE;
2943 try_calculate_minor_collection_allowance (gboolean overwrite)
2945 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2946 mword los_memory_saved;
2949 g_assert (need_calculate_minor_collection_allowance);
2951 if (!need_calculate_minor_collection_allowance)
2954 if (!*major_collector.have_swept) {
2956 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2960 num_major_sections = major_collector.get_num_major_sections ();
2962 num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
2963 los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
2965 save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2968 * We aim to allow the allocation of as many sections as is
2969 * necessary to reclaim save_target sections in the next
2970 * collection. We assume the collection pattern won't change.
2971 * In the last cycle, we had num_major_sections_saved for
2972 * minor_collection_sections_alloced. Assuming things won't
2973 * change, this must be the same ratio as save_target for
2974 * allowance_target, i.e.
2976 * num_major_sections_saved save_target
2977 * --------------------------------- == ----------------
2978 * minor_collection_sections_alloced allowance_target
2982 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));
2984 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2986 if (major_collector.have_computed_minor_collection_allowance)
2987 major_collector.have_computed_minor_collection_allowance ();
2989 need_calculate_minor_collection_allowance = FALSE;
2993 need_major_collection (mword space_needed)
2995 mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
2996 return (space_needed > available_free_space ()) ||
2997 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
3001 mono_sgen_need_major_collection (mword space_needed)
3003 return need_major_collection (space_needed);
3007 job_gray_queue (WorkerData *worker_data)
3009 return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
3016 } ScanFromRemsetsJobData;
3019 job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
3021 ScanFromRemsetsJobData *job_data = job_data_untyped;
3023 scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
3028 CopyOrMarkObjectFunc func;
3032 } ScanFromRegisteredRootsJobData;
3035 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
3037 ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
3039 scan_from_registered_roots (job_data->func,
3040 job_data->heap_start, job_data->heap_end,
3041 job_data->root_type,
3042 job_gray_queue (worker_data));
3049 } ScanThreadDataJobData;
3052 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
3054 ScanThreadDataJobData *job_data = job_data_untyped;
3056 scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
3057 job_gray_queue (worker_data));
3061 * Collect objects in the nursery. Returns whether to trigger a major
3065 collect_nursery (size_t requested_size)
3067 gboolean needs_major;
3068 size_t max_garbage_amount;
3069 char *orig_nursery_next;
3070 ScanFromRemsetsJobData sfrjd;
3071 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3072 ScanThreadDataJobData stdjd;
3073 TV_DECLARE (all_atv);
3074 TV_DECLARE (all_btv);
3078 if (disable_minor_collections)
3081 mono_perfcounters->gc_collections0++;
3083 current_collection_generation = GENERATION_NURSERY;
3085 binary_protocol_collection (GENERATION_NURSERY);
3086 check_scan_starts ();
3090 orig_nursery_next = nursery_next;
3091 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3092 /* FIXME: optimize later to use the higher address where an object can be present */
3093 nursery_next = MAX (nursery_next, nursery_end);
3095 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)));
3096 max_garbage_amount = nursery_next - nursery_start;
3097 g_assert (nursery_section->size >= max_garbage_amount);
3099 /* world must be stopped already */
3100 TV_GETTIME (all_atv);
3103 /* Pinning no longer depends on clearing all nursery fragments */
3104 clear_current_nursery_fragment (orig_nursery_next);
3107 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3110 check_for_xdomain_refs ();
3112 nursery_section->next_data = nursery_next;
3114 major_collector.start_nursery_collection ();
3116 try_calculate_minor_collection_allowance (FALSE);
3118 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3119 workers_init_distribute_gray_queue ();
3122 mono_stats.minor_gc_count ++;
3124 global_remset_cache_clear ();
3126 /* pin from pinned handles */
3128 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
3129 pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3130 /* identify pinned objects */
3131 optimize_pin_queue (0);
3132 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3133 nursery_section->pin_queue_start = pin_queue;
3134 nursery_section->pin_queue_num_entries = next_pin_slot;
3136 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3137 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3138 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3140 if (consistency_check_at_minor_collection)
3141 check_consistency ();
3143 workers_start_all_workers ();
3146 * Walk all the roots and copy the young objects to the old
3147 * generation, starting from to_space.
3149 * The global remsets must be processed before the workers start
3150 * marking because they might add global remsets.
3152 scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3154 workers_start_marking ();
3156 sfrjd.heap_start = nursery_start;
3157 sfrjd.heap_end = nursery_next;
3158 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_remsets, &sfrjd);
3160 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3162 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3163 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3165 if (use_cardtable) {
3167 card_tables_collect_stats (TRUE);
3168 scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3170 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
3173 if (!major_collector.is_parallel)
3174 drain_gray_stack (&gray_queue, -1);
3176 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3177 report_registered_roots ();
3178 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3179 report_finalizer_roots ();
3181 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3183 /* registered roots, this includes static fields */
3184 scrrjd_normal.func = major_collector.copy_object;
3185 scrrjd_normal.heap_start = nursery_start;
3186 scrrjd_normal.heap_end = nursery_next;
3187 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3188 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
3190 scrrjd_wbarrier.func = major_collector.copy_object;
3191 scrrjd_wbarrier.heap_start = nursery_start;
3192 scrrjd_wbarrier.heap_end = nursery_next;
3193 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3194 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
3197 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3200 stdjd.heap_start = nursery_start;
3201 stdjd.heap_end = nursery_next;
3202 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
3205 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3208 if (major_collector.is_parallel) {
3209 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3210 workers_distribute_gray_queue_sections ();
3216 if (major_collector.is_parallel)
3217 g_assert (gray_object_queue_is_empty (&gray_queue));
3219 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3221 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3222 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3225 * The (single-threaded) finalization code might have done
3226 * some copying/marking so we can only reset the GC thread's
3227 * worker data here instead of earlier when we joined the
3230 if (major_collector.reset_worker_data)
3231 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3233 if (objects_pinned) {
3234 evacuate_pin_staging_area ();
3235 optimize_pin_queue (0);
3236 nursery_section->pin_queue_start = pin_queue;
3237 nursery_section->pin_queue_num_entries = next_pin_slot;
3240 /* walk the pin_queue, build up the fragment list of free memory, unmark
3241 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3244 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3245 build_nursery_fragments (pin_queue, next_pin_slot);
3246 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3248 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3249 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3251 if (consistency_check_at_minor_collection)
3252 check_major_refs ();
3254 major_collector.finish_nursery_collection ();
3256 TV_GETTIME (all_btv);
3257 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3260 dump_heap ("minor", num_minor_gcs - 1, NULL);
3262 /* prepare the pin queue for the next collection */
3263 last_num_pinned = next_pin_slot;
3265 if (fin_ready_list || critical_fin_list) {
3266 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3267 mono_gc_finalize_notify ();
3271 g_assert (gray_object_queue_is_empty (&gray_queue));
3274 card_tables_collect_stats (FALSE);
3276 check_scan_starts ();
3278 binary_protocol_flush_buffers (FALSE);
3280 /*objects are late pinned because of lack of memory, so a major is a good call*/
3281 needs_major = need_major_collection (0) || objects_pinned;
3282 current_collection_generation = -1;
3290 FinalizeEntry *list;
3291 } ScanFinalizerEntriesJobData;
3294 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
3296 ScanFinalizerEntriesJobData *job_data = job_data_untyped;
3298 scan_finalizer_entries (major_collector.copy_or_mark_object,
3300 job_gray_queue (worker_data));
3304 major_do_collection (const char *reason)
3306 LOSObject *bigobj, *prevbo;
3307 TV_DECLARE (all_atv);
3308 TV_DECLARE (all_btv);
3311 /* FIXME: only use these values for the precise scan
3312 * note that to_space pointers should be excluded anyway...
3314 char *heap_start = NULL;
3315 char *heap_end = (char*)-1;
3316 int old_next_pin_slot;
3317 ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3318 ScanThreadDataJobData stdjd;
3319 ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
3321 mono_perfcounters->gc_collections1++;
3323 last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3326 * A domain could have been freed, resulting in
3327 * los_memory_usage being less than last_collection_los_memory_usage.
3329 last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3330 last_collection_old_los_memory_usage = los_memory_usage;
3333 //count_ref_nonref_objs ();
3334 //consistency_check ();
3336 binary_protocol_collection (GENERATION_OLD);
3337 check_scan_starts ();
3338 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3339 workers_init_distribute_gray_queue ();
3342 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3344 mono_stats.major_gc_count ++;
3346 /* world must be stopped already */
3347 TV_GETTIME (all_atv);
3350 /* Pinning depends on this */
3351 clear_nursery_fragments (nursery_next);
3354 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3356 nursery_section->next_data = nursery_end;
3357 /* we should also coalesce scanning from sections close to each other
3358 * and deal with pointers outside of the sections later.
3361 if (major_collector.start_major_collection)
3362 major_collector.start_major_collection ();
3364 *major_collector.have_swept = FALSE;
3365 reset_minor_collection_allowance ();
3368 check_for_xdomain_refs ();
3370 /* The remsets are not useful for a major collection */
3372 global_remset_cache_clear ();
3374 card_table_clear ();
3378 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3379 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3380 optimize_pin_queue (0);
3383 * pin_queue now contains all candidate pointers, sorted and
3384 * uniqued. We must do two passes now to figure out which
3385 * objects are pinned.
3387 * The first is to find within the pin_queue the area for each
3388 * section. This requires that the pin_queue be sorted. We
3389 * also process the LOS objects and pinned chunks here.
3391 * The second, destructive, pass is to reduce the section
3392 * areas to pointers to the actually pinned objects.
3394 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3395 /* first pass for the sections */
3396 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3397 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3398 /* identify possible pointers to the insize of large objects */
3399 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3400 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3402 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3403 pin_object (bigobj->data);
3404 /* FIXME: only enqueue if object has references */
3405 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3407 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3408 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));
3411 /* second pass for the sections */
3412 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3413 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3414 old_next_pin_slot = next_pin_slot;
3417 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3418 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3419 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3421 major_collector.init_to_space ();
3423 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3424 main_gc_thread = pthread_self ();
3427 workers_start_all_workers ();
3428 workers_start_marking ();
3430 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3431 report_registered_roots ();
3433 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3435 /* registered roots, this includes static fields */
3436 scrrjd_normal.func = major_collector.copy_or_mark_object;
3437 scrrjd_normal.heap_start = heap_start;
3438 scrrjd_normal.heap_end = heap_end;
3439 scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3440 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
3442 scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
3443 scrrjd_wbarrier.heap_start = heap_start;
3444 scrrjd_wbarrier.heap_end = heap_end;
3445 scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3446 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
3449 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3452 stdjd.heap_start = heap_start;
3453 stdjd.heap_end = heap_end;
3454 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
3457 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3460 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3462 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3463 report_finalizer_roots ();
3465 /* scan the list of objects ready for finalization */
3466 sfejd_fin_ready.list = fin_ready_list;
3467 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_fin_ready);
3469 sfejd_critical_fin.list = critical_fin_list;
3470 workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_critical_fin);
3473 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3474 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3477 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3479 if (major_collector.is_parallel) {
3480 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3481 workers_distribute_gray_queue_sections ();
3487 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3488 main_gc_thread = NULL;
3491 if (major_collector.is_parallel)
3492 g_assert (gray_object_queue_is_empty (&gray_queue));
3494 /* all the objects in the heap */
3495 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3497 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3500 * The (single-threaded) finalization code might have done
3501 * some copying/marking so we can only reset the GC thread's
3502 * worker data here instead of earlier when we joined the
3505 if (major_collector.reset_worker_data)
3506 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3508 if (objects_pinned) {
3509 /*This is slow, but we just OOM'd*/
3510 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3511 evacuate_pin_staging_area ();
3512 optimize_pin_queue (0);
3513 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3517 reset_heap_boundaries ();
3518 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
3520 /* sweep the big objects list */
3522 for (bigobj = los_object_list; bigobj;) {
3523 if (object_is_pinned (bigobj->data)) {
3524 unpin_object (bigobj->data);
3525 mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3528 /* not referenced anywhere, so we can free it */
3530 prevbo->next = bigobj->next;
3532 los_object_list = bigobj->next;
3534 bigobj = bigobj->next;
3535 mono_sgen_los_free_object (to_free);
3539 bigobj = bigobj->next;
3543 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3545 mono_sgen_los_sweep ();
3548 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3550 major_collector.sweep ();
3553 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3555 /* walk the pin_queue, build up the fragment list of free memory, unmark
3556 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3559 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
3562 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3564 TV_GETTIME (all_btv);
3565 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3568 dump_heap ("major", num_major_gcs - 1, reason);
3570 /* prepare the pin queue for the next collection */
3572 if (fin_ready_list || critical_fin_list) {
3573 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3574 mono_gc_finalize_notify ();
3578 g_assert (gray_object_queue_is_empty (&gray_queue));
3580 try_calculate_minor_collection_allowance (TRUE);
3582 minor_collection_sections_alloced = 0;
3583 last_collection_los_memory_usage = los_memory_usage;
3585 major_collector.finish_major_collection ();
3587 check_scan_starts ();
3589 binary_protocol_flush_buffers (FALSE);
3591 //consistency_check ();
3595 major_collection (const char *reason)
3597 if (disable_major_collections) {
3598 collect_nursery (0);
3602 current_collection_generation = GENERATION_OLD;
3603 major_do_collection (reason);
3604 current_collection_generation = -1;
3608 sgen_collect_major_no_lock (const char *reason)
3610 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3612 major_collection (reason);
3614 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3618 * When deciding if it's better to collect or to expand, keep track
3619 * of how much garbage was reclaimed with the last collection: if it's too
3621 * This is called when we could not allocate a small object.
3623 static void __attribute__((noinline))
3624 minor_collect_or_expand_inner (size_t size)
3626 int do_minor_collection = 1;
3628 g_assert (nursery_section);
3629 if (do_minor_collection) {
3630 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3632 if (collect_nursery (size)) {
3633 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3634 major_collection ("minor overflow");
3635 /* keep events symmetric */
3636 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3638 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3640 /* this also sets the proper pointers for the next allocation */
3641 if (!alloc_fragment_for_size (size)) {
3643 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3644 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3645 for (i = 0; i < last_num_pinned; ++i) {
3646 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])));
3650 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3652 //report_internal_mem_usage ();
3656 * ######################################################################
3657 * ######## Memory allocation from the OS
3658 * ######################################################################
3659 * This section of code deals with getting memory from the OS and
3660 * allocating memory for GC-internal data structures.
3661 * Internal memory can be handled with a freelist for small objects.
3667 G_GNUC_UNUSED static void
3668 report_internal_mem_usage (void)
3670 printf ("Internal memory usage:\n");
3671 mono_sgen_report_internal_mem_usage ();
3672 printf ("Pinned memory usage:\n");
3673 major_collector.report_pinned_memory_usage ();
3677 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3678 * This must not require any lock.
3681 mono_sgen_alloc_os_memory (size_t size, int activate)
3684 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3686 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3687 size += pagesize - 1;
3688 size &= ~(pagesize - 1);
3689 ptr = mono_valloc (0, size, prot_flags);
3691 total_alloc += size;
3696 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3699 mono_sgen_free_os_memory (void *addr, size_t size)
3701 mono_vfree (addr, size);
3703 size += pagesize - 1;
3704 size &= ~(pagesize - 1);
3706 total_alloc -= size;
3710 * ######################################################################
3711 * ######## Object allocation
3712 * ######################################################################
3713 * This section of code deals with allocating memory for objects.
3714 * There are several ways:
3715 * *) allocate large objects
3716 * *) allocate normal objects
3717 * *) fast lock-free allocation
3718 * *) allocation of pinned objects
3722 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3724 /* remove from the list */
3726 prev->next = frag->next;
3728 nursery_fragments = frag->next;
3729 nursery_next = frag->fragment_start;
3730 nursery_frag_real_end = frag->fragment_end;
3732 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));
3733 frag->next = fragment_freelist;
3734 fragment_freelist = frag;
3738 * Allocate a new nursery fragment able to hold an object of size @size.
3739 * nursery_next and nursery_frag_real_end are set to the boundaries of the fragment.
3740 * Return TRUE if found, FALSE otherwise.
3743 alloc_fragment_for_size (size_t size)
3745 Fragment *frag, *prev;
3746 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3748 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3749 /* Clear the remaining space, pinning depends on this */
3750 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3754 for (frag = nursery_fragments; frag; frag = frag->next) {
3755 if (size <= (frag->fragment_end - frag->fragment_start)) {
3756 setup_fragment (frag, prev, size);
3765 * Same as alloc_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3766 * This improves nursery usage.
3769 alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3771 Fragment *frag, *prev, *min_prev;
3772 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));
3774 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3775 /* Clear the remaining space, pinning depends on this */
3776 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3779 min_prev = GINT_TO_POINTER (-1);
3782 for (frag = nursery_fragments; frag; frag = frag->next) {
3783 int frag_size = frag->fragment_end - frag->fragment_start;
3784 if (desired_size <= frag_size) {
3785 setup_fragment (frag, prev, desired_size);
3786 return desired_size;
3788 if (minimum_size <= frag_size)
3794 if (min_prev != GINT_TO_POINTER (-1)) {
3797 frag = min_prev->next;
3799 frag = nursery_fragments;
3801 frag_size = frag->fragment_end - frag->fragment_start;
3802 HEAVY_STAT (++stat_wasted_fragments_used);
3803 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3805 setup_fragment (frag, min_prev, minimum_size);
3813 alloc_degraded (MonoVTable *vtable, size_t size)
3815 if (need_major_collection (0)) {
3816 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3818 major_collection ("degraded overflow");
3820 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3823 return major_collector.alloc_degraded (vtable, size);
3827 * Provide a variant that takes just the vtable for small fixed-size objects.
3828 * The aligned size is already computed and stored in vt->gc_descr.
3829 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3830 * processing. We can keep track of where objects start, for example,
3831 * so when we scan the thread stacks for pinned objects, we can start
3832 * a search for the pinned object in SCAN_START_SIZE chunks.
3835 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3837 /* FIXME: handle OOM */
3842 HEAVY_STAT (++stat_objects_alloced);
3843 if (size <= MAX_SMALL_OBJ_SIZE)
3844 HEAVY_STAT (stat_bytes_alloced += size);
3846 HEAVY_STAT (stat_bytes_alloced_los += size);
3848 size = ALIGN_UP (size);
3850 g_assert (vtable->gc_descr);
3852 if (G_UNLIKELY (collect_before_allocs)) {
3853 static int alloc_count;
3855 InterlockedIncrement (&alloc_count);
3856 if (((alloc_count % collect_before_allocs) == 0) && nursery_section) {
3857 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3859 collect_nursery (0);
3861 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3862 if (!degraded_mode && !alloc_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
3864 g_assert_not_reached ();
3870 * We must already have the lock here instead of after the
3871 * fast path because we might be interrupted in the fast path
3872 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3873 * and we'll end up allocating an object in a fragment which
3874 * no longer belongs to us.
3876 * The managed allocator does not do this, but it's treated
3877 * specially by the world-stopping code.
3880 if (size > MAX_SMALL_OBJ_SIZE) {
3881 p = mono_sgen_los_alloc_large_inner (vtable, size);
3883 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3885 p = (void**)TLAB_NEXT;
3886 /* FIXME: handle overflow */
3887 new_next = (char*)p + size;
3888 TLAB_NEXT = new_next;
3890 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3894 * FIXME: We might need a memory barrier here so the change to tlab_next is
3895 * visible before the vtable store.
3898 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3899 binary_protocol_alloc (p , vtable, size);
3900 g_assert (*p == NULL);
3903 g_assert (TLAB_NEXT == new_next);
3910 /* there are two cases: the object is too big or we run out of space in the TLAB */
3911 /* we also reach here when the thread does its first allocation after a minor
3912 * collection, since the tlab_ variables are initialized to NULL.
3913 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3914 * objects that need finalizers can have the high bit set in their size
3915 * so the above check fails and we can readily add the object to the queue.
3916 * This avoids taking again the GC lock when registering, but this is moot when
3917 * doing thread-local allocation, so it may not be a good idea.
3919 g_assert (TLAB_NEXT == new_next);
3920 if (TLAB_NEXT >= TLAB_REAL_END) {
3922 * Run out of space in the TLAB. When this happens, some amount of space
3923 * remains in the TLAB, but not enough to satisfy the current allocation
3924 * request. Currently, we retire the TLAB in all cases, later we could
3925 * keep it if the remaining space is above a treshold, and satisfy the
3926 * allocation directly from the nursery.
3929 /* when running in degraded mode, we continue allocing that way
3930 * for a while, to decrease the number of useless nursery collections.
3932 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3933 p = alloc_degraded (vtable, size);
3934 binary_protocol_alloc_degraded (p, vtable, size);
3938 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3939 if (size > tlab_size) {
3940 /* Allocate directly from the nursery */
3941 if (nursery_next + size >= nursery_frag_real_end) {
3942 if (!alloc_fragment_for_size (size)) {
3943 minor_collect_or_expand_inner (size);
3944 if (degraded_mode) {
3945 p = alloc_degraded (vtable, size);
3946 binary_protocol_alloc_degraded (p, vtable, size);
3952 p = (void*)nursery_next;
3953 nursery_next += size;
3954 if (nursery_next > nursery_frag_real_end) {
3959 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3960 memset (p, 0, size);
3963 int alloc_size = tlab_size;
3964 int available_in_nursery = nursery_frag_real_end - nursery_next;
3966 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3968 if (alloc_size >= available_in_nursery) {
3969 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3970 alloc_size = available_in_nursery;
3972 alloc_size = alloc_fragment_for_size_range (tlab_size, size);
3974 alloc_size = tlab_size;
3975 minor_collect_or_expand_inner (tlab_size);
3976 if (degraded_mode) {
3977 p = alloc_degraded (vtable, size);
3978 binary_protocol_alloc_degraded (p, vtable, size);
3985 /* Allocate a new TLAB from the current nursery fragment */
3986 TLAB_START = nursery_next;
3987 nursery_next += alloc_size;
3988 TLAB_NEXT = TLAB_START;
3989 TLAB_REAL_END = TLAB_START + alloc_size;
3990 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3992 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3993 memset (TLAB_START, 0, alloc_size);
3996 /* Allocate from the TLAB */
3997 p = (void*)TLAB_NEXT;
3999 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4001 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4004 /* Reached tlab_temp_end */
4006 /* record the scan start so we can find pinned objects more easily */
4007 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4008 /* we just bump tlab_temp_end as well */
4009 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4010 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4015 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4016 binary_protocol_alloc (p, vtable, size);
4024 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4030 size = ALIGN_UP (size);
4032 g_assert (vtable->gc_descr);
4033 if (size <= MAX_SMALL_OBJ_SIZE) {
4034 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4036 p = (void**)TLAB_NEXT;
4037 /* FIXME: handle overflow */
4038 new_next = (char*)p + size;
4039 TLAB_NEXT = new_next;
4041 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4045 * FIXME: We might need a memory barrier here so the change to tlab_next is
4046 * visible before the vtable store.
4049 HEAVY_STAT (++stat_objects_alloced);
4050 HEAVY_STAT (stat_bytes_alloced += size);
4052 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4053 binary_protocol_alloc (p, vtable, size);
4054 g_assert (*p == NULL);
4057 g_assert (TLAB_NEXT == new_next);
4066 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4069 #ifndef DISABLE_CRITICAL_REGION
4071 ENTER_CRITICAL_REGION;
4072 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4074 EXIT_CRITICAL_REGION;
4077 EXIT_CRITICAL_REGION;
4080 res = mono_gc_alloc_obj_nolock (vtable, size);
4082 if (G_UNLIKELY (!res))
4083 return mono_gc_out_of_memory (size);
4088 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4091 #ifndef DISABLE_CRITICAL_REGION
4093 ENTER_CRITICAL_REGION;
4094 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4096 arr->max_length = max_length;
4097 EXIT_CRITICAL_REGION;
4100 EXIT_CRITICAL_REGION;
4105 arr = mono_gc_alloc_obj_nolock (vtable, size);
4106 if (G_UNLIKELY (!arr)) {
4108 return mono_gc_out_of_memory (size);
4111 arr->max_length = max_length;
4119 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4122 MonoArrayBounds *bounds;
4126 arr = mono_gc_alloc_obj_nolock (vtable, size);
4127 if (G_UNLIKELY (!arr)) {
4129 return mono_gc_out_of_memory (size);
4132 arr->max_length = max_length;
4134 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4135 arr->bounds = bounds;
4143 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4146 #ifndef DISABLE_CRITICAL_REGION
4148 ENTER_CRITICAL_REGION;
4149 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4152 EXIT_CRITICAL_REGION;
4155 EXIT_CRITICAL_REGION;
4160 str = mono_gc_alloc_obj_nolock (vtable, size);
4161 if (G_UNLIKELY (!str)) {
4163 return mono_gc_out_of_memory (size);
4174 * To be used for interned strings and possibly MonoThread, reflection handles.
4175 * We may want to explicitly free these objects.
4178 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4181 size = ALIGN_UP (size);
4184 if (size > MAX_SMALL_OBJ_SIZE) {
4185 /* large objects are always pinned anyway */
4186 p = mono_sgen_los_alloc_large_inner (vtable, size);
4188 DEBUG (9, g_assert (vtable->klass->inited));
4189 p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
4192 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4193 binary_protocol_alloc_pinned (p, vtable, size);
4201 mono_gc_alloc_mature (MonoVTable *vtable)
4204 size_t size = ALIGN_UP (vtable->klass->instance_size);
4206 res = alloc_degraded (vtable, size);
4209 if (G_UNLIKELY (vtable->klass->has_finalize))
4210 mono_object_register_finalizer ((MonoObject*)res);
4216 * ######################################################################
4217 * ######## Finalization support
4218 * ######################################################################
4222 * this is valid for the nursery: if the object has been forwarded it means it's
4223 * still refrenced from a root. If it is pinned it's still alive as well.
4224 * Return TRUE if @obj is ready to be finalized.
4226 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4229 is_critical_finalizer (FinalizeEntry *entry)
4234 if (!mono_defaults.critical_finalizer_object)
4237 obj = entry->object;
4238 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4240 return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4244 queue_finalization_entry (FinalizeEntry *entry) {
4245 if (is_critical_finalizer (entry)) {
4246 entry->next = critical_fin_list;
4247 critical_fin_list = entry;
4249 entry->next = fin_ready_list;
4250 fin_ready_list = entry;
4254 /* LOCKING: requires that the GC lock is held */
4256 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4258 FinalizeEntry **finalizable_hash = hash_table->table;
4259 mword finalizable_hash_size = hash_table->size;
4262 FinalizeEntry **new_hash;
4263 FinalizeEntry *entry, *next;
4264 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4266 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4267 for (i = 0; i < finalizable_hash_size; ++i) {
4268 for (entry = finalizable_hash [i]; entry; entry = next) {
4269 hash = mono_object_hash (entry->object) % new_size;
4271 entry->next = new_hash [hash];
4272 new_hash [hash] = entry;
4275 mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4276 hash_table->table = new_hash;
4277 hash_table->size = new_size;
4280 /* LOCKING: requires that the GC lock is held */
4282 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4284 if (hash_table->num_registered >= hash_table->size * 2)
4285 rehash_fin_table (hash_table);
4288 /* LOCKING: requires that the GC lock is held */
4290 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
4292 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4293 FinalizeEntry *entry, *prev;
4295 FinalizeEntry **finalizable_hash = hash_table->table;
4296 mword finalizable_hash_size = hash_table->size;
4300 for (i = 0; i < finalizable_hash_size; ++i) {
4302 for (entry = finalizable_hash [i]; entry;) {
4303 if ((char*)entry->object >= start && (char*)entry->object < end && !major_collector.is_object_live (entry->object)) {
4304 gboolean is_fin_ready = object_is_fin_ready (entry->object);
4305 char *copy = entry->object;
4306 copy_func ((void**)©, queue);
4309 FinalizeEntry *next;
4310 /* remove and put in fin_ready_list */
4312 prev->next = entry->next;
4314 finalizable_hash [i] = entry->next;
4316 num_ready_finalizers++;
4317 hash_table->num_registered--;
4318 queue_finalization_entry (entry);
4319 bridge_register_finalized_object ((MonoObject*)copy);
4320 /* Make it survive */
4321 from = entry->object;
4322 entry->object = copy;
4323 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));
4327 char *from = entry->object;
4328 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4329 FinalizeEntry *next = entry->next;
4330 unsigned int major_hash;
4331 /* remove from the list */
4333 prev->next = entry->next;
4335 finalizable_hash [i] = entry->next;
4336 hash_table->num_registered--;
4338 entry->object = copy;
4340 /* insert it into the major hash */
4341 rehash_fin_table_if_necessary (&major_finalizable_hash);
4342 major_hash = mono_object_hash ((MonoObject*) copy) %
4343 major_finalizable_hash.size;
4344 entry->next = major_finalizable_hash.table [major_hash];
4345 major_finalizable_hash.table [major_hash] = entry;
4346 major_finalizable_hash.num_registered++;
4348 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4353 /* update pointer */
4354 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
4355 entry->object = copy;
4360 entry = entry->next;
4366 object_is_reachable (char *object, char *start, char *end)
4368 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4369 if (object < start || object >= end)
4371 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4375 mono_sgen_object_is_live (void *obj)
4377 if (ptr_in_nursery (obj))
4378 return object_is_pinned (obj);
4379 if (current_collection_generation == GENERATION_NURSERY)
4381 return major_collector.is_object_live (obj);
4384 /* LOCKING: requires that the GC lock is held */
4386 null_ephemerons_for_domain (MonoDomain *domain)
4388 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4391 MonoObject *object = (MonoObject*)current->array;
4393 if (object && !object->vtable) {
4394 EphemeronLinkNode *tmp = current;
4397 prev->next = current->next;
4399 ephemeron_list = current->next;
4401 current = current->next;
4402 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4405 current = current->next;
4410 /* LOCKING: requires that the GC lock is held */
4412 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4414 int was_in_nursery, was_promoted;
4415 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4417 Ephemeron *cur, *array_end;
4421 char *object = current->array;
4423 if (!object_is_reachable (object, start, end)) {
4424 EphemeronLinkNode *tmp = current;
4426 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4429 prev->next = current->next;
4431 ephemeron_list = current->next;
4433 current = current->next;
4434 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4439 was_in_nursery = ptr_in_nursery (object);
4440 copy_func ((void**)&object, queue);
4441 current->array = object;
4443 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4444 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4446 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4448 array = (MonoArray*)object;
4449 cur = mono_array_addr (array, Ephemeron, 0);
4450 array_end = cur + mono_array_length_fast (array);
4451 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4453 for (; cur < array_end; ++cur) {
4454 char *key = (char*)cur->key;
4456 if (!key || key == tombstone)
4459 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4460 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4461 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4463 if (!object_is_reachable (key, start, end)) {
4464 cur->key = tombstone;
4470 if (ptr_in_nursery (key)) {/*key was not promoted*/
4471 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4472 mono_sgen_add_to_global_remset (queue->allocator, &cur->key);
4474 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4475 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4476 mono_sgen_add_to_global_remset (queue->allocator, &cur->value);
4481 current = current->next;
4485 /* LOCKING: requires that the GC lock is held */
4487 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4489 int nothing_marked = 1;
4490 EphemeronLinkNode *current = ephemeron_list;
4492 Ephemeron *cur, *array_end;
4495 for (current = ephemeron_list; current; current = current->next) {
4496 char *object = current->array;
4497 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4499 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4500 if (object < start || object >= end)
4503 /*It has to be alive*/
4504 if (!object_is_reachable (object, start, end)) {
4505 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4509 copy_func ((void**)&object, queue);
4511 array = (MonoArray*)object;
4512 cur = mono_array_addr (array, Ephemeron, 0);
4513 array_end = cur + mono_array_length_fast (array);
4514 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4516 for (; cur < array_end; ++cur) {
4517 char *key = cur->key;
4519 if (!key || key == tombstone)
4522 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4523 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4524 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4526 if (object_is_reachable (key, start, end)) {
4527 char *value = cur->value;
4529 copy_func ((void**)&cur->key, queue);
4531 if (!object_is_reachable (value, start, end))
4533 copy_func ((void**)&cur->value, queue);
4539 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4540 return nothing_marked;
4543 /* LOCKING: requires that the GC lock is held */
4545 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
4547 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4548 DisappearingLink **disappearing_link_hash = hash->table;
4549 int disappearing_link_hash_size = hash->size;
4550 DisappearingLink *entry, *prev;
4552 if (!hash->num_links)
4554 for (i = 0; i < disappearing_link_hash_size; ++i) {
4556 for (entry = disappearing_link_hash [i]; entry;) {
4558 gboolean track = DISLINK_TRACK (entry);
4561 * Tracked references are processed after
4562 * finalization handling whereas standard weak
4563 * references are processed before. If an
4564 * object is still not marked after finalization
4565 * handling it means that it either doesn't have
4566 * a finalizer or the finalizer has already run,
4567 * so we must null a tracking reference.
4569 if (track == before_finalization) {
4571 entry = entry->next;
4575 object = DISLINK_OBJECT (entry);
4577 if (object >= start && object < end && !major_collector.is_object_live (object)) {
4578 if (object_is_fin_ready (object)) {
4579 void **p = entry->link;
4580 DisappearingLink *old;
4582 /* remove from list */
4584 prev->next = entry->next;
4586 disappearing_link_hash [i] = entry->next;
4587 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4589 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4594 char *copy = object;
4595 copy_func ((void**)©, queue);
4597 /* Update pointer if it's moved. If the object
4598 * has been moved out of the nursery, we need to
4599 * remove the link from the minor hash table to
4602 * FIXME: what if an object is moved earlier?
4605 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4606 void **link = entry->link;
4607 DisappearingLink *old;
4608 /* remove from list */
4610 prev->next = entry->next;
4612 disappearing_link_hash [i] = entry->next;
4614 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4618 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4619 track, GENERATION_OLD);
4621 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4625 *entry->link = HIDE_POINTER (copy, track);
4626 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4631 entry = entry->next;
4636 /* LOCKING: requires that the GC lock is held */
4638 null_links_for_domain (MonoDomain *domain, int generation)
4640 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4641 DisappearingLink **disappearing_link_hash = hash->table;
4642 int disappearing_link_hash_size = hash->size;
4643 DisappearingLink *entry, *prev;
4645 for (i = 0; i < disappearing_link_hash_size; ++i) {
4647 for (entry = disappearing_link_hash [i]; entry; ) {
4648 char *object = DISLINK_OBJECT (entry);
4649 if (object && !((MonoObject*)object)->vtable) {
4650 DisappearingLink *next = entry->next;
4655 disappearing_link_hash [i] = next;
4657 if (*(entry->link)) {
4658 *(entry->link) = NULL;
4659 g_warning ("Disappearing link %p not freed", entry->link);
4661 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4668 entry = entry->next;
4673 /* LOCKING: requires that the GC lock is held */
4675 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4676 FinalizeEntryHashTable *hash_table)
4678 FinalizeEntry **finalizable_hash = hash_table->table;
4679 mword finalizable_hash_size = hash_table->size;
4680 FinalizeEntry *entry, *prev;
4683 if (no_finalize || !out_size || !out_array)
4686 for (i = 0; i < finalizable_hash_size; ++i) {
4688 for (entry = finalizable_hash [i]; entry;) {
4689 if (mono_object_domain (entry->object) == domain) {
4690 FinalizeEntry *next;
4691 /* remove and put in out_array */
4693 prev->next = entry->next;
4695 finalizable_hash [i] = entry->next;
4697 hash_table->num_registered--;
4698 out_array [count ++] = entry->object;
4699 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));
4701 if (count == out_size)
4706 entry = entry->next;
4713 * mono_gc_finalizers_for_domain:
4714 * @domain: the unloading appdomain
4715 * @out_array: output array
4716 * @out_size: size of output array
4718 * Store inside @out_array up to @out_size objects that belong to the unloading
4719 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4720 * until it returns 0.
4721 * The items are removed from the finalizer data structure, so the caller is supposed
4723 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4726 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4731 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4732 if (result < out_size) {
4733 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4734 &major_finalizable_hash);
4742 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4744 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4745 FinalizeEntry **finalizable_hash;
4746 mword finalizable_hash_size;
4747 FinalizeEntry *entry, *prev;
4751 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4752 hash = mono_object_hash (obj);
4754 rehash_fin_table_if_necessary (hash_table);
4755 finalizable_hash = hash_table->table;
4756 finalizable_hash_size = hash_table->size;
4757 hash %= finalizable_hash_size;
4759 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4760 if (entry->object == obj) {
4762 /* remove from the list */
4764 prev->next = entry->next;
4766 finalizable_hash [hash] = entry->next;
4767 hash_table->num_registered--;
4768 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));
4769 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4777 /* request to deregister, but already out of the list */
4781 entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4782 entry->object = obj;
4783 entry->next = finalizable_hash [hash];
4784 finalizable_hash [hash] = entry;
4785 hash_table->num_registered++;
4786 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)));
4791 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4793 if (ptr_in_nursery (obj))
4794 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4796 register_for_finalization (obj, user_data, GENERATION_OLD);
4800 rehash_dislink (DisappearingLinkHashTable *hash_table)
4802 DisappearingLink **disappearing_link_hash = hash_table->table;
4803 int disappearing_link_hash_size = hash_table->size;
4806 DisappearingLink **new_hash;
4807 DisappearingLink *entry, *next;
4808 int new_size = g_spaced_primes_closest (hash_table->num_links);
4810 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4811 for (i = 0; i < disappearing_link_hash_size; ++i) {
4812 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4813 hash = mono_aligned_addr_hash (entry->link) % new_size;
4815 entry->next = new_hash [hash];
4816 new_hash [hash] = entry;
4819 mono_sgen_free_internal_dynamic (disappearing_link_hash,
4820 disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4821 hash_table->table = new_hash;
4822 hash_table->size = new_size;
4825 /* LOCKING: assumes the GC lock is held */
4827 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4829 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4830 DisappearingLink *entry, *prev;
4832 DisappearingLink **disappearing_link_hash = hash_table->table;
4833 int disappearing_link_hash_size = hash_table->size;
4835 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4836 rehash_dislink (hash_table);
4837 disappearing_link_hash = hash_table->table;
4838 disappearing_link_hash_size = hash_table->size;
4840 /* FIXME: add check that link is not in the heap */
4841 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4842 entry = disappearing_link_hash [hash];
4844 for (; entry; entry = entry->next) {
4845 /* link already added */
4846 if (link == entry->link) {
4847 /* NULL obj means remove */
4850 prev->next = entry->next;
4852 disappearing_link_hash [hash] = entry->next;
4853 hash_table->num_links--;
4854 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4855 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4858 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4866 entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4867 *link = HIDE_POINTER (obj, track);
4869 entry->next = disappearing_link_hash [hash];
4870 disappearing_link_hash [hash] = entry;
4871 hash_table->num_links++;
4872 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)));
4875 /* LOCKING: assumes the GC lock is held */
4877 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4879 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4880 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4882 if (ptr_in_nursery (obj))
4883 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4885 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4890 mono_gc_invoke_finalizers (void)
4892 FinalizeEntry *entry = NULL;
4893 gboolean entry_is_critical = FALSE;
4896 /* FIXME: batch to reduce lock contention */
4897 while (fin_ready_list || critical_fin_list) {
4901 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4903 /* We have finalized entry in the last
4904 interation, now we need to remove it from
4907 *list = entry->next;
4909 FinalizeEntry *e = *list;
4910 while (e->next != entry)
4912 e->next = entry->next;
4914 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4918 /* Now look for the first non-null entry. */
4919 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4922 entry_is_critical = FALSE;
4924 entry_is_critical = TRUE;
4925 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4930 g_assert (entry->object);
4931 num_ready_finalizers--;
4932 obj = entry->object;
4933 entry->object = NULL;
4934 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4942 g_assert (entry->object == NULL);
4944 /* the object is on the stack so it is pinned */
4945 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4946 mono_gc_run_finalize (obj, NULL);
4953 mono_gc_pending_finalizers (void)
4955 return fin_ready_list || critical_fin_list;
4958 /* Negative value to remove */
4960 mono_gc_add_memory_pressure (gint64 value)
4962 /* FIXME: Use interlocked functions */
4964 memory_pressure += value;
4969 mono_sgen_register_major_sections_alloced (int num_sections)
4971 minor_collection_sections_alloced += num_sections;
4975 mono_sgen_get_minor_collection_allowance (void)
4977 return minor_collection_allowance;
4981 * ######################################################################
4982 * ######## registered roots support
4983 * ######################################################################
4987 rehash_roots (gboolean pinned)
4991 RootRecord **new_hash;
4992 RootRecord *entry, *next;
4995 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4996 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4997 for (i = 0; i < roots_hash_size [pinned]; ++i) {
4998 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4999 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5001 entry->next = new_hash [hash];
5002 new_hash [hash] = entry;
5005 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5006 roots_hash [pinned] = new_hash;
5007 roots_hash_size [pinned] = new_size;
5011 find_root (int root_type, char *start, guint32 addr_hash)
5013 RootRecord *new_root;
5015 guint32 hash = addr_hash % roots_hash_size [root_type];
5016 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5017 /* we allow changing the size and the descriptor (for thread statics etc) */
5018 if (new_root->start_root == start) {
5027 * We do not coalesce roots.
5030 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5032 RootRecord *new_root;
5033 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5036 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5037 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5040 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5041 new_root = find_root (i, start, addr_hash);
5042 /* we allow changing the size and the descriptor (for thread statics etc) */
5044 size_t old_size = new_root->end_root - new_root->start_root;
5045 new_root->end_root = new_root->start_root + size;
5046 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5047 ((new_root->root_desc == 0) && (descr == NULL)));
5048 new_root->root_desc = (mword)descr;
5050 roots_size -= old_size;
5055 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
5057 new_root->start_root = start;
5058 new_root->end_root = new_root->start_root + size;
5059 new_root->root_desc = (mword)descr;
5061 hash = addr_hash % roots_hash_size [root_type];
5062 num_roots_entries [root_type]++;
5063 new_root->next = roots_hash [root_type] [hash];
5064 roots_hash [root_type][hash] = new_root;
5065 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));
5075 mono_gc_register_root (char *start, size_t size, void *descr)
5077 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5081 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5083 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5087 mono_gc_deregister_root (char* addr)
5089 RootRecord *tmp, *prev;
5090 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5094 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5095 hash = addr_hash % roots_hash_size [root_type];
5096 tmp = roots_hash [root_type][hash];
5099 if (tmp->start_root == (char*)addr) {
5101 prev->next = tmp->next;
5103 roots_hash [root_type][hash] = tmp->next;
5104 roots_size -= (tmp->end_root - tmp->start_root);
5105 num_roots_entries [root_type]--;
5106 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5107 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
5118 * ######################################################################
5119 * ######## Thread handling (stop/start code)
5120 * ######################################################################
5123 /* FIXME: handle large/small config */
5124 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5126 SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5128 #if USE_SIGNAL_BASED_START_STOP_WORLD
5130 static MonoSemType suspend_ack_semaphore;
5131 static MonoSemType *suspend_ack_semaphore_ptr;
5132 static unsigned int global_stop_count = 0;
5134 static sigset_t suspend_signal_mask;
5137 static MonoContext cur_thread_ctx = {0};
5139 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5143 mono_sgen_thread_info_current (void)
5145 #ifdef HAVE_KW_THREAD
5148 return pthread_getspecific (thread_info_key);
5153 mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
5155 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5156 SgenThreadInfo *info;
5158 info = thread_table [hash];
5159 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5166 update_current_thread_stack (void *start)
5168 int stack_guard = 0;
5169 #ifndef USE_MONO_CTX
5170 void *ptr = cur_thread_regs;
5172 SgenThreadInfo *info = mono_sgen_thread_info_current ();
5174 info->stack_start = align_pointer (&stack_guard);
5175 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5177 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
5178 info->monoctx = &cur_thread_ctx;
5180 ARCH_STORE_REGS (ptr);
5181 info->stopped_regs = ptr;
5183 if (gc_callbacks.thread_suspend_func)
5184 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5188 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5189 * have cross-domain checks in the write barrier.
5191 //#define XDOMAIN_CHECKS_IN_WBARRIER
5193 #ifndef SGEN_BINARY_PROTOCOL
5194 #ifndef HEAVY_STATISTICS
5195 #define MANAGED_ALLOCATION
5196 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5197 #define MANAGED_WBARRIER
5203 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5206 mono_sgen_wait_for_suspend_ack (int count)
5210 for (i = 0; i < count; ++i) {
5211 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5212 if (errno != EINTR) {
5213 g_error ("sem_wait ()");
5220 restart_threads_until_none_in_managed_allocator (void)
5222 SgenThreadInfo *info;
5223 int result, num_threads_died = 0;
5224 int sleep_duration = -1;
5227 int restart_count = 0, restarted_count = 0;
5228 /* restart all threads that stopped in the
5230 FOREACH_THREAD (info) {
5233 if (!info->stack_start || info->in_critical_region ||
5234 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5235 binary_protocol_thread_restart ((gpointer)info->id);
5236 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5237 result = thread_resume (pthread_mach_thread_np (info->id));
5239 result = pthread_kill (info->id, restart_signal_num);
5247 /* we set the stopped_ip to
5248 NULL for threads which
5249 we're not restarting so
5250 that we can easily identify
5252 info->stopped_ip = NULL;
5253 info->stopped_domain = NULL;
5255 } END_FOREACH_THREAD
5256 /* if no threads were restarted, we're done */
5257 if (restart_count == 0)
5260 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5261 /* mach thread_resume is synchronous so we dont need to wait for them */
5263 /* wait for the threads to signal their restart */
5264 mono_sgen_wait_for_suspend_ack (restart_count);
5267 if (sleep_duration < 0) {
5271 g_usleep (sleep_duration);
5272 sleep_duration += 10;
5275 /* stop them again */
5276 FOREACH_THREAD (info) {
5277 if (info->skip || info->stopped_ip == NULL)
5279 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5280 result = thread_suspend (pthread_mach_thread_np (info->id));
5282 result = pthread_kill (info->id, suspend_signal_num);
5289 } END_FOREACH_THREAD
5290 /* some threads might have died */
5291 num_threads_died += restart_count - restarted_count;
5292 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5293 /* mach thread_resume is synchronous so we dont need to wait for them */
5295 /* wait for the threads to signal their suspension
5297 mono_sgen_wait_for_suspend_ack (restart_count);
5301 return num_threads_died;
5304 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5306 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5308 SgenThreadInfo *info;
5310 int old_errno = errno;
5312 MonoContext monoctx;
5314 gpointer regs [ARCH_NUM_REGS];
5316 gpointer stack_start;
5318 info = mono_sgen_thread_info_current ();
5319 info->stopped_domain = mono_domain_get ();
5320 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5321 stop_count = global_stop_count;
5322 /* duplicate signal */
5323 if (0 && info->stop_count == stop_count) {
5327 #ifdef HAVE_KW_THREAD
5328 /* update the remset info in the thread data structure */
5329 info->remset = remembered_set;
5331 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5332 /* If stack_start is not within the limits, then don't set it
5333 in info and we will be restarted. */
5334 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5335 info->stack_start = stack_start;
5338 mono_sigctx_to_monoctx (context, &monoctx);
5339 info->monoctx = &monoctx;
5341 ARCH_COPY_SIGCTX_REGS (regs, context);
5342 info->stopped_regs = regs;
5345 g_assert (!info->stack_start);
5348 /* Notify the JIT */
5349 if (gc_callbacks.thread_suspend_func)
5350 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5352 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5353 /* notify the waiting thread */
5354 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5355 info->stop_count = stop_count;
5357 /* wait until we receive the restart signal */
5360 sigsuspend (&suspend_signal_mask);
5361 } while (info->signal != restart_signal_num);
5363 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5364 /* notify the waiting thread */
5365 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5371 restart_handler (int sig)
5373 SgenThreadInfo *info;
5374 int old_errno = errno;
5376 info = mono_sgen_thread_info_current ();
5377 info->signal = restart_signal_num;
5378 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5384 acquire_gc_locks (void)
5390 release_gc_locks (void)
5392 UNLOCK_INTERRUPTION;
5395 static TV_DECLARE (stop_world_time);
5396 static unsigned long max_pause_usec = 0;
5398 /* LOCKING: assumes the GC lock is held */
5400 stop_world (int generation)
5404 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
5405 acquire_gc_locks ();
5407 update_current_thread_stack (&count);
5409 global_stop_count++;
5410 DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_sgen_thread_info_current (), (gpointer)ARCH_GET_THREAD ()));
5411 TV_GETTIME (stop_world_time);
5412 count = mono_sgen_thread_handshake (suspend_signal_num);
5413 count -= restart_threads_until_none_in_managed_allocator ();
5414 g_assert (count >= 0);
5415 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5416 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
5420 /* LOCKING: assumes the GC lock is held */
5422 restart_world (int generation)
5425 SgenThreadInfo *info;
5426 TV_DECLARE (end_sw);
5429 /* notify the profiler of the leftovers */
5430 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5431 if (moved_objects_idx) {
5432 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5433 moved_objects_idx = 0;
5436 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
5437 FOREACH_THREAD (info) {
5438 info->stack_start = NULL;
5440 info->monoctx = NULL;
5442 info->stopped_regs = NULL;
5444 } END_FOREACH_THREAD
5446 release_gc_locks ();
5448 count = mono_sgen_thread_handshake (restart_signal_num);
5449 TV_GETTIME (end_sw);
5450 usec = TV_ELAPSED (stop_world_time, end_sw);
5451 max_pause_usec = MAX (usec, max_pause_usec);
5452 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5453 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
5457 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5460 mono_sgen_get_current_collection_generation (void)
5462 return current_collection_generation;
5466 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5468 gc_callbacks = *callbacks;
5472 mono_gc_get_gc_callbacks ()
5474 return &gc_callbacks;
5477 /* Variables holding start/end nursery so it won't have to be passed at every call */
5478 static void *scan_area_arg_start, *scan_area_arg_end;
5481 mono_gc_conservatively_scan_area (void *start, void *end)
5483 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5487 mono_gc_scan_object (void *obj)
5489 UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
5491 if (current_collection_generation == GENERATION_NURSERY)
5492 major_collector.copy_object (&obj, data->queue);
5494 major_collector.copy_or_mark_object (&obj, data->queue);
5499 * Mark from thread stacks and registers.
5502 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
5504 SgenThreadInfo *info;
5506 scan_area_arg_start = start_nursery;
5507 scan_area_arg_end = end_nursery;
5509 FOREACH_THREAD (info) {
5511 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));
5514 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));
5515 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
5516 UserCopyOrMarkData data = { NULL, queue };
5517 set_user_copy_or_mark_data (&data);
5518 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5519 set_user_copy_or_mark_data (NULL);
5520 } else if (!precise) {
5521 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5526 conservatively_pin_objects_from ((void**)info->monoctx, (void**)info->monoctx + ARCH_NUM_REGS,
5527 start_nursery, end_nursery, PIN_TYPE_STACK);
5530 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5531 start_nursery, end_nursery, PIN_TYPE_STACK);
5533 } END_FOREACH_THREAD
5537 find_pinning_ref_from_thread (char *obj, size_t size)
5540 SgenThreadInfo *info;
5541 char *endobj = obj + size;
5543 FOREACH_THREAD (info) {
5544 char **start = (char**)info->stack_start;
5547 while (start < (char**)info->stack_end) {
5548 if (*start >= obj && *start < endobj) {
5549 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));
5554 for (j = 0; j < ARCH_NUM_REGS; ++j) {
5556 mword w = ((mword*)info->monoctx) [j];
5558 mword w = (mword)info->stopped_regs [j];
5561 if (w >= (mword)obj && w < (mword)obj + size)
5562 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)info->id));
5563 } END_FOREACH_THREAD
5568 ptr_on_stack (void *ptr)
5570 gpointer stack_start = &stack_start;
5571 SgenThreadInfo *info = mono_sgen_thread_info_current ();
5573 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5579 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
5586 HEAVY_STAT (++stat_global_remsets_processed);
5588 HEAVY_STAT (++stat_local_remsets_processed);
5590 /* FIXME: exclude stack locations */
5591 switch ((*p) & REMSET_TYPE_MASK) {
5592 case REMSET_LOCATION:
5594 //__builtin_prefetch (ptr);
5595 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5596 gpointer old = *ptr;
5597 major_collector.copy_object (ptr, queue);
5598 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5600 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5601 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5603 * If the object is pinned, each reference to it from nonpinned objects
5604 * becomes part of the global remset, which can grow very large.
5606 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5607 mono_sgen_add_to_global_remset (queue->allocator, ptr);
5610 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5614 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5615 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5618 while (count-- > 0) {
5619 major_collector.copy_object (ptr, queue);
5620 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5621 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5622 mono_sgen_add_to_global_remset (queue->allocator, ptr);
5627 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5628 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5630 major_collector.minor_scan_object ((char*)ptr, queue);
5632 case REMSET_VTYPE: {
5633 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5634 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5639 ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
5643 g_assert_not_reached ();
5648 #ifdef HEAVY_STATISTICS
5650 collect_store_remsets (RememberedSet *remset, mword *bumper)
5652 mword *p = remset->data;
5657 while (p < remset->store_next) {
5658 switch ((*p) & REMSET_TYPE_MASK) {
5659 case REMSET_LOCATION:
5662 ++stat_saved_remsets_1;
5664 if (*p == last1 || *p == last2) {
5665 ++stat_saved_remsets_2;
5682 g_assert_not_reached ();
5692 RememberedSet *remset;
5694 SgenThreadInfo *info;
5696 mword *addresses, *bumper, *p, *r;
5698 FOREACH_THREAD (info) {
5699 for (remset = info->remset; remset; remset = remset->next)
5700 size += remset->store_next - remset->data;
5701 } END_FOREACH_THREAD
5702 for (remset = freed_thread_remsets; remset; remset = remset->next)
5703 size += remset->store_next - remset->data;
5704 for (remset = global_remset; remset; remset = remset->next)
5705 size += remset->store_next - remset->data;
5707 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5709 FOREACH_THREAD (info) {
5710 for (remset = info->remset; remset; remset = remset->next)
5711 bumper = collect_store_remsets (remset, bumper);
5712 } END_FOREACH_THREAD
5713 for (remset = global_remset; remset; remset = remset->next)
5714 bumper = collect_store_remsets (remset, bumper);
5715 for (remset = freed_thread_remsets; remset; remset = remset->next)
5716 bumper = collect_store_remsets (remset, bumper);
5718 g_assert (bumper <= addresses + size);
5720 stat_store_remsets += bumper - addresses;
5722 sort_addresses ((void**)addresses, bumper - addresses);
5725 while (r < bumper) {
5731 stat_store_remsets_unique += p - addresses;
5733 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5738 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5740 *info->store_remset_buffer_index_addr = 0;
5741 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5745 remset_byte_size (RememberedSet *remset)
5747 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5751 scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5753 RememberedSet *remset;
5754 mword *p, *next_p, *store_pos;
5756 /* the global one */
5757 for (remset = global_remset; remset; remset = remset->next) {
5758 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));
5759 store_pos = remset->data;
5760 for (p = remset->data; p < remset->store_next; p = next_p) {
5761 void **ptr = (void**)p [0];
5763 /*Ignore previously processed remset.*/
5764 if (!global_remset_location_was_not_added (ptr)) {
5769 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5772 * Clear global remsets of locations which no longer point to the
5773 * nursery. Otherwise, they could grow indefinitely between major
5776 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5778 if (ptr_in_nursery (*ptr)) {
5779 *store_pos ++ = p [0];
5780 HEAVY_STAT (++stat_global_remsets_readded);
5784 /* Truncate the remset */
5785 remset->store_next = store_pos;
5790 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5793 SgenThreadInfo *info;
5794 RememberedSet *remset;
5795 GenericStoreRememberedSet *store_remset;
5798 #ifdef HEAVY_STATISTICS
5802 /* the generic store ones */
5803 store_remset = generic_store_remsets;
5804 while (store_remset) {
5805 GenericStoreRememberedSet *next = store_remset->next;
5807 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5808 gpointer addr = store_remset->data [i];
5810 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5813 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5815 store_remset = next;
5817 generic_store_remsets = NULL;
5819 /* the per-thread ones */
5820 FOREACH_THREAD (info) {
5821 RememberedSet *next;
5823 for (remset = info->remset; remset; remset = next) {
5824 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));
5825 for (p = remset->data; p < remset->store_next;)
5826 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5827 remset->store_next = remset->data;
5828 next = remset->next;
5829 remset->next = NULL;
5830 if (remset != info->remset) {
5831 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5832 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5835 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5836 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5837 clear_thread_store_remset_buffer (info);
5838 } END_FOREACH_THREAD
5840 /* the freed thread ones */
5841 while (freed_thread_remsets) {
5842 RememberedSet *next;
5843 remset = freed_thread_remsets;
5844 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));
5845 for (p = remset->data; p < remset->store_next;)
5846 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5847 next = remset->next;
5848 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5849 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5850 freed_thread_remsets = next;
5855 * Clear the info in the remembered sets: we're doing a major collection, so
5856 * the per-thread ones are not needed and the global ones will be reconstructed
5860 clear_remsets (void)
5862 SgenThreadInfo *info;
5863 RememberedSet *remset, *next;
5865 /* the global list */
5866 for (remset = global_remset; remset; remset = next) {
5867 remset->store_next = remset->data;
5868 next = remset->next;
5869 remset->next = NULL;
5870 if (remset != global_remset) {
5871 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5872 mono_sgen_free_internal_dynamic_delayed (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET,
5873 mono_sgen_get_unmanaged_allocator ());
5876 /* the generic store ones */
5877 while (generic_store_remsets) {
5878 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5879 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5880 generic_store_remsets = gs_next;
5882 /* the per-thread ones */
5883 FOREACH_THREAD (info) {
5884 for (remset = info->remset; remset; remset = next) {
5885 remset->store_next = remset->data;
5886 next = remset->next;
5887 remset->next = NULL;
5888 if (remset != info->remset) {
5889 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5890 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5893 clear_thread_store_remset_buffer (info);
5894 } END_FOREACH_THREAD
5896 /* the freed thread ones */
5897 while (freed_thread_remsets) {
5898 next = freed_thread_remsets->next;
5899 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5900 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5901 freed_thread_remsets = next;
5906 * Clear the thread local TLAB variables for all threads.
5911 SgenThreadInfo *info;
5913 FOREACH_THREAD (info) {
5914 /* A new TLAB will be allocated when the thread does its first allocation */
5915 *info->tlab_start_addr = NULL;
5916 *info->tlab_next_addr = NULL;
5917 *info->tlab_temp_end_addr = NULL;
5918 *info->tlab_real_end_addr = NULL;
5919 } END_FOREACH_THREAD
5922 /* LOCKING: assumes the GC lock is held */
5923 static SgenThreadInfo*
5924 gc_register_current_thread (void *addr)
5927 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5928 #ifndef HAVE_KW_THREAD
5929 SgenThreadInfo *__thread_info__ = info;
5935 memset (info, 0, sizeof (SgenThreadInfo));
5936 #ifndef HAVE_KW_THREAD
5937 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5939 g_assert (!pthread_getspecific (thread_info_key));
5940 pthread_setspecific (thread_info_key, info);
5945 info->id = ARCH_GET_THREAD ();
5946 info->stop_count = -1;
5949 info->stack_start = NULL;
5950 info->tlab_start_addr = &TLAB_START;
5951 info->tlab_next_addr = &TLAB_NEXT;
5952 info->tlab_temp_end_addr = &TLAB_TEMP_END;
5953 info->tlab_real_end_addr = &TLAB_REAL_END;
5954 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5955 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5956 info->stopped_ip = NULL;
5957 info->stopped_domain = NULL;
5959 info->monoctx = NULL;
5961 info->stopped_regs = NULL;
5964 binary_protocol_thread_register ((gpointer)info->id);
5966 #ifdef HAVE_KW_THREAD
5967 tlab_next_addr = &tlab_next;
5968 store_remset_buffer_index_addr = &store_remset_buffer_index;
5971 #if defined(__MACH__)
5972 info->mach_port = mach_thread_self ();
5975 /* try to get it with attributes first */
5976 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5980 pthread_attr_t attr;
5981 pthread_getattr_np (pthread_self (), &attr);
5982 pthread_attr_getstack (&attr, &sstart, &size);
5983 info->stack_start_limit = sstart;
5984 info->stack_end = (char*)sstart + size;
5985 pthread_attr_destroy (&attr);
5987 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5988 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5989 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5992 /* FIXME: we assume the stack grows down */
5993 gsize stack_bottom = (gsize)addr;
5994 stack_bottom += 4095;
5995 stack_bottom &= ~4095;
5996 info->stack_end = (char*)stack_bottom;
6000 #ifdef HAVE_KW_THREAD
6001 stack_end = info->stack_end;
6004 /* hash into the table */
6005 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
6006 info->next = thread_table [hash];
6007 thread_table [hash] = info;
6009 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
6010 pthread_setspecific (remembered_set_key, info->remset);
6011 #ifdef HAVE_KW_THREAD
6012 remembered_set = info->remset;
6015 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
6016 STORE_REMSET_BUFFER_INDEX = 0;
6018 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
6020 if (gc_callbacks.thread_attach_func)
6021 info->runtime_data = gc_callbacks.thread_attach_func ();
6027 add_generic_store_remset_from_buffer (gpointer *buffer)
6029 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
6030 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6031 remset->next = generic_store_remsets;
6032 generic_store_remsets = remset;
6036 unregister_current_thread (void)
6039 SgenThreadInfo *prev = NULL;
6041 RememberedSet *rset;
6042 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6044 binary_protocol_thread_unregister ((gpointer)id);
6046 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6047 p = thread_table [hash];
6049 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6050 while (!ARCH_THREAD_EQUALS (p->id, id)) {
6055 thread_table [hash] = p->next;
6057 prev->next = p->next;
6060 #if defined(__MACH__)
6061 mach_port_deallocate (current_task (), p->mach_port);
6064 if (gc_callbacks.thread_detach_func) {
6065 gc_callbacks.thread_detach_func (p->runtime_data);
6066 p->runtime_data = NULL;
6070 if (freed_thread_remsets) {
6071 for (rset = p->remset; rset->next; rset = rset->next)
6073 rset->next = freed_thread_remsets;
6074 freed_thread_remsets = p->remset;
6076 freed_thread_remsets = p->remset;
6079 if (*p->store_remset_buffer_index_addr)
6080 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6081 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6086 unregister_thread (void *k)
6088 /* If a delegate is passed to native code and invoked on a thread we dont
6089 * know about, the jit will register it with mono_jit_thead_attach, but
6090 * we have no way of knowing when that thread goes away. SGen has a TSD
6091 * so we assume that if the domain is still registered, we can detach
6094 if (mono_domain_get ())
6095 mono_thread_detach (mono_thread_current ());
6098 unregister_current_thread ();
6103 mono_gc_register_thread (void *baseptr)
6105 SgenThreadInfo *info;
6109 info = mono_sgen_thread_info_current ();
6111 info = gc_register_current_thread (baseptr);
6113 /* The main thread might get registered before callbacks are set */
6114 if (gc_callbacks.thread_attach_func && !info->runtime_data)
6115 info->runtime_data = gc_callbacks.thread_attach_func ();
6119 /* Need a better place to initialize this */
6120 if (!array_fill_vtable && mono_get_root_domain ()) {
6121 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
6124 return info != NULL;
6128 * mono_gc_set_stack_end:
6130 * Set the end of the current threads stack to STACK_END. The stack space between
6131 * STACK_END and the real end of the threads stack will not be scanned during collections.
6134 mono_gc_set_stack_end (void *stack_end)
6136 SgenThreadInfo *info;
6139 info = mono_sgen_thread_info_current ();
6141 g_assert (stack_end < info->stack_end);
6142 info->stack_end = stack_end;
6147 #if USE_PTHREAD_INTERCEPT
6150 void *(*start_routine) (void *);
6153 MonoSemType registered;
6154 } SgenThreadStartInfo;
6157 gc_start_thread (void *arg)
6159 SgenThreadStartInfo *start_info = arg;
6160 SgenThreadInfo* info;
6161 void *t_arg = start_info->arg;
6162 void *(*start_func) (void*) = start_info->start_routine;
6167 info = gc_register_current_thread (&result);
6169 post_result = MONO_SEM_POST (&(start_info->registered));
6170 g_assert (!post_result);
6171 result = start_func (t_arg);
6172 g_assert (!mono_domain_get ());
6174 * this is done by the pthread key dtor
6176 unregister_current_thread ();
6184 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6186 SgenThreadStartInfo *start_info;
6189 start_info = malloc (sizeof (SgenThreadStartInfo));
6192 MONO_SEM_INIT (&(start_info->registered), 0);
6193 start_info->arg = arg;
6194 start_info->start_routine = start_routine;
6196 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6198 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6199 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6202 MONO_SEM_DESTROY (&(start_info->registered));
6208 mono_gc_pthread_join (pthread_t thread, void **retval)
6210 return pthread_join (thread, retval);
6214 mono_gc_pthread_detach (pthread_t thread)
6216 return pthread_detach (thread);
6219 #endif /* USE_PTHREAD_INTERCEPT */
6222 * ######################################################################
6223 * ######## Write barriers
6224 * ######################################################################
6228 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
6231 dummy_use (gpointer v) {
6232 __asm__ volatile ("" : "=r"(v) : "r"(v));
6236 static RememberedSet*
6237 alloc_remset (int size, gpointer id) {
6238 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6239 res->store_next = res->data;
6240 res->end_set = res->data + size;
6242 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6246 static RememberedSet*
6247 alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id)
6249 RememberedSet* res = mono_sgen_alloc_internal_full (alc, sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6250 res->store_next = res->data;
6251 res->end_set = res->data + size;
6253 DEBUG (4, fprintf (gc_debug_file, "Allocated global remset size %d at %p for %p\n", size, res->data, id));
6258 * Note: the write barriers first do the needed GC work and then do the actual store:
6259 * this way the value is visible to the conservative GC scan after the write barrier
6260 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6261 * the conservative scan, otherwise by the remembered set scan.
6264 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6266 HEAVY_STAT (++stat_wbarrier_set_field);
6267 if (ptr_in_nursery (field_ptr)) {
6268 *(void**)field_ptr = value;
6271 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6272 if (use_cardtable) {
6273 *(void**)field_ptr = value;
6274 if (ptr_in_nursery (value))
6275 sgen_card_table_mark_address ((mword)field_ptr);
6282 rs = REMEMBERED_SET;
6283 if (rs->store_next < rs->end_set) {
6284 *(rs->store_next++) = (mword)field_ptr;
6285 *(void**)field_ptr = value;
6289 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6290 rs->next = REMEMBERED_SET;
6291 REMEMBERED_SET = rs;
6292 #ifdef HAVE_KW_THREAD
6293 mono_sgen_thread_info_current ()->remset = rs;
6295 *(rs->store_next++) = (mword)field_ptr;
6296 *(void**)field_ptr = value;
6302 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6304 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6305 if (ptr_in_nursery (slot_ptr)) {
6306 *(void**)slot_ptr = value;
6309 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6310 if (use_cardtable) {
6311 *(void**)slot_ptr = value;
6312 if (ptr_in_nursery (value))
6313 sgen_card_table_mark_address ((mword)slot_ptr);
6320 rs = REMEMBERED_SET;
6321 if (rs->store_next < rs->end_set) {
6322 *(rs->store_next++) = (mword)slot_ptr;
6323 *(void**)slot_ptr = value;
6327 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6328 rs->next = REMEMBERED_SET;
6329 REMEMBERED_SET = rs;
6330 #ifdef HAVE_KW_THREAD
6331 mono_sgen_thread_info_current ()->remset = rs;
6333 *(rs->store_next++) = (mword)slot_ptr;
6334 *(void**)slot_ptr = value;
6340 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6342 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6343 /*This check can be done without taking a lock since dest_ptr array is pinned*/
6344 if (ptr_in_nursery (dest_ptr) || count <= 0) {
6345 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6349 if (use_cardtable) {
6350 gpointer *dest = dest_ptr;
6351 gpointer *src = src_ptr;
6353 /*overlapping that required backward copying*/
6354 if (src < dest && (src + count) > dest) {
6355 gpointer *start = dest;
6359 for (; dest >= start; --src, --dest) {
6360 gpointer value = *src;
6362 if (ptr_in_nursery (value))
6363 sgen_card_table_mark_address ((mword)dest);
6367 gpointer *end = dest + count;
6368 for (; dest < end; ++src, ++dest) {
6369 gpointer value = *src;
6371 if (ptr_in_nursery (value))
6372 sgen_card_table_mark_address ((mword)dest);
6380 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6382 rs = REMEMBERED_SET;
6383 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6384 if (rs->store_next + 1 < rs->end_set) {
6385 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6386 *(rs->store_next++) = count;
6390 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6391 rs->next = REMEMBERED_SET;
6392 REMEMBERED_SET = rs;
6393 #ifdef HAVE_KW_THREAD
6394 mono_sgen_thread_info_current ()->remset = rs;
6396 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6397 *(rs->store_next++) = count;
6403 static char *found_obj;
6406 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
6408 char *ptr = user_data;
6410 if (ptr >= obj && ptr < obj + size) {
6411 g_assert (!found_obj);
6416 /* for use in the debugger */
6417 char* find_object_for_ptr (char *ptr);
6419 find_object_for_ptr (char *ptr)
6421 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6423 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6424 find_object_for_ptr_callback, ptr, TRUE);
6430 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
6435 * Very inefficient, but this is debugging code, supposed to
6436 * be called from gdb, so we don't care.
6439 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
6444 evacuate_remset_buffer (void)
6449 buffer = STORE_REMSET_BUFFER;
6451 add_generic_store_remset_from_buffer (buffer);
6452 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6454 STORE_REMSET_BUFFER_INDEX = 0;
6458 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6464 HEAVY_STAT (++stat_wbarrier_generic_store);
6466 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6467 /* FIXME: ptr_in_heap must be called with the GC lock held */
6468 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6469 char *start = find_object_for_ptr (ptr);
6470 MonoObject *value = *(MonoObject**)ptr;
6474 MonoObject *obj = (MonoObject*)start;
6475 if (obj->vtable->domain != value->vtable->domain)
6476 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6482 if (*(gpointer*)ptr)
6483 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6485 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6486 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6490 if (use_cardtable) {
6491 if (ptr_in_nursery(*(gpointer*)ptr))
6492 sgen_card_table_mark_address ((mword)ptr);
6498 buffer = STORE_REMSET_BUFFER;
6499 index = STORE_REMSET_BUFFER_INDEX;
6500 /* This simple optimization eliminates a sizable portion of
6501 entries. Comparing it to the last but one entry as well
6502 doesn't eliminate significantly more entries. */
6503 if (buffer [index] == ptr) {
6508 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6509 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6512 if (index >= STORE_REMSET_BUFFER_SIZE) {
6513 evacuate_remset_buffer ();
6514 index = STORE_REMSET_BUFFER_INDEX;
6515 g_assert (index == 0);
6518 buffer [index] = ptr;
6519 STORE_REMSET_BUFFER_INDEX = index;
6525 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6527 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6528 *(void**)ptr = value;
6529 if (ptr_in_nursery (value))
6530 mono_gc_wbarrier_generic_nostore (ptr);
6534 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
6536 mword *dest = _dest;
6541 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
6546 size -= SIZEOF_VOID_P;
6553 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6556 size_t size = count * mono_class_value_size (klass, NULL);
6558 HEAVY_STAT (++stat_wbarrier_value_copy);
6559 g_assert (klass->valuetype);
6561 memmove (dest, src, size);
6562 if (use_cardtable) {
6563 sgen_card_table_mark_range ((mword)dest, size);
6565 rs = REMEMBERED_SET;
6566 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
6570 g_assert (klass->gc_descr_inited);
6571 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));
6573 if (rs->store_next + 3 < rs->end_set) {
6574 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6575 *(rs->store_next++) = (mword)klass->gc_descr;
6576 *(rs->store_next++) = (mword)count;
6580 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6581 rs->next = REMEMBERED_SET;
6582 REMEMBERED_SET = rs;
6583 #ifdef HAVE_KW_THREAD
6584 mono_sgen_thread_info_current ()->remset = rs;
6586 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6587 *(rs->store_next++) = (mword)klass->gc_descr;
6588 *(rs->store_next++) = (mword)count;
6594 * mono_gc_wbarrier_object_copy:
6596 * Write barrier to call when obj is the result of a clone or copy of an object.
6599 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6605 HEAVY_STAT (++stat_wbarrier_object_copy);
6606 rs = REMEMBERED_SET;
6607 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6608 size = mono_object_class (obj)->instance_size;
6610 /* do not copy the sync state */
6611 memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6612 size - sizeof (MonoObject));
6613 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6617 if (rs->store_next < rs->end_set) {
6618 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6622 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6623 rs->next = REMEMBERED_SET;
6624 REMEMBERED_SET = rs;
6625 #ifdef HAVE_KW_THREAD
6626 mono_sgen_thread_info_current ()->remset = rs;
6628 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6633 * ######################################################################
6634 * ######## Collector debugging
6635 * ######################################################################
6638 const char*descriptor_types [] = {
6650 describe_ptr (char *ptr)
6657 if (ptr_in_nursery (ptr)) {
6658 printf ("Pointer inside nursery.\n");
6660 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
6662 printf ("Pointer is the start of object %p in LOS space.\n", start);
6664 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
6666 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
6667 printf ("Pointer inside oldspace.\n");
6668 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
6669 printf ("Pointer is inside a pinned chunk.\n");
6671 printf ("Pointer unknown.\n");
6676 if (object_is_pinned (ptr))
6677 printf ("Object is pinned.\n");
6679 if (object_is_forwarded (ptr))
6680 printf ("Object is forwared.\n");
6682 // FIXME: Handle pointers to the inside of objects
6683 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6685 printf ("VTable: %p\n", vtable);
6686 if (vtable == NULL) {
6687 printf ("VTable is invalid (empty).\n");
6690 if (ptr_in_nursery (vtable)) {
6691 printf ("VTable is invalid (points inside nursery).\n");
6694 printf ("Class: %s\n", vtable->klass->name);
6696 desc = ((GCVTable*)vtable)->desc;
6697 printf ("Descriptor: %lx\n", (long)desc);
6700 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6704 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6710 switch ((*p) & REMSET_TYPE_MASK) {
6711 case REMSET_LOCATION:
6712 if (*p == (mword)addr)
6716 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6718 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6722 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6723 count = safe_object_get_size ((MonoObject*)ptr);
6724 count = ALIGN_UP (count);
6725 count /= sizeof (mword);
6726 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6730 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6734 switch (desc & 0x7) {
6735 case DESC_TYPE_RUN_LENGTH:
6736 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6738 case DESC_TYPE_SMALL_BITMAP:
6739 OBJ_BITMAP_SIZE (skip_size, desc, start);
6743 g_assert_not_reached ();
6746 /* The descriptor includes the size of MonoObject */
6747 skip_size -= sizeof (MonoObject);
6749 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6754 g_assert_not_reached ();
6760 * Return whenever ADDR occurs in the remembered sets
6763 find_in_remsets (char *addr)
6766 SgenThreadInfo *info;
6767 RememberedSet *remset;
6768 GenericStoreRememberedSet *store_remset;
6770 gboolean found = FALSE;
6772 /* the global one */
6773 for (remset = global_remset; remset; remset = remset->next) {
6774 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));
6775 for (p = remset->data; p < remset->store_next;) {
6776 p = find_in_remset_loc (p, addr, &found);
6782 /* the generic store ones */
6783 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6784 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6785 if (store_remset->data [i] == addr)
6790 /* the per-thread ones */
6791 FOREACH_THREAD (info) {
6793 for (remset = info->remset; remset; remset = remset->next) {
6794 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));
6795 for (p = remset->data; p < remset->store_next;) {
6796 p = find_in_remset_loc (p, addr, &found);
6801 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6802 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6805 } END_FOREACH_THREAD
6807 /* the freed thread ones */
6808 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6809 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));
6810 for (p = remset->data; p < remset->store_next;) {
6811 p = find_in_remset_loc (p, addr, &found);
6820 static gboolean missing_remsets;
6823 * We let a missing remset slide if the target object is pinned,
6824 * because the store might have happened but the remset not yet added,
6825 * but in that case the target must be pinned. We might theoretically
6826 * miss some missing remsets this way, but it's very unlikely.
6829 #define HANDLE_PTR(ptr,obj) do { \
6830 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6831 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6832 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); \
6833 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6834 if (!object_is_pinned (*(ptr))) \
6835 missing_remsets = TRUE; \
6841 * Check that each object reference which points into the nursery can
6842 * be found in the remembered sets.
6845 check_consistency_callback (char *start, size_t size, void *dummy)
6847 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6848 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6850 #define SCAN_OBJECT_ACTION
6851 #include "sgen-scan-object.h"
6855 * Perform consistency check of the heap.
6857 * Assumes the world is stopped.
6860 check_consistency (void)
6862 // Need to add more checks
6864 missing_remsets = FALSE;
6866 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6868 // Check that oldspace->newspace pointers are registered with the collector
6869 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6871 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6873 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6875 if (!binary_protocol_is_enabled ())
6876 g_assert (!missing_remsets);
6881 #define HANDLE_PTR(ptr,obj) do { \
6882 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
6883 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
6887 check_major_refs_callback (char *start, size_t size, void *dummy)
6889 #define SCAN_OBJECT_ACTION
6890 #include "sgen-scan-object.h"
6894 check_major_refs (void)
6896 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6897 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6900 /* Check that the reference is valid */
6902 #define HANDLE_PTR(ptr,obj) do { \
6904 g_assert (safe_name (*(ptr)) != NULL); \
6911 * Perform consistency check on an object. Currently we only check that the
6912 * reference fields are valid.
6915 check_object (char *start)
6920 #include "sgen-scan-object.h"
6924 * ######################################################################
6925 * ######## Other mono public interface functions.
6926 * ######################################################################
6929 #define REFS_SIZE 128
6932 MonoGCReferences callback;
6936 MonoObject *refs [REFS_SIZE];
6937 uintptr_t offsets [REFS_SIZE];
6941 #define HANDLE_PTR(ptr,obj) do { \
6943 if (hwi->count == REFS_SIZE) { \
6944 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
6948 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
6949 hwi->refs [hwi->count++] = *(ptr); \
6954 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6956 #include "sgen-scan-object.h"
6960 walk_references (char *start, size_t size, void *data)
6962 HeapWalkInfo *hwi = data;
6965 collect_references (hwi, start, size);
6966 if (hwi->count || !hwi->called)
6967 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6971 * mono_gc_walk_heap:
6972 * @flags: flags for future use
6973 * @callback: a function pointer called for each object in the heap
6974 * @data: a user data pointer that is passed to callback
6976 * This function can be used to iterate over all the live objects in the heap:
6977 * for each object, @callback is invoked, providing info about the object's
6978 * location in memory, its class, its size and the objects it references.
6979 * For each referenced object it's offset from the object address is
6980 * reported in the offsets array.
6981 * The object references may be buffered, so the callback may be invoked
6982 * multiple times for the same object: in all but the first call, the size
6983 * argument will be zero.
6984 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6985 * profiler event handler.
6987 * Returns: a non-zero value if the GC doesn't support heap walking
6990 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6995 hwi.callback = callback;
6998 clear_nursery_fragments (nursery_next);
6999 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
7001 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
7002 mono_sgen_los_iterate_objects (walk_references, &hwi);
7008 mono_gc_collect (int generation)
7013 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
7014 stop_world (generation);
7015 if (generation == 0) {
7016 collect_nursery (0);
7018 major_collection ("user request");
7020 restart_world (generation);
7021 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
7026 mono_gc_max_generation (void)
7032 mono_gc_collection_count (int generation)
7034 if (generation == 0)
7035 return num_minor_gcs;
7036 return num_major_gcs;
7040 mono_gc_get_used_size (void)
7044 tot = los_memory_usage;
7045 tot += nursery_section->next_data - nursery_section->data;
7046 tot += major_collector.get_used_size ();
7047 /* FIXME: account for pinned objects */
7053 mono_gc_get_heap_size (void)
7059 mono_gc_disable (void)
7067 mono_gc_enable (void)
7075 mono_gc_get_los_limit (void)
7077 return MAX_SMALL_OBJ_SIZE;
7081 mono_object_is_alive (MonoObject* o)
7087 mono_gc_get_generation (MonoObject *obj)
7089 if (ptr_in_nursery (obj))
7095 mono_gc_enable_events (void)
7100 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7103 mono_gc_register_disappearing_link (obj, link_addr, track);
7108 mono_gc_weak_link_remove (void **link_addr)
7111 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7116 mono_gc_weak_link_get (void **link_addr)
7120 return (MonoObject*) REVEAL_POINTER (*link_addr);
7124 mono_gc_ephemeron_array_add (MonoObject *obj)
7126 EphemeronLinkNode *node;
7130 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
7135 node->array = (char*)obj;
7136 node->next = ephemeron_list;
7137 ephemeron_list = node;
7139 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
7146 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7149 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
7150 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7151 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7153 mword complex = alloc_complex_descriptor (bitmap, numbits);
7154 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7158 static void *all_ref_root_descrs [32];
7161 mono_gc_make_root_descr_all_refs (int numbits)
7166 if (numbits < 32 && all_ref_root_descrs [numbits])
7167 return all_ref_root_descrs [numbits];
7169 gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
7170 memset (gc_bitmap, 0xff, numbits / 8);
7171 if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT))
7172 gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
7174 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
7175 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
7179 all_ref_root_descrs [numbits] = descr;
7185 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7189 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7190 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7191 user_descriptors [user_descriptors_next ++] = marker;
7197 mono_gc_alloc_fixed (size_t size, void *descr)
7199 /* FIXME: do a single allocation */
7200 void *res = calloc (1, size);
7203 if (!mono_gc_register_root (res, size, descr)) {
7211 mono_gc_free_fixed (void* addr)
7213 mono_gc_deregister_root (addr);
7218 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7222 result = func (data);
7223 UNLOCK_INTERRUPTION;
7228 mono_gc_is_gc_thread (void)
7232 result = mono_sgen_thread_info_current () != NULL;
7238 mono_gc_base_init (void)
7242 char *major_collector_opt = NULL;
7243 struct sigaction sinfo;
7247 /* the gc_initialized guard seems to imply this method is
7248 idempotent, but LOCK_INIT(gc_mutex) might not be. It's
7249 defined in sgen-gc.h as nothing, so there's no danger at
7251 LOCK_INIT (gc_mutex);
7253 if (gc_initialized) {
7257 pagesize = mono_pagesize ();
7258 gc_debug_file = stdout;
7260 LOCK_INIT (interruption_mutex);
7261 LOCK_INIT (global_remset_mutex);
7262 LOCK_INIT (pin_queue_mutex);
7264 if ((env = getenv ("MONO_GC_PARAMS"))) {
7265 opts = g_strsplit (env, ",", -1);
7266 for (ptr = opts; *ptr; ++ptr) {
7268 if (g_str_has_prefix (opt, "major=")) {
7269 opt = strchr (opt, '=') + 1;
7270 major_collector_opt = g_strdup (opt);
7278 mono_sgen_init_internal_allocator ();
7280 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
7281 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
7282 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
7283 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
7284 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
7285 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
7286 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
7287 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
7288 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
7290 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
7291 mono_sgen_marksweep_init (&major_collector);
7292 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
7293 mono_sgen_marksweep_fixed_init (&major_collector);
7294 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
7295 mono_sgen_marksweep_par_init (&major_collector);
7296 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
7297 mono_sgen_marksweep_fixed_par_init (&major_collector);
7298 } else if (!strcmp (major_collector_opt, "copying")) {
7299 mono_sgen_copying_init (&major_collector);
7301 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
7305 #ifdef SGEN_HAVE_CARDTABLE
7306 use_cardtable = major_collector.supports_cardtable;
7308 use_cardtable = FALSE;
7311 num_workers = mono_cpu_count ();
7312 g_assert (num_workers > 0);
7313 if (num_workers > 16)
7316 /* Keep this the default for now */
7317 conservative_stack_mark = TRUE;
7320 for (ptr = opts; *ptr; ++ptr) {
7322 if (g_str_has_prefix (opt, "major="))
7324 if (g_str_has_prefix (opt, "wbarrier=")) {
7325 opt = strchr (opt, '=') + 1;
7326 if (strcmp (opt, "remset") == 0) {
7327 use_cardtable = FALSE;
7328 } else if (strcmp (opt, "cardtable") == 0) {
7329 if (!use_cardtable) {
7330 if (major_collector.supports_cardtable)
7331 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
7333 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
7339 if (g_str_has_prefix (opt, "max-heap-size=")) {
7340 opt = strchr (opt, '=') + 1;
7341 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
7342 if ((max_heap & (mono_pagesize () - 1))) {
7343 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
7347 fprintf (stderr, "max-heap-size must be an integer.\n");
7352 if (g_str_has_prefix (opt, "workers=")) {
7355 if (!major_collector.is_parallel) {
7356 fprintf (stderr, "The workers= option can only be used for parallel collectors.");
7359 opt = strchr (opt, '=') + 1;
7360 val = strtol (opt, &endptr, 10);
7361 if (!*opt || *endptr) {
7362 fprintf (stderr, "Cannot parse the workers= option value.");
7365 if (val <= 0 || val > 16) {
7366 fprintf (stderr, "The number of workers must be in the range 1 to 16.");
7369 num_workers = (int)val;
7372 if (g_str_has_prefix (opt, "stack-mark=")) {
7373 opt = strchr (opt, '=') + 1;
7374 if (!strcmp (opt, "precise")) {
7375 conservative_stack_mark = FALSE;
7376 } else if (!strcmp (opt, "conservative")) {
7377 conservative_stack_mark = TRUE;
7379 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
7385 if (g_str_has_prefix (opt, "nursery-size=")) {
7387 opt = strchr (opt, '=') + 1;
7388 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
7389 default_nursery_size = val;
7390 #ifdef SGEN_ALIGN_NURSERY
7391 if ((val & (val - 1))) {
7392 fprintf (stderr, "The nursery size must be a power of two.\n");
7396 default_nursery_bits = 0;
7397 while (1 << (++ default_nursery_bits) != default_nursery_size)
7401 fprintf (stderr, "nursery-size must be an integer.\n");
7407 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
7408 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
7409 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7410 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7411 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
7412 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
7413 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
7414 if (major_collector.print_gc_param_usage)
7415 major_collector.print_gc_param_usage ();
7422 if (major_collector.is_parallel)
7423 workers_init (num_workers);
7425 if (major_collector_opt)
7426 g_free (major_collector_opt);
7428 nursery_size = DEFAULT_NURSERY_SIZE;
7429 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7430 init_heap_size_limits (max_heap);
7434 if ((env = getenv ("MONO_GC_DEBUG"))) {
7435 opts = g_strsplit (env, ",", -1);
7436 for (ptr = opts; ptr && *ptr; ptr ++) {
7438 if (opt [0] >= '0' && opt [0] <= '9') {
7439 gc_debug_level = atoi (opt);
7444 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7445 gc_debug_file = fopen (rf, "wb");
7447 gc_debug_file = stderr;
7450 } else if (!strcmp (opt, "collect-before-allocs")) {
7451 collect_before_allocs = 1;
7452 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
7453 char *arg = strchr (opt, '=') + 1;
7454 collect_before_allocs = atoi (arg);
7455 } else if (!strcmp (opt, "check-at-minor-collections")) {
7456 consistency_check_at_minor_collection = TRUE;
7457 nursery_clear_policy = CLEAR_AT_GC;
7458 } else if (!strcmp (opt, "xdomain-checks")) {
7459 xdomain_checks = TRUE;
7460 } else if (!strcmp (opt, "clear-at-gc")) {
7461 nursery_clear_policy = CLEAR_AT_GC;
7462 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
7463 nursery_clear_policy = CLEAR_AT_GC;
7464 } else if (!strcmp (opt, "check-scan-starts")) {
7465 do_scan_starts_check = TRUE;
7466 } else if (!strcmp (opt, "disable-minor")) {
7467 disable_minor_collections = TRUE;
7468 } else if (!strcmp (opt, "disable-major")) {
7469 disable_major_collections = TRUE;
7470 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7471 char *filename = strchr (opt, '=') + 1;
7472 nursery_clear_policy = CLEAR_AT_GC;
7473 heap_dump_file = fopen (filename, "w");
7475 fprintf (heap_dump_file, "<sgen-dump>\n");
7476 #ifdef SGEN_BINARY_PROTOCOL
7477 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7478 char *filename = strchr (opt, '=') + 1;
7479 binary_protocol_init (filename);
7482 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7483 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7484 fprintf (stderr, "Valid options are:\n");
7485 fprintf (stderr, " collect-before-allocs[=<n>]\n");
7486 fprintf (stderr, " check-at-minor-collections\n");
7487 fprintf (stderr, " disable-minor\n");
7488 fprintf (stderr, " disable-major\n");
7489 fprintf (stderr, " xdomain-checks\n");
7490 fprintf (stderr, " clear-at-gc\n");
7497 if (major_collector.post_param_init)
7498 major_collector.post_param_init ();
7500 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7501 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7503 sigfillset (&sinfo.sa_mask);
7504 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7505 sinfo.sa_sigaction = suspend_handler;
7506 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7507 g_error ("failed sigaction");
7510 sinfo.sa_handler = restart_handler;
7511 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7512 g_error ("failed sigaction");
7515 sigfillset (&suspend_signal_mask);
7516 sigdelset (&suspend_signal_mask, restart_signal_num);
7518 global_remset = alloc_remset (1024, NULL);
7519 global_remset->next = NULL;
7521 pthread_key_create (&remembered_set_key, unregister_thread);
7523 #ifndef HAVE_KW_THREAD
7524 pthread_key_create (&thread_info_key, NULL);
7530 gc_initialized = TRUE;
7532 mono_gc_register_thread (&sinfo);
7536 mono_gc_get_suspend_signal (void)
7538 return suspend_signal_num;
7548 #ifdef HAVE_KW_THREAD
7549 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7550 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7551 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7552 mono_mb_emit_i4 ((mb), (offset)); \
7557 * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
7558 * where the two are the same.
7561 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7562 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7563 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7564 mono_mb_emit_i4 ((mb), thread_info_key); \
7565 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7566 mono_mb_emit_byte ((mb), CEE_ADD); \
7567 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7570 #define EMIT_TLS_ACCESS(mb,member,dummy) do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
7575 #ifdef MANAGED_ALLOCATION
7576 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7577 * for each class. This is currently not easy to do, as it is hard to generate basic
7578 * blocks + branches, but it is easy with the linear IL codebase.
7580 * For this to work we'd need to solve the TLAB race, first. Now we
7581 * require the allocator to be in a few known methods to make sure
7582 * that they are executed atomically via the restart mechanism.
7585 create_allocator (int atype)
7587 int p_var, size_var;
7588 guint32 slowpath_branch, max_size_branch;
7589 MonoMethodBuilder *mb;
7591 MonoMethodSignature *csig;
7592 static gboolean registered = FALSE;
7593 int tlab_next_addr_var, new_next_var;
7595 const char *name = NULL;
7596 AllocatorWrapperInfo *info;
7598 #ifdef HAVE_KW_THREAD
7599 int tlab_next_addr_offset = -1;
7600 int tlab_temp_end_offset = -1;
7602 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7603 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7605 g_assert (tlab_next_addr_offset != -1);
7606 g_assert (tlab_temp_end_offset != -1);
7610 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7611 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7615 if (atype == ATYPE_SMALL) {
7617 name = "AllocSmall";
7618 } else if (atype == ATYPE_NORMAL) {
7621 } else if (atype == ATYPE_VECTOR) {
7623 name = "AllocVector";
7625 g_assert_not_reached ();
7628 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7629 csig->ret = &mono_defaults.object_class->byval_arg;
7630 for (i = 0; i < num_params; ++i)
7631 csig->params [i] = &mono_defaults.int_class->byval_arg;
7633 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7634 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7635 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7636 /* size = vtable->klass->instance_size; */
7637 mono_mb_emit_ldarg (mb, 0);
7638 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7639 mono_mb_emit_byte (mb, CEE_ADD);
7640 mono_mb_emit_byte (mb, CEE_LDIND_I);
7641 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7642 mono_mb_emit_byte (mb, CEE_ADD);
7643 /* FIXME: assert instance_size stays a 4 byte integer */
7644 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7645 mono_mb_emit_stloc (mb, size_var);
7646 } else if (atype == ATYPE_VECTOR) {
7647 MonoExceptionClause *clause;
7649 MonoClass *oom_exc_class;
7652 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7653 mono_mb_emit_ldarg (mb, 1);
7654 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7655 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7656 mono_mb_emit_exception (mb, "OverflowException", NULL);
7657 mono_mb_patch_short_branch (mb, pos);
7659 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7660 clause->try_offset = mono_mb_get_label (mb);
7662 /* vtable->klass->sizes.element_size */
7663 mono_mb_emit_ldarg (mb, 0);
7664 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7665 mono_mb_emit_byte (mb, CEE_ADD);
7666 mono_mb_emit_byte (mb, CEE_LDIND_I);
7667 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7668 mono_mb_emit_byte (mb, CEE_ADD);
7669 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7672 mono_mb_emit_ldarg (mb, 1);
7673 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7674 /* + sizeof (MonoArray) */
7675 mono_mb_emit_icon (mb, sizeof (MonoArray));
7676 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7677 mono_mb_emit_stloc (mb, size_var);
7679 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7682 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7683 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7684 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7685 "System", "OverflowException");
7686 g_assert (clause->data.catch_class);
7687 clause->handler_offset = mono_mb_get_label (mb);
7689 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7690 "System", "OutOfMemoryException");
7691 g_assert (oom_exc_class);
7692 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7695 mono_mb_emit_byte (mb, CEE_POP);
7696 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7697 mono_mb_emit_byte (mb, CEE_THROW);
7699 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7700 mono_mb_set_clauses (mb, 1, clause);
7701 mono_mb_patch_branch (mb, pos_leave);
7704 g_assert_not_reached ();
7707 /* size += ALLOC_ALIGN - 1; */
7708 mono_mb_emit_ldloc (mb, size_var);
7709 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7710 mono_mb_emit_byte (mb, CEE_ADD);
7711 /* size &= ~(ALLOC_ALIGN - 1); */
7712 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7713 mono_mb_emit_byte (mb, CEE_AND);
7714 mono_mb_emit_stloc (mb, size_var);
7716 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7717 if (atype != ATYPE_SMALL) {
7718 mono_mb_emit_ldloc (mb, size_var);
7719 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7720 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7724 * We need to modify tlab_next, but the JIT only supports reading, so we read
7725 * another tls var holding its address instead.
7728 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7729 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7730 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7731 mono_mb_emit_stloc (mb, tlab_next_addr_var);
7733 /* p = (void**)tlab_next; */
7734 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7735 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7736 mono_mb_emit_byte (mb, CEE_LDIND_I);
7737 mono_mb_emit_stloc (mb, p_var);
7739 /* new_next = (char*)p + size; */
7740 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7741 mono_mb_emit_ldloc (mb, p_var);
7742 mono_mb_emit_ldloc (mb, size_var);
7743 mono_mb_emit_byte (mb, CEE_CONV_I);
7744 mono_mb_emit_byte (mb, CEE_ADD);
7745 mono_mb_emit_stloc (mb, new_next_var);
7747 /* tlab_next = new_next */
7748 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7749 mono_mb_emit_ldloc (mb, new_next_var);
7750 mono_mb_emit_byte (mb, CEE_STIND_I);
7752 /* if (G_LIKELY (new_next < tlab_temp_end)) */
7753 mono_mb_emit_ldloc (mb, new_next_var);
7754 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7755 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7758 if (atype != ATYPE_SMALL)
7759 mono_mb_patch_short_branch (mb, max_size_branch);
7761 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7762 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7764 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7765 mono_mb_emit_ldarg (mb, 0);
7766 mono_mb_emit_ldloc (mb, size_var);
7767 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7768 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7769 } else if (atype == ATYPE_VECTOR) {
7770 mono_mb_emit_ldarg (mb, 1);
7771 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7773 g_assert_not_reached ();
7775 mono_mb_emit_byte (mb, CEE_RET);
7778 mono_mb_patch_short_branch (mb, slowpath_branch);
7780 /* FIXME: Memory barrier */
7783 mono_mb_emit_ldloc (mb, p_var);
7784 mono_mb_emit_ldarg (mb, 0);
7785 mono_mb_emit_byte (mb, CEE_STIND_I);
7787 if (atype == ATYPE_VECTOR) {
7788 /* arr->max_length = max_length; */
7789 mono_mb_emit_ldloc (mb, p_var);
7790 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7791 mono_mb_emit_ldarg (mb, 1);
7792 mono_mb_emit_byte (mb, CEE_STIND_I);
7796 mono_mb_emit_ldloc (mb, p_var);
7797 mono_mb_emit_byte (mb, CEE_RET);
7799 res = mono_mb_create_method (mb, csig, 8);
7801 mono_method_get_header (res)->init_locals = FALSE;
7803 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7804 info->gc_name = "sgen";
7805 info->alloc_type = atype;
7806 mono_marshal_set_wrapper_info (res, info);
7813 mono_gc_get_gc_name (void)
7818 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7819 static MonoMethod *write_barrier_method;
7822 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7828 if (!mono_thread_internal_current ())
7829 /* Happens during thread attach */
7834 ji = mono_jit_info_table_find (domain, ip);
7837 method = ji->method;
7839 if (method == write_barrier_method)
7841 for (i = 0; i < ATYPE_NUM; ++i)
7842 if (method == alloc_method_cache [i])
7848 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7849 * The signature of the called method is:
7850 * object allocate (MonoVTable *vtable)
7853 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7855 #ifdef MANAGED_ALLOCATION
7856 MonoClass *klass = vtable->klass;
7858 #ifdef HAVE_KW_THREAD
7859 int tlab_next_offset = -1;
7860 int tlab_temp_end_offset = -1;
7861 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7862 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7864 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7868 if (!mono_runtime_has_tls_get ())
7870 if (klass->instance_size > tlab_size)
7872 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7876 if (klass->byval_arg.type == MONO_TYPE_STRING)
7878 if (collect_before_allocs)
7881 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7882 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7884 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7891 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7893 #ifdef MANAGED_ALLOCATION
7894 MonoClass *klass = vtable->klass;
7896 #ifdef HAVE_KW_THREAD
7897 int tlab_next_offset = -1;
7898 int tlab_temp_end_offset = -1;
7899 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7900 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7902 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7908 if (!mono_runtime_has_tls_get ())
7910 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7912 if (collect_before_allocs)
7914 g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
7916 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7923 mono_gc_get_managed_allocator_by_type (int atype)
7925 #ifdef MANAGED_ALLOCATION
7928 if (!mono_runtime_has_tls_get ())
7931 mono_loader_lock ();
7932 res = alloc_method_cache [atype];
7934 res = alloc_method_cache [atype] = create_allocator (atype);
7935 mono_loader_unlock ();
7943 mono_gc_get_managed_allocator_types (void)
7950 mono_gc_get_write_barrier (void)
7953 MonoMethodBuilder *mb;
7954 MonoMethodSignature *sig;
7955 #ifdef MANAGED_WBARRIER
7956 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7957 #ifndef SGEN_ALIGN_NURSERY
7958 int label_continue_1, label_continue_2, label_no_wb_5;
7959 int dereferenced_var;
7961 int buffer_var, buffer_index_var, dummy_var;
7963 #ifdef HAVE_KW_THREAD
7964 int stack_end_offset = -1, store_remset_buffer_offset = -1;
7965 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7967 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7968 g_assert (stack_end_offset != -1);
7969 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7970 g_assert (store_remset_buffer_offset != -1);
7971 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7972 g_assert (store_remset_buffer_index_offset != -1);
7973 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7974 g_assert (store_remset_buffer_index_addr_offset != -1);
7978 g_assert (!use_cardtable);
7980 // FIXME: Maybe create a separate version for ctors (the branch would be
7981 // correctly predicted more times)
7982 if (write_barrier_method)
7983 return write_barrier_method;
7985 /* Create the IL version of mono_gc_barrier_generic_store () */
7986 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7987 sig->ret = &mono_defaults.void_class->byval_arg;
7988 sig->params [0] = &mono_defaults.int_class->byval_arg;
7990 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7992 #ifdef MANAGED_WBARRIER
7993 if (mono_runtime_has_tls_get ()) {
7994 #ifdef SGEN_ALIGN_NURSERY
7995 // if (ptr_in_nursery (ptr)) return;
7997 * Masking out the bits might be faster, but we would have to use 64 bit
7998 * immediates, which might be slower.
8000 mono_mb_emit_ldarg (mb, 0);
8001 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8002 mono_mb_emit_byte (mb, CEE_SHR_UN);
8003 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8004 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
8006 // if (!ptr_in_nursery (*ptr)) return;
8007 mono_mb_emit_ldarg (mb, 0);
8008 mono_mb_emit_byte (mb, CEE_LDIND_I);
8009 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8010 mono_mb_emit_byte (mb, CEE_SHR_UN);
8011 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8012 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
8015 // if (ptr < (nursery_start)) goto continue;
8016 mono_mb_emit_ldarg (mb, 0);
8017 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8018 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
8020 // if (ptr >= nursery_end)) goto continue;
8021 mono_mb_emit_ldarg (mb, 0);
8022 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
8023 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
8026 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
8029 mono_mb_patch_branch (mb, label_continue_1);
8030 mono_mb_patch_branch (mb, label_continue_2);
8032 // Dereference and store in local var
8033 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8034 mono_mb_emit_ldarg (mb, 0);
8035 mono_mb_emit_byte (mb, CEE_LDIND_I);
8036 mono_mb_emit_stloc (mb, dereferenced_var);
8038 // if (*ptr < nursery_start) return;
8039 mono_mb_emit_ldloc (mb, dereferenced_var);
8040 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8041 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
8043 // if (*ptr >= nursery_end) return;
8044 mono_mb_emit_ldloc (mb, dereferenced_var);
8045 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
8046 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
8049 // if (ptr >= stack_end) goto need_wb;
8050 mono_mb_emit_ldarg (mb, 0);
8051 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
8052 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
8054 // if (ptr >= stack_start) return;
8055 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8056 mono_mb_emit_ldarg (mb, 0);
8057 mono_mb_emit_ldloc_addr (mb, dummy_var);
8058 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
8061 mono_mb_patch_branch (mb, label_need_wb);
8063 // buffer = STORE_REMSET_BUFFER;
8064 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8065 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
8066 mono_mb_emit_stloc (mb, buffer_var);
8068 // buffer_index = STORE_REMSET_BUFFER_INDEX;
8069 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8070 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
8071 mono_mb_emit_stloc (mb, buffer_index_var);
8073 // if (buffer [buffer_index] == ptr) return;
8074 mono_mb_emit_ldloc (mb, buffer_var);
8075 mono_mb_emit_ldloc (mb, buffer_index_var);
8076 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8077 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8078 mono_mb_emit_byte (mb, CEE_SHL);
8079 mono_mb_emit_byte (mb, CEE_ADD);
8080 mono_mb_emit_byte (mb, CEE_LDIND_I);
8081 mono_mb_emit_ldarg (mb, 0);
8082 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
8085 mono_mb_emit_ldloc (mb, buffer_index_var);
8086 mono_mb_emit_icon (mb, 1);
8087 mono_mb_emit_byte (mb, CEE_ADD);
8088 mono_mb_emit_stloc (mb, buffer_index_var);
8090 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
8091 mono_mb_emit_ldloc (mb, buffer_index_var);
8092 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
8093 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
8095 // buffer [buffer_index] = ptr;
8096 mono_mb_emit_ldloc (mb, buffer_var);
8097 mono_mb_emit_ldloc (mb, buffer_index_var);
8098 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8099 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8100 mono_mb_emit_byte (mb, CEE_SHL);
8101 mono_mb_emit_byte (mb, CEE_ADD);
8102 mono_mb_emit_ldarg (mb, 0);
8103 mono_mb_emit_byte (mb, CEE_STIND_I);
8105 // STORE_REMSET_BUFFER_INDEX = buffer_index;
8106 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8107 mono_mb_emit_ldloc (mb, buffer_index_var);
8108 mono_mb_emit_byte (mb, CEE_STIND_I);
8111 mono_mb_patch_branch (mb, label_no_wb_1);
8112 mono_mb_patch_branch (mb, label_no_wb_2);
8113 mono_mb_patch_branch (mb, label_no_wb_3);
8114 mono_mb_patch_branch (mb, label_no_wb_4);
8115 #ifndef SGEN_ALIGN_NURSERY
8116 mono_mb_patch_branch (mb, label_no_wb_5);
8118 mono_mb_emit_byte (mb, CEE_RET);
8121 mono_mb_patch_branch (mb, label_slow_path);
8125 mono_mb_emit_ldarg (mb, 0);
8126 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
8127 mono_mb_emit_byte (mb, CEE_RET);
8129 res = mono_mb_create_method (mb, sig, 16);
8132 mono_loader_lock ();
8133 if (write_barrier_method) {
8134 /* Already created */
8135 mono_free_method (res);
8137 /* double-checked locking */
8138 mono_memory_barrier ();
8139 write_barrier_method = res;
8141 mono_loader_unlock ();
8143 return write_barrier_method;
8147 mono_gc_get_description (void)
8149 return g_strdup ("sgen");
8153 mono_gc_set_desktop_mode (void)
8158 mono_gc_is_moving (void)
8164 mono_gc_is_disabled (void)
8170 mono_sgen_debug_printf (int level, const char *format, ...)
8174 if (level > gc_debug_level)
8177 va_start (ap, format);
8178 vfprintf (gc_debug_file, format, ap);
8183 mono_sgen_get_logfile (void)
8185 return gc_debug_file;
8189 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
8195 #endif /* HAVE_SGEN_GC */