2 * sgen-gc.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
11 * Thread start/stop adapted from Boehm's GC:
12 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
13 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
14 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
15 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
16 * Copyright 2001-2003 Ximian, Inc
17 * Copyright 2003-2010 Novell, Inc.
18 * Copyright 2011 Xamarin, Inc.
19 * Copyright (C) 2012 Xamarin Inc
21 * This library is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU Library General Public
23 * License 2.0 as published by the Free Software Foundation;
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License 2.0 along with this library; if not, write to the Free
32 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 * Important: allocation provides always zeroed memory, having to do
35 * a memset after allocation is deadly for performance.
36 * Memory usage at startup is currently as follows:
38 * 64 KB internal space
40 * We should provide a small memory config with half the sizes
42 * We currently try to make as few mono assumptions as possible:
43 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
45 * 2) gc descriptor is the second word in the vtable (first word in the class)
46 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
47 * 4) there is a function to get an object's size and the number of
48 * elements in an array.
49 * 5) we know the special way bounds are allocated for complex arrays
50 * 6) we know about proxies and how to treat them when domains are unloaded
52 * Always try to keep stack usage to a minimum: no recursive behaviour
53 * and no large stack allocs.
55 * General description.
56 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
57 * When the nursery is full we start a nursery collection: this is performed with a
59 * When the old generation is full we start a copying GC of the old generation as well:
60 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
61 * in the future. Maybe we'll even do both during the same collection like IMMIX.
63 * The things that complicate this description are:
64 * *) pinned objects: we can't move them so we need to keep track of them
65 * *) no precise info of the thread stacks and registers: we need to be able to
66 * quickly find the objects that may be referenced conservatively and pin them
67 * (this makes the first issues more important)
68 * *) large objects are too expensive to be dealt with using copying GC: we handle them
69 * with mark/sweep during major collections
70 * *) some objects need to not move even if they are small (interned strings, Type handles):
71 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
72 * PinnedChunks regions
78 *) we could have a function pointer in MonoClass to implement
79 customized write barriers for value types
81 *) investigate the stuff needed to advance a thread to a GC-safe
82 point (single-stepping, read from unmapped memory etc) and implement it.
83 This would enable us to inline allocations and write barriers, for example,
84 or at least parts of them, like the write barrier checks.
85 We may need this also for handling precise info on stacks, even simple things
86 as having uninitialized data on the stack and having to wait for the prolog
87 to zero it. Not an issue for the last frame that we scan conservatively.
88 We could always not trust the value in the slots anyway.
90 *) modify the jit to save info about references in stack locations:
91 this can be done just for locals as a start, so that at least
92 part of the stack is handled precisely.
94 *) test/fix endianess issues
96 *) Implement a card table as the write barrier instead of remembered
97 sets? Card tables are not easy to implement with our current
98 memory layout. We have several different kinds of major heap
99 objects: Small objects in regular blocks, small objects in pinned
100 chunks and LOS objects. If we just have a pointer we have no way
101 to tell which kind of object it points into, therefore we cannot
102 know where its card table is. The least we have to do to make
103 this happen is to get rid of write barriers for indirect stores.
106 *) Get rid of write barriers for indirect stores. We can do this by
107 telling the GC to wbarrier-register an object once we do an ldloca
108 or ldelema on it, and to unregister it once it's not used anymore
109 (it can only travel downwards on the stack). The problem with
110 unregistering is that it needs to happen eventually no matter
111 what, even if exceptions are thrown, the thread aborts, etc.
112 Rodrigo suggested that we could do only the registering part and
113 let the collector find out (pessimistically) when it's safe to
114 unregister, namely when the stack pointer of the thread that
115 registered the object is higher than it was when the registering
116 happened. This might make for a good first implementation to get
117 some data on performance.
119 *) Some sort of blacklist support? Blacklists is a concept from the
120 Boehm GC: if during a conservative scan we find pointers to an
121 area which we might use as heap, we mark that area as unusable, so
122 pointer retention by random pinning pointers is reduced.
124 *) experiment with max small object size (very small right now - 2kb,
125 because it's tied to the max freelist size)
127 *) add an option to mmap the whole heap in one chunk: it makes for many
128 simplifications in the checks (put the nursery at the top and just use a single
129 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
130 not flexible (too much of the address space may be used by default or we can't
131 increase the heap as needed) and we'd need a race-free mechanism to return memory
132 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
133 was written to, munmap is needed, but the following mmap may not find the same segment
136 *) memzero the major fragments after restarting the world and optionally a smaller
139 *) investigate having fragment zeroing threads
141 *) separate locks for finalization and other minor stuff to reduce
144 *) try a different copying order to improve memory locality
146 *) a thread abort after a store but before the write barrier will
147 prevent the write barrier from executing
149 *) specialized dynamically generated markers/copiers
151 *) Dynamically adjust TLAB size to the number of threads. If we have
152 too many threads that do allocation, we might need smaller TLABs,
153 and we might get better performance with larger TLABs if we only
154 have a handful of threads. We could sum up the space left in all
155 assigned TLABs and if that's more than some percentage of the
156 nursery size, reduce the TLAB size.
158 *) Explore placing unreachable objects on unused nursery memory.
159 Instead of memset'ng a region to zero, place an int[] covering it.
160 A good place to start is add_nursery_frag. The tricky thing here is
161 placing those objects atomically outside of a collection.
163 *) Allocation should use asymmetric Dekker synchronization:
164 http://blogs.oracle.com/dave/resource/Asymmetric-Dekker-Synchronization.txt
165 This should help weak consistency archs.
172 #define _XOPEN_SOURCE
173 #define _DARWIN_C_SOURCE
179 #ifdef HAVE_PTHREAD_H
182 #ifdef HAVE_SEMAPHORE_H
183 #include <semaphore.h>
191 #include "metadata/sgen-gc.h"
192 #include "metadata/metadata-internals.h"
193 #include "metadata/class-internals.h"
194 #include "metadata/gc-internal.h"
195 #include "metadata/object-internals.h"
196 #include "metadata/threads.h"
197 #include "metadata/sgen-cardtable.h"
198 #include "metadata/sgen-ssb.h"
199 #include "metadata/sgen-protocol.h"
200 #include "metadata/sgen-archdep.h"
201 #include "metadata/sgen-bridge.h"
202 #include "metadata/sgen-memory-governor.h"
203 #include "metadata/sgen-hash-table.h"
204 #include "metadata/mono-gc.h"
205 #include "metadata/method-builder.h"
206 #include "metadata/profiler-private.h"
207 #include "metadata/monitor.h"
208 #include "metadata/threadpool-internals.h"
209 #include "metadata/mempool-internals.h"
210 #include "metadata/marshal.h"
211 #include "metadata/runtime.h"
212 #include "metadata/sgen-cardtable.h"
213 #include "metadata/sgen-pinning.h"
214 #include "metadata/sgen-workers.h"
215 #include "utils/mono-mmap.h"
216 #include "utils/mono-time.h"
217 #include "utils/mono-semaphore.h"
218 #include "utils/mono-counters.h"
219 #include "utils/mono-proclib.h"
220 #include "utils/mono-memory-model.h"
221 #include "utils/mono-logger-internal.h"
222 #include "utils/dtrace.h"
224 #include <mono/utils/mono-logger-internal.h>
225 #include <mono/utils/memcheck.h>
227 #if defined(__MACH__)
228 #include "utils/mach-support.h"
231 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
235 #include "mono/cil/opcode.def"
241 #undef pthread_create
243 #undef pthread_detach
246 * ######################################################################
247 * ######## Types and constants used by the GC.
248 * ######################################################################
251 /* 0 means not initialized, 1 is initialized, -1 means in progress */
252 static int gc_initialized = 0;
253 /* If set, check if we need to do something every X allocations */
254 gboolean has_per_allocation_action;
255 /* If set, do a heap check every X allocation */
256 guint32 verify_before_allocs = 0;
257 /* If set, do a minor collection before every X allocation */
258 guint32 collect_before_allocs = 0;
259 /* If set, do a whole heap check before each collection */
260 static gboolean whole_heap_check_before_collection = FALSE;
261 /* If set, do a heap consistency check before each minor collection */
262 static gboolean consistency_check_at_minor_collection = FALSE;
263 /* If set, check whether mark bits are consistent after major collections */
264 static gboolean check_mark_bits_after_major_collection = FALSE;
265 /* If set, check that all nursery objects are pinned/not pinned, depending on context */
266 static gboolean check_nursery_objects_pinned = FALSE;
267 /* If set, do a few checks when the concurrent collector is used */
268 static gboolean do_concurrent_checks = FALSE;
269 /* If set, check that there are no references to the domain left at domain unload */
270 static gboolean xdomain_checks = FALSE;
271 /* If not null, dump the heap after each collection into this file */
272 static FILE *heap_dump_file = NULL;
273 /* If set, mark stacks conservatively, even if precise marking is possible */
274 static gboolean conservative_stack_mark = FALSE;
275 /* If set, do a plausibility check on the scan_starts before and after
277 static gboolean do_scan_starts_check = FALSE;
278 static gboolean nursery_collection_is_parallel = FALSE;
279 static gboolean disable_minor_collections = FALSE;
280 static gboolean disable_major_collections = FALSE;
281 gboolean do_pin_stats = FALSE;
282 static gboolean do_verify_nursery = FALSE;
283 static gboolean do_dump_nursery_content = FALSE;
285 #ifdef HEAVY_STATISTICS
286 long long stat_objects_alloced_degraded = 0;
287 long long stat_bytes_alloced_degraded = 0;
289 long long stat_copy_object_called_nursery = 0;
290 long long stat_objects_copied_nursery = 0;
291 long long stat_copy_object_called_major = 0;
292 long long stat_objects_copied_major = 0;
294 long long stat_scan_object_called_nursery = 0;
295 long long stat_scan_object_called_major = 0;
297 long long stat_slots_allocated_in_vain;
299 long long stat_nursery_copy_object_failed_from_space = 0;
300 long long stat_nursery_copy_object_failed_forwarded = 0;
301 long long stat_nursery_copy_object_failed_pinned = 0;
302 long long stat_nursery_copy_object_failed_to_space = 0;
304 static int stat_wbarrier_set_field = 0;
305 static int stat_wbarrier_set_arrayref = 0;
306 static int stat_wbarrier_arrayref_copy = 0;
307 static int stat_wbarrier_generic_store = 0;
308 static int stat_wbarrier_set_root = 0;
309 static int stat_wbarrier_value_copy = 0;
310 static int stat_wbarrier_object_copy = 0;
313 int stat_minor_gcs = 0;
314 int stat_major_gcs = 0;
316 static long long stat_pinned_objects = 0;
318 static long long time_minor_pre_collection_fragment_clear = 0;
319 static long long time_minor_pinning = 0;
320 static long long time_minor_scan_remsets = 0;
321 static long long time_minor_scan_pinned = 0;
322 static long long time_minor_scan_registered_roots = 0;
323 static long long time_minor_scan_thread_data = 0;
324 static long long time_minor_finish_gray_stack = 0;
325 static long long time_minor_fragment_creation = 0;
327 static long long time_major_pre_collection_fragment_clear = 0;
328 static long long time_major_pinning = 0;
329 static long long time_major_scan_pinned = 0;
330 static long long time_major_scan_registered_roots = 0;
331 static long long time_major_scan_thread_data = 0;
332 static long long time_major_scan_alloc_pinned = 0;
333 static long long time_major_scan_finalized = 0;
334 static long long time_major_scan_big_objects = 0;
335 static long long time_major_finish_gray_stack = 0;
336 static long long time_major_free_bigobjs = 0;
337 static long long time_major_los_sweep = 0;
338 static long long time_major_sweep = 0;
339 static long long time_major_fragment_creation = 0;
341 int gc_debug_level = 0;
346 mono_gc_flush_info (void)
348 fflush (gc_debug_file);
352 #define TV_DECLARE SGEN_TV_DECLARE
353 #define TV_GETTIME SGEN_TV_GETTIME
354 #define TV_ELAPSED SGEN_TV_ELAPSED
355 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
357 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
359 NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
361 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
362 #define object_is_pinned SGEN_OBJECT_IS_PINNED
363 #define pin_object SGEN_PIN_OBJECT
364 #define unpin_object SGEN_UNPIN_OBJECT
366 #define ptr_in_nursery sgen_ptr_in_nursery
368 #define LOAD_VTABLE SGEN_LOAD_VTABLE
371 safe_name (void* obj)
373 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
374 return vt->klass->name;
377 #define safe_object_get_size sgen_safe_object_get_size
380 sgen_safe_name (void* obj)
382 return safe_name (obj);
386 * ######################################################################
387 * ######## Global data.
388 * ######################################################################
390 LOCK_DECLARE (gc_mutex);
391 static int gc_disabled = 0;
393 static gboolean use_cardtable;
395 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
397 static mword pagesize = 4096;
398 int degraded_mode = 0;
400 static mword bytes_pinned_from_failed_allocation = 0;
402 GCMemSection *nursery_section = NULL;
403 static mword lowest_heap_address = ~(mword)0;
404 static mword highest_heap_address = 0;
406 LOCK_DECLARE (sgen_interruption_mutex);
407 static LOCK_DECLARE (pin_queue_mutex);
409 #define LOCK_PIN_QUEUE mono_mutex_lock (&pin_queue_mutex)
410 #define UNLOCK_PIN_QUEUE mono_mutex_unlock (&pin_queue_mutex)
412 typedef struct _FinalizeReadyEntry FinalizeReadyEntry;
413 struct _FinalizeReadyEntry {
414 FinalizeReadyEntry *next;
418 typedef struct _EphemeronLinkNode EphemeronLinkNode;
420 struct _EphemeronLinkNode {
421 EphemeronLinkNode *next;
430 int current_collection_generation = -1;
431 volatile gboolean concurrent_collection_in_progress = FALSE;
433 /* objects that are ready to be finalized */
434 static FinalizeReadyEntry *fin_ready_list = NULL;
435 static FinalizeReadyEntry *critical_fin_list = NULL;
437 static EphemeronLinkNode *ephemeron_list;
439 /* registered roots: the key to the hash is the root start address */
441 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
443 SgenHashTable roots_hash [ROOT_TYPE_NUM] = {
444 SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
445 SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
446 SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL)
448 static mword roots_size = 0; /* amount of memory in the root set */
450 #define GC_ROOT_NUM 32
452 int count; /* must be the first field */
453 void *objects [GC_ROOT_NUM];
454 int root_types [GC_ROOT_NUM];
455 uintptr_t extra_info [GC_ROOT_NUM];
459 notify_gc_roots (GCRootReport *report)
463 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
468 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
470 if (report->count == GC_ROOT_NUM)
471 notify_gc_roots (report);
472 report->objects [report->count] = object;
473 report->root_types [report->count] = rtype;
474 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
477 MonoNativeTlsKey thread_info_key;
479 #ifdef HAVE_KW_THREAD
480 __thread SgenThreadInfo *sgen_thread_info;
481 __thread gpointer *store_remset_buffer;
482 __thread long store_remset_buffer_index;
483 __thread char *stack_end;
484 __thread long *store_remset_buffer_index_addr;
487 /* The size of a TLAB */
488 /* The bigger the value, the less often we have to go to the slow path to allocate a new
489 * one, but the more space is wasted by threads not allocating much memory.
491 * FIXME: Make this self-tuning for each thread.
493 guint32 tlab_size = (1024 * 4);
495 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
497 /* Functions supplied by the runtime to be called by the GC */
498 static MonoGCCallbacks gc_callbacks;
500 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
501 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
503 #define ALIGN_UP SGEN_ALIGN_UP
505 #define MOVED_OBJECTS_NUM 64
506 static void *moved_objects [MOVED_OBJECTS_NUM];
507 static int moved_objects_idx = 0;
509 /* Vtable of the objects used to fill out nursery fragments before a collection */
510 static MonoVTable *array_fill_vtable;
512 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
513 MonoNativeThreadId main_gc_thread = NULL;
516 /*Object was pinned during the current collection*/
517 static mword objects_pinned;
520 * ######################################################################
521 * ######## Macros and function declarations.
522 * ######################################################################
526 align_pointer (void *ptr)
528 mword p = (mword)ptr;
529 p += sizeof (gpointer) - 1;
530 p &= ~ (sizeof (gpointer) - 1);
534 typedef SgenGrayQueue GrayQueue;
536 /* forward declarations */
537 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
538 static void scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx);
539 static void scan_finalizer_entries (FinalizeReadyEntry *list, ScanCopyContext ctx);
540 static void report_finalizer_roots (void);
541 static void report_registered_roots (void);
543 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
544 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, ScanCopyContext ctx);
545 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
547 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
550 static void init_stats (void);
552 static int mark_ephemerons_in_range (ScanCopyContext ctx);
553 static void clear_unreachable_ephemerons (gboolean concurrent_cementing, ScanCopyContext ctx);
554 static void null_ephemerons_for_domain (MonoDomain *domain);
556 SgenObjectOperations current_object_ops;
557 SgenMajorCollector major_collector;
558 SgenMinorCollector sgen_minor_collector;
559 static GrayQueue gray_queue;
560 static GrayQueue remember_major_objects_gray_queue;
562 static SgenRemeberedSet remset;
564 /* The gray queue to use from the main collection thread. */
565 #define WORKERS_DISTRIBUTE_GRAY_QUEUE (&gray_queue)
568 * The gray queue a worker job must use. If we're not parallel or
569 * concurrent, we use the main gray queue.
571 static SgenGrayQueue*
572 sgen_workers_get_job_gray_queue (WorkerData *worker_data)
574 return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
577 static gboolean have_non_collection_major_object_remembers = FALSE;
580 sgen_remember_major_object_for_concurrent_mark (char *obj)
582 if (!major_collector.is_concurrent)
585 g_assert (current_collection_generation == GENERATION_NURSERY || current_collection_generation == -1);
587 if (!concurrent_collection_in_progress)
590 GRAY_OBJECT_ENQUEUE (&remember_major_objects_gray_queue, obj);
592 if (current_collection_generation != GENERATION_NURSERY) {
594 * This happens when the mutator allocates large or
595 * pinned objects or when allocating in degraded
598 have_non_collection_major_object_remembers = TRUE;
605 gray_queue_redirect (SgenGrayQueue *queue)
607 gboolean wake = FALSE;
611 GrayQueueSection *section = sgen_gray_object_dequeue_section (queue);
614 sgen_section_gray_queue_enqueue (queue->alloc_prepare_data, section);
619 g_assert (concurrent_collection_in_progress ||
620 (current_collection_generation == GENERATION_OLD && major_collector.is_parallel));
621 if (sgen_workers_have_started ()) {
622 sgen_workers_wake_up_all ();
624 if (concurrent_collection_in_progress)
625 g_assert (current_collection_generation == -1);
631 redirect_major_object_remembers (void)
633 gray_queue_redirect (&remember_major_objects_gray_queue);
634 have_non_collection_major_object_remembers = FALSE;
638 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
640 MonoObject *o = (MonoObject*)(obj);
641 MonoObject *ref = (MonoObject*)*(ptr);
642 int offset = (char*)(ptr) - (char*)o;
644 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
646 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
648 if (mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
649 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
651 /* Thread.cached_culture_info */
652 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
653 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
654 !strcmp(o->vtable->klass->name_space, "System") &&
655 !strcmp(o->vtable->klass->name, "Object[]"))
658 * 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
659 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
660 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
661 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
662 * 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
663 * 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
664 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
665 * 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
666 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
668 if (!strcmp (ref->vtable->klass->name_space, "System") &&
669 !strcmp (ref->vtable->klass->name, "Byte[]") &&
670 !strcmp (o->vtable->klass->name_space, "System.IO") &&
671 !strcmp (o->vtable->klass->name, "MemoryStream"))
673 /* append_job() in threadpool.c */
674 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
675 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
676 !strcmp (o->vtable->klass->name_space, "System") &&
677 !strcmp (o->vtable->klass->name, "Object[]") &&
678 mono_thread_pool_is_queue_array ((MonoArray*) o))
684 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
686 MonoObject *o = (MonoObject*)(obj);
687 MonoObject *ref = (MonoObject*)*(ptr);
688 int offset = (char*)(ptr) - (char*)o;
690 MonoClassField *field;
693 if (!ref || ref->vtable->domain == domain)
695 if (is_xdomain_ref_allowed (ptr, obj, domain))
699 for (class = o->vtable->klass; class; class = class->parent) {
702 for (i = 0; i < class->field.count; ++i) {
703 if (class->fields[i].offset == offset) {
704 field = &class->fields[i];
712 if (ref->vtable->klass == mono_defaults.string_class)
713 str = mono_string_to_utf8 ((MonoString*)ref);
716 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
717 o, o->vtable->klass->name_space, o->vtable->klass->name,
718 offset, field ? field->name : "",
719 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
720 mono_gc_scan_for_specific_ref (o, TRUE);
726 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
729 scan_object_for_xdomain_refs (char *start, mword size, void *data)
731 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
733 #include "sgen-scan-object.h"
736 static gboolean scan_object_for_specific_ref_precise = TRUE;
739 #define HANDLE_PTR(ptr,obj) do { \
740 if ((MonoObject*)*(ptr) == key) { \
741 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
742 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
747 scan_object_for_specific_ref (char *start, MonoObject *key)
751 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
754 if (scan_object_for_specific_ref_precise) {
755 #include "sgen-scan-object.h"
757 mword *words = (mword*)start;
758 size_t size = safe_object_get_size ((MonoObject*)start);
760 for (i = 0; i < size / sizeof (mword); ++i) {
761 if (words [i] == (mword)key) {
762 g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
763 key, start, safe_name (start), i * sizeof (mword));
770 sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
772 while (start < end) {
776 if (!*(void**)start) {
777 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
782 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
788 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
790 if ((MonoVTable*)SGEN_LOAD_VTABLE (obj) != array_fill_vtable)
791 callback (obj, size, data);
798 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
800 scan_object_for_specific_ref (obj, key);
804 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
808 g_print ("found ref to %p in root record %p\n", key, root);
811 static MonoObject *check_key = NULL;
812 static RootRecord *check_root = NULL;
815 check_root_obj_specific_ref_from_marker (void **obj)
817 check_root_obj_specific_ref (check_root, check_key, *obj);
821 scan_roots_for_specific_ref (MonoObject *key, int root_type)
827 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
828 mword desc = root->root_desc;
832 switch (desc & ROOT_DESC_TYPE_MASK) {
833 case ROOT_DESC_BITMAP:
834 desc >>= ROOT_DESC_TYPE_SHIFT;
837 check_root_obj_specific_ref (root, key, *start_root);
842 case ROOT_DESC_COMPLEX: {
843 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
844 int bwords = (*bitmap_data) - 1;
845 void **start_run = start_root;
847 while (bwords-- > 0) {
848 gsize bmap = *bitmap_data++;
849 void **objptr = start_run;
852 check_root_obj_specific_ref (root, key, *objptr);
856 start_run += GC_BITS_PER_WORD;
860 case ROOT_DESC_USER: {
861 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
862 marker (start_root, check_root_obj_specific_ref_from_marker);
865 case ROOT_DESC_RUN_LEN:
866 g_assert_not_reached ();
868 g_assert_not_reached ();
870 } SGEN_HASH_TABLE_FOREACH_END;
877 mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
882 scan_object_for_specific_ref_precise = precise;
884 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
885 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
887 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
889 sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
891 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
892 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
894 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], ptr, root) {
895 while (ptr < (void**)root->end_root) {
896 check_root_obj_specific_ref (root, *ptr, key);
899 } SGEN_HASH_TABLE_FOREACH_END;
903 need_remove_object_for_domain (char *start, MonoDomain *domain)
905 if (mono_object_domain (start) == domain) {
906 SGEN_LOG (4, "Need to cleanup object %p", start);
907 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
914 process_object_for_domain_clearing (char *start, MonoDomain *domain)
916 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
917 if (vt->klass == mono_defaults.internal_thread_class)
918 g_assert (mono_object_domain (start) == mono_get_root_domain ());
919 /* The object could be a proxy for an object in the domain
921 if (mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
922 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
924 /* The server could already have been zeroed out, so
925 we need to check for that, too. */
926 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
927 SGEN_LOG (4, "Cleaning up remote pointer in %p to object %p", start, server);
928 ((MonoRealProxy*)start)->unwrapped_server = NULL;
933 static MonoDomain *check_domain = NULL;
936 check_obj_not_in_domain (void **o)
938 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
942 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
946 check_domain = domain;
947 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
948 mword desc = root->root_desc;
950 /* The MonoDomain struct is allowed to hold
951 references to objects in its own domain. */
952 if (start_root == (void**)domain)
955 switch (desc & ROOT_DESC_TYPE_MASK) {
956 case ROOT_DESC_BITMAP:
957 desc >>= ROOT_DESC_TYPE_SHIFT;
959 if ((desc & 1) && *start_root)
960 check_obj_not_in_domain (*start_root);
965 case ROOT_DESC_COMPLEX: {
966 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
967 int bwords = (*bitmap_data) - 1;
968 void **start_run = start_root;
970 while (bwords-- > 0) {
971 gsize bmap = *bitmap_data++;
972 void **objptr = start_run;
974 if ((bmap & 1) && *objptr)
975 check_obj_not_in_domain (*objptr);
979 start_run += GC_BITS_PER_WORD;
983 case ROOT_DESC_USER: {
984 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
985 marker (start_root, check_obj_not_in_domain);
988 case ROOT_DESC_RUN_LEN:
989 g_assert_not_reached ();
991 g_assert_not_reached ();
993 } SGEN_HASH_TABLE_FOREACH_END;
999 check_for_xdomain_refs (void)
1003 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1004 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1006 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1008 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1009 scan_object_for_xdomain_refs (bigobj->data, sgen_los_object_size (bigobj), NULL);
1013 clear_domain_process_object (char *obj, MonoDomain *domain)
1017 process_object_for_domain_clearing (obj, domain);
1018 remove = need_remove_object_for_domain (obj, domain);
1020 if (remove && ((MonoObject*)obj)->synchronisation) {
1021 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1023 sgen_register_disappearing_link (NULL, dislink, FALSE, TRUE);
1030 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1032 if (clear_domain_process_object (obj, domain))
1033 memset (obj, 0, size);
1037 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1039 clear_domain_process_object (obj, domain);
1043 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1045 if (need_remove_object_for_domain (obj, domain))
1046 major_collector.free_non_pinned_object (obj, size);
1050 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1052 if (need_remove_object_for_domain (obj, domain))
1053 major_collector.free_pinned_object (obj, size);
1057 * When appdomains are unloaded we can easily remove objects that have finalizers,
1058 * but all the others could still be present in random places on the heap.
1059 * We need a sweep to get rid of them even though it's going to be costly
1061 * The reason we need to remove them is because we access the vtable and class
1062 * structures to know the object size and the reference bitmap: once the domain is
1063 * unloaded the point to random memory.
1066 mono_gc_clear_domain (MonoDomain * domain)
1068 LOSObject *bigobj, *prev;
1073 sgen_process_fin_stage_entries ();
1074 sgen_process_dislink_stage_entries ();
1076 sgen_clear_nursery_fragments ();
1078 if (xdomain_checks && domain != mono_get_root_domain ()) {
1079 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1080 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1081 check_for_xdomain_refs ();
1084 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1085 to memory returned to the OS.*/
1086 null_ephemerons_for_domain (domain);
1088 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1089 sgen_null_links_for_domain (domain, i);
1091 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1092 sgen_remove_finalizers_for_domain (domain, i);
1094 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1095 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1097 /* We need two passes over major and large objects because
1098 freeing such objects might give their memory back to the OS
1099 (in the case of large objects) or obliterate its vtable
1100 (pinned objects with major-copying or pinned and non-pinned
1101 objects with major-mark&sweep), but we might need to
1102 dereference a pointer from an object to another object if
1103 the first object is a proxy. */
1104 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1105 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1106 clear_domain_process_object (bigobj->data, domain);
1109 for (bigobj = los_object_list; bigobj;) {
1110 if (need_remove_object_for_domain (bigobj->data, domain)) {
1111 LOSObject *to_free = bigobj;
1113 prev->next = bigobj->next;
1115 los_object_list = bigobj->next;
1116 bigobj = bigobj->next;
1117 SGEN_LOG (4, "Freeing large object %p", bigobj->data);
1118 sgen_los_free_object (to_free);
1122 bigobj = bigobj->next;
1124 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1125 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1127 if (G_UNLIKELY (do_pin_stats)) {
1128 if (domain == mono_get_root_domain ())
1129 sgen_pin_stats_print_class_stats ();
1136 * sgen_add_to_global_remset:
1138 * The global remset contains locations which point into newspace after
1139 * a minor collection. This can happen if the objects they point to are pinned.
1141 * LOCKING: If called from a parallel collector, the global remset
1142 * lock must be held. For serial collectors that is not necessary.
1145 sgen_add_to_global_remset (gpointer ptr, gpointer obj, gboolean concurrent_cementing)
1147 SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Target pointer of global remset must be in the nursery");
1149 if (!major_collector.is_concurrent) {
1150 SGEN_ASSERT (5, !concurrent_cementing, "Concurrent cementing must only happen with the concurrent collector");
1151 SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections");
1153 if (current_collection_generation == -1)
1154 SGEN_ASSERT (5, concurrent_cementing, "Global remsets outside of collection pauses can only be added by the concurrent collector");
1155 if (concurrent_cementing)
1156 SGEN_ASSERT (5, concurrent_collection_in_progress, "Concurrent collection must be in process in order to add global remsets");
1159 if (!object_is_pinned (obj))
1160 SGEN_ASSERT (5, sgen_minor_collector.is_split, "Non-pinned objects can only remain in nursery if it is a split nursery");
1161 else if (sgen_cement_lookup_or_register (obj, concurrent_cementing))
1164 remset.record_pointer (ptr);
1166 #ifdef ENABLE_DTRACE
1167 if (G_UNLIKELY (do_pin_stats))
1168 sgen_pin_stats_register_global_remset (obj);
1170 SGEN_LOG (8, "Adding global remset for %p", ptr);
1171 binary_protocol_global_remset (ptr, obj, (gpointer)SGEN_LOAD_VTABLE (obj));
1173 HEAVY_STAT (++stat_global_remsets_added);
1175 if (G_UNLIKELY (MONO_GC_GLOBAL_REMSET_ADD_ENABLED ())) {
1176 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
1177 MONO_GC_GLOBAL_REMSET_ADD ((mword)ptr, (mword)obj, sgen_safe_object_get_size (obj),
1178 vt->klass->name_space, vt->klass->name);
1184 * sgen_drain_gray_stack:
1186 * Scan objects in the gray stack until the stack is empty. This should be called
1187 * frequently after each object is copied, to achieve better locality and cache
1191 sgen_drain_gray_stack (int max_objs, ScanCopyContext ctx)
1194 ScanObjectFunc scan_func = ctx.scan_func;
1195 GrayQueue *queue = ctx.queue;
1197 if (max_objs == -1) {
1199 GRAY_OBJECT_DEQUEUE (queue, obj);
1202 SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, safe_name (obj));
1203 scan_func (obj, queue);
1209 for (i = 0; i != max_objs; ++i) {
1210 GRAY_OBJECT_DEQUEUE (queue, obj);
1213 SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, safe_name (obj));
1214 scan_func (obj, queue);
1216 } while (max_objs < 0);
1222 * Addresses from start to end are already sorted. This function finds
1223 * the object header for each address and pins the object. The
1224 * addresses must be inside the passed section. The (start of the)
1225 * address array is overwritten with the addresses of the actually
1226 * pinned objects. Return the number of pinned objects.
1229 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, ScanCopyContext ctx)
1234 void *last_obj = NULL;
1235 size_t last_obj_size = 0;
1238 void **definitely_pinned = start;
1239 ScanObjectFunc scan_func = ctx.scan_func;
1240 SgenGrayQueue *queue = ctx.queue;
1242 sgen_nursery_allocator_prepare_for_pinning ();
1244 while (start < end) {
1246 /* the range check should be reduntant */
1247 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1248 SGEN_LOG (5, "Considering pinning addr %p", addr);
1249 /* multiple pointers to the same object */
1250 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1254 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1255 g_assert (idx < section->num_scan_start);
1256 search_start = (void*)section->scan_starts [idx];
1257 if (!search_start || search_start > addr) {
1260 search_start = section->scan_starts [idx];
1261 if (search_start && search_start <= addr)
1264 if (!search_start || search_start > addr)
1265 search_start = start_nursery;
1267 if (search_start < last_obj)
1268 search_start = (char*)last_obj + last_obj_size;
1269 /* now addr should be in an object a short distance from search_start
1270 * Note that search_start must point to zeroed mem or point to an object.
1274 if (!*(void**)search_start) {
1275 /* Consistency check */
1277 for (frag = nursery_fragments; frag; frag = frag->next) {
1278 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1279 g_assert_not_reached ();
1283 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1286 last_obj = search_start;
1287 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1289 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1290 /* Marks the beginning of a nursery fragment, skip */
1292 SGEN_LOG (8, "Pinned try match %p (%s), size %zd", last_obj, safe_name (last_obj), last_obj_size);
1293 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1295 scan_func (search_start, queue);
1297 SGEN_LOG (4, "Pinned object %p, vtable %p (%s), count %d\n",
1298 search_start, *(void**)search_start, safe_name (search_start), count);
1299 binary_protocol_pin (search_start,
1300 (gpointer)LOAD_VTABLE (search_start),
1301 safe_object_get_size (search_start));
1303 #ifdef ENABLE_DTRACE
1304 if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
1305 int gen = sgen_ptr_in_nursery (search_start) ? GENERATION_NURSERY : GENERATION_OLD;
1306 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (search_start);
1307 MONO_GC_OBJ_PINNED ((mword)search_start,
1308 sgen_safe_object_get_size (search_start),
1309 vt->klass->name_space, vt->klass->name, gen);
1313 pin_object (search_start);
1314 GRAY_OBJECT_ENQUEUE (queue, search_start);
1315 if (G_UNLIKELY (do_pin_stats))
1316 sgen_pin_stats_register_object (search_start, last_obj_size);
1317 definitely_pinned [count] = search_start;
1323 /* skip to the next object */
1324 search_start = (void*)((char*)search_start + last_obj_size);
1325 } while (search_start <= addr);
1326 /* we either pinned the correct object or we ignored the addr because
1327 * it points to unused zeroed memory.
1333 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1334 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1335 GCRootReport report;
1337 for (idx = 0; idx < count; ++idx)
1338 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1339 notify_gc_roots (&report);
1341 stat_pinned_objects += count;
1346 sgen_pin_objects_in_section (GCMemSection *section, ScanCopyContext ctx)
1348 int num_entries = section->pin_queue_num_entries;
1350 void **start = section->pin_queue_start;
1352 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1353 section->data, section->next_data, ctx);
1354 section->pin_queue_num_entries = reduced_to;
1356 section->pin_queue_start = NULL;
1362 sgen_pin_object (void *object, GrayQueue *queue)
1364 g_assert (!concurrent_collection_in_progress);
1366 if (sgen_collection_is_parallel ()) {
1368 /*object arrives pinned*/
1369 sgen_pin_stage_ptr (object);
1373 SGEN_PIN_OBJECT (object);
1374 sgen_pin_stage_ptr (object);
1376 if (G_UNLIKELY (do_pin_stats))
1377 sgen_pin_stats_register_object (object, safe_object_get_size (object));
1379 GRAY_OBJECT_ENQUEUE (queue, object);
1380 binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
1382 #ifdef ENABLE_DTRACE
1383 if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
1384 int gen = sgen_ptr_in_nursery (object) ? GENERATION_NURSERY : GENERATION_OLD;
1385 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (object);
1386 MONO_GC_OBJ_PINNED ((mword)object, sgen_safe_object_get_size (object), vt->klass->name_space, vt->klass->name, gen);
1392 sgen_parallel_pin_or_update (void **ptr, void *obj, MonoVTable *vt, SgenGrayQueue *queue)
1396 gboolean major_pinned = FALSE;
1398 if (sgen_ptr_in_nursery (obj)) {
1399 if (SGEN_CAS_PTR (obj, (void*)((mword)vt | SGEN_PINNED_BIT), vt) == vt) {
1400 sgen_pin_object (obj, queue);
1404 major_collector.pin_major_object (obj, queue);
1405 major_pinned = TRUE;
1408 vtable_word = *(mword*)obj;
1409 /*someone else forwarded it, update the pointer and bail out*/
1410 if (vtable_word & SGEN_FORWARDED_BIT) {
1411 *ptr = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
1415 /*someone pinned it, nothing to do.*/
1416 if (vtable_word & SGEN_PINNED_BIT || major_pinned)
1421 /* Sort the addresses in array in increasing order.
1422 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1425 sgen_sort_addresses (void **array, int size)
1430 for (i = 1; i < size; ++i) {
1433 int parent = (child - 1) / 2;
1435 if (array [parent] >= array [child])
1438 tmp = array [parent];
1439 array [parent] = array [child];
1440 array [child] = tmp;
1446 for (i = size - 1; i > 0; --i) {
1449 array [i] = array [0];
1455 while (root * 2 + 1 <= end) {
1456 int child = root * 2 + 1;
1458 if (child < end && array [child] < array [child + 1])
1460 if (array [root] >= array [child])
1464 array [root] = array [child];
1465 array [child] = tmp;
1473 * Scan the memory between start and end and queue values which could be pointers
1474 * to the area between start_nursery and end_nursery for later consideration.
1475 * Typically used for thread stacks.
1478 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1482 #ifdef VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE
1483 VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
1486 while (start < end) {
1487 if (*start >= start_nursery && *start < end_nursery) {
1489 * *start can point to the middle of an object
1490 * note: should we handle pointing at the end of an object?
1491 * pinning in C# code disallows pointing at the end of an object
1492 * but there is some small chance that an optimizing C compiler
1493 * may keep the only reference to an object by pointing
1494 * at the end of it. We ignore this small chance for now.
1495 * Pointers to the end of an object are indistinguishable
1496 * from pointers to the start of the next object in memory
1497 * so if we allow that we'd need to pin two objects...
1498 * We queue the pointer in an array, the
1499 * array will then be sorted and uniqued. This way
1500 * we can coalesce several pinning pointers and it should
1501 * be faster since we'd do a memory scan with increasing
1502 * addresses. Note: we can align the address to the allocation
1503 * alignment, so the unique process is more effective.
1505 mword addr = (mword)*start;
1506 addr &= ~(ALLOC_ALIGN - 1);
1507 if (addr >= (mword)start_nursery && addr < (mword)end_nursery) {
1508 SGEN_LOG (6, "Pinning address %p from %p", (void*)addr, start);
1509 sgen_pin_stage_ptr ((void*)addr);
1512 if (G_UNLIKELY (do_pin_stats)) {
1513 if (ptr_in_nursery ((void*)addr))
1514 sgen_pin_stats_register_address ((char*)addr, pin_type);
1520 SGEN_LOG (7, "found %d potential pinned heap pointers", count);
1524 * The first thing we do in a collection is to identify pinned objects.
1525 * This function considers all the areas of memory that need to be
1526 * conservatively scanned.
1529 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
1533 SGEN_LOG (2, "Scanning pinned roots (%d bytes, %d/%d entries)", (int)roots_size, roots_hash [ROOT_TYPE_NORMAL].num_entries, roots_hash [ROOT_TYPE_PINNED].num_entries);
1534 /* objects pinned from the API are inside these roots */
1535 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], start_root, root) {
1536 SGEN_LOG (6, "Pinned roots %p-%p", start_root, root->end_root);
1537 conservatively_pin_objects_from (start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
1538 } SGEN_HASH_TABLE_FOREACH_END;
1539 /* now deal with the thread stacks
1540 * in the future we should be able to conservatively scan only:
1541 * *) the cpu registers
1542 * *) the unmanaged stack frames
1543 * *) the _last_ managed stack frame
1544 * *) pointers slots in managed frames
1546 scan_thread_data (start_nursery, end_nursery, FALSE, queue);
1550 unpin_objects_from_queue (SgenGrayQueue *queue)
1554 GRAY_OBJECT_DEQUEUE (queue, addr);
1557 g_assert (SGEN_OBJECT_IS_PINNED (addr));
1558 SGEN_UNPIN_OBJECT (addr);
1563 CopyOrMarkObjectFunc func;
1565 } UserCopyOrMarkData;
1567 static MonoNativeTlsKey user_copy_or_mark_key;
1570 init_user_copy_or_mark_key (void)
1572 mono_native_tls_alloc (&user_copy_or_mark_key, NULL);
1576 set_user_copy_or_mark_data (UserCopyOrMarkData *data)
1578 mono_native_tls_set_value (user_copy_or_mark_key, data);
1582 single_arg_user_copy_or_mark (void **obj)
1584 UserCopyOrMarkData *data = mono_native_tls_get_value (user_copy_or_mark_key);
1586 data->func (obj, data->queue);
1590 * The memory area from start_root to end_root contains pointers to objects.
1591 * Their position is precisely described by @desc (this means that the pointer
1592 * can be either NULL or the pointer to the start of an object).
1593 * This functions copies them to to_space updates them.
1595 * This function is not thread-safe!
1598 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc, ScanCopyContext ctx)
1600 CopyOrMarkObjectFunc copy_func = ctx.copy_func;
1601 SgenGrayQueue *queue = ctx.queue;
1603 switch (desc & ROOT_DESC_TYPE_MASK) {
1604 case ROOT_DESC_BITMAP:
1605 desc >>= ROOT_DESC_TYPE_SHIFT;
1607 if ((desc & 1) && *start_root) {
1608 copy_func (start_root, queue);
1609 SGEN_LOG (9, "Overwrote root at %p with %p", start_root, *start_root);
1610 sgen_drain_gray_stack (-1, ctx);
1616 case ROOT_DESC_COMPLEX: {
1617 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
1618 int bwords = (*bitmap_data) - 1;
1619 void **start_run = start_root;
1621 while (bwords-- > 0) {
1622 gsize bmap = *bitmap_data++;
1623 void **objptr = start_run;
1625 if ((bmap & 1) && *objptr) {
1626 copy_func (objptr, queue);
1627 SGEN_LOG (9, "Overwrote root at %p with %p", objptr, *objptr);
1628 sgen_drain_gray_stack (-1, ctx);
1633 start_run += GC_BITS_PER_WORD;
1637 case ROOT_DESC_USER: {
1638 UserCopyOrMarkData data = { copy_func, queue };
1639 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
1640 set_user_copy_or_mark_data (&data);
1641 marker (start_root, single_arg_user_copy_or_mark);
1642 set_user_copy_or_mark_data (NULL);
1645 case ROOT_DESC_RUN_LEN:
1646 g_assert_not_reached ();
1648 g_assert_not_reached ();
1653 reset_heap_boundaries (void)
1655 lowest_heap_address = ~(mword)0;
1656 highest_heap_address = 0;
1660 sgen_update_heap_boundaries (mword low, mword high)
1665 old = lowest_heap_address;
1668 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
1671 old = highest_heap_address;
1674 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
1678 * Allocate and setup the data structures needed to be able to allocate objects
1679 * in the nursery. The nursery is stored in nursery_section.
1682 alloc_nursery (void)
1684 GCMemSection *section;
1689 if (nursery_section)
1691 SGEN_LOG (2, "Allocating nursery size: %lu", (unsigned long)sgen_nursery_size);
1692 /* later we will alloc a larger area for the nursery but only activate
1693 * what we need. The rest will be used as expansion if we have too many pinned
1694 * objects in the existing nursery.
1696 /* FIXME: handle OOM */
1697 section = sgen_alloc_internal (INTERNAL_MEM_SECTION);
1699 alloc_size = sgen_nursery_size;
1701 /* If there isn't enough space even for the nursery we should simply abort. */
1702 g_assert (sgen_memgov_try_alloc_space (alloc_size, SPACE_NURSERY));
1704 #ifdef SGEN_ALIGN_NURSERY
1705 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
1707 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
1709 sgen_update_heap_boundaries ((mword)data, (mword)(data + sgen_nursery_size));
1710 SGEN_LOG (4, "Expanding nursery size (%p-%p): %lu, total: %lu", data, data + alloc_size, (unsigned long)sgen_nursery_size, (unsigned long)mono_gc_get_heap_size ());
1711 section->data = section->next_data = data;
1712 section->size = alloc_size;
1713 section->end_data = data + sgen_nursery_size;
1714 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
1715 section->scan_starts = sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS, TRUE);
1716 section->num_scan_start = scan_starts;
1718 nursery_section = section;
1720 sgen_nursery_allocator_set_nursery_bounds (data, data + sgen_nursery_size);
1724 mono_gc_get_nursery (int *shift_bits, size_t *size)
1726 *size = sgen_nursery_size;
1727 #ifdef SGEN_ALIGN_NURSERY
1728 *shift_bits = DEFAULT_NURSERY_BITS;
1732 return sgen_get_nursery_start ();
1736 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1738 SgenThreadInfo *info = mono_thread_info_current ();
1740 /* Could be called from sgen_thread_unregister () with a NULL info */
1743 info->stopped_domain = domain;
1748 mono_gc_precise_stack_mark_enabled (void)
1750 return !conservative_stack_mark;
1754 mono_gc_get_logfile (void)
1756 return gc_debug_file;
1760 report_finalizer_roots_list (FinalizeReadyEntry *list)
1762 GCRootReport report;
1763 FinalizeReadyEntry *fin;
1766 for (fin = list; fin; fin = fin->next) {
1769 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
1771 notify_gc_roots (&report);
1775 report_finalizer_roots (void)
1777 report_finalizer_roots_list (fin_ready_list);
1778 report_finalizer_roots_list (critical_fin_list);
1781 static GCRootReport *root_report;
1784 single_arg_report_root (void **obj)
1787 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
1791 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
1793 switch (desc & ROOT_DESC_TYPE_MASK) {
1794 case ROOT_DESC_BITMAP:
1795 desc >>= ROOT_DESC_TYPE_SHIFT;
1797 if ((desc & 1) && *start_root) {
1798 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
1804 case ROOT_DESC_COMPLEX: {
1805 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
1806 int bwords = (*bitmap_data) - 1;
1807 void **start_run = start_root;
1809 while (bwords-- > 0) {
1810 gsize bmap = *bitmap_data++;
1811 void **objptr = start_run;
1813 if ((bmap & 1) && *objptr) {
1814 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
1819 start_run += GC_BITS_PER_WORD;
1823 case ROOT_DESC_USER: {
1824 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
1825 root_report = report;
1826 marker (start_root, single_arg_report_root);
1829 case ROOT_DESC_RUN_LEN:
1830 g_assert_not_reached ();
1832 g_assert_not_reached ();
1837 report_registered_roots_by_type (int root_type)
1839 GCRootReport report;
1843 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1844 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1845 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
1846 } SGEN_HASH_TABLE_FOREACH_END;
1847 notify_gc_roots (&report);
1851 report_registered_roots (void)
1853 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
1854 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
1858 scan_finalizer_entries (FinalizeReadyEntry *list, ScanCopyContext ctx)
1860 CopyOrMarkObjectFunc copy_func = ctx.copy_func;
1861 SgenGrayQueue *queue = ctx.queue;
1862 FinalizeReadyEntry *fin;
1864 for (fin = list; fin; fin = fin->next) {
1867 SGEN_LOG (5, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object));
1868 copy_func (&fin->object, queue);
1873 generation_name (int generation)
1875 switch (generation) {
1876 case GENERATION_NURSERY: return "nursery";
1877 case GENERATION_OLD: return "old";
1878 default: g_assert_not_reached ();
1883 sgen_generation_name (int generation)
1885 return generation_name (generation);
1888 SgenObjectOperations *
1889 sgen_get_current_object_ops (void){
1890 return ¤t_object_ops;
1895 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
1899 int done_with_ephemerons, ephemeron_rounds = 0;
1900 CopyOrMarkObjectFunc copy_func = current_object_ops.copy_or_mark_object;
1901 ScanObjectFunc scan_func = current_object_ops.scan_object;
1902 ScanCopyContext ctx = { scan_func, copy_func, queue };
1905 * We copied all the reachable objects. Now it's the time to copy
1906 * the objects that were not referenced by the roots, but by the copied objects.
1907 * we built a stack of objects pointed to by gray_start: they are
1908 * additional roots and we may add more items as we go.
1909 * We loop until gray_start == gray_objects which means no more objects have
1910 * been added. Note this is iterative: no recursion is involved.
1911 * We need to walk the LO list as well in search of marked big objects
1912 * (use a flag since this is needed only on major collections). We need to loop
1913 * here as well, so keep a counter of marked LO (increasing it in copy_object).
1914 * To achieve better cache locality and cache usage, we drain the gray stack
1915 * frequently, after each object is copied, and just finish the work here.
1917 sgen_drain_gray_stack (-1, ctx);
1919 SGEN_LOG (2, "%s generation done", generation_name (generation));
1922 Reset bridge data, we might have lingering data from a previous collection if this is a major
1923 collection trigged by minor overflow.
1925 We must reset the gathered bridges since their original block might be evacuated due to major
1926 fragmentation in the meanwhile and the bridge code should not have to deal with that.
1928 sgen_bridge_reset_data ();
1931 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
1932 * before processing finalizable objects and non-tracking weak links to avoid finalizing/clearing
1933 * objects that are in fact reachable.
1935 done_with_ephemerons = 0;
1937 done_with_ephemerons = mark_ephemerons_in_range (ctx);
1938 sgen_drain_gray_stack (-1, ctx);
1940 } while (!done_with_ephemerons);
1942 sgen_scan_togglerefs (start_addr, end_addr, ctx);
1943 if (generation == GENERATION_OLD)
1944 sgen_scan_togglerefs (sgen_get_nursery_start (), sgen_get_nursery_end (), ctx);
1946 if (sgen_need_bridge_processing ()) {
1947 sgen_collect_bridge_objects (generation, ctx);
1948 if (generation == GENERATION_OLD)
1949 sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
1953 Make sure we drain the gray stack before processing disappearing links and finalizers.
1954 If we don't make sure it is empty we might wrongly see a live object as dead.
1956 sgen_drain_gray_stack (-1, ctx);
1959 We must clear weak links that don't track resurrection before processing object ready for
1960 finalization so they can be cleared before that.
1962 sgen_null_link_in_range (generation, TRUE, ctx);
1963 if (generation == GENERATION_OLD)
1964 sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx);
1967 /* walk the finalization queue and move also the objects that need to be
1968 * finalized: use the finalized objects as new roots so the objects they depend
1969 * on are also not reclaimed. As with the roots above, only objects in the nursery
1970 * are marked/copied.
1972 sgen_finalize_in_range (generation, ctx);
1973 if (generation == GENERATION_OLD)
1974 sgen_finalize_in_range (GENERATION_NURSERY, ctx);
1975 /* drain the new stack that might have been created */
1976 SGEN_LOG (6, "Precise scan of gray area post fin");
1977 sgen_drain_gray_stack (-1, ctx);
1980 * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
1982 done_with_ephemerons = 0;
1984 done_with_ephemerons = mark_ephemerons_in_range (ctx);
1985 sgen_drain_gray_stack (-1, ctx);
1987 } while (!done_with_ephemerons);
1990 * Clear ephemeron pairs with unreachable keys.
1991 * We pass the copy func so we can figure out if an array was promoted or not.
1993 clear_unreachable_ephemerons (generation == GENERATION_OLD && major_collector.is_concurrent, ctx);
1996 SGEN_LOG (2, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
1999 * handle disappearing links
2000 * Note we do this after checking the finalization queue because if an object
2001 * survives (at least long enough to be finalized) we don't clear the link.
2002 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2003 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2006 g_assert (sgen_gray_object_queue_is_empty (queue));
2008 sgen_null_link_in_range (generation, FALSE, ctx);
2009 if (generation == GENERATION_OLD)
2010 sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx);
2011 if (sgen_gray_object_queue_is_empty (queue))
2013 sgen_drain_gray_stack (-1, ctx);
2016 g_assert (sgen_gray_object_queue_is_empty (queue));
2020 sgen_check_section_scan_starts (GCMemSection *section)
2023 for (i = 0; i < section->num_scan_start; ++i) {
2024 if (section->scan_starts [i]) {
2025 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2026 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2032 check_scan_starts (void)
2034 if (!do_scan_starts_check)
2036 sgen_check_section_scan_starts (nursery_section);
2037 major_collector.check_scan_starts ();
2041 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx)
2045 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
2046 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
2047 precisely_scan_objects_from (start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, ctx);
2048 } SGEN_HASH_TABLE_FOREACH_END;
2052 sgen_dump_occupied (char *start, char *end, char *section_start)
2054 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2058 sgen_dump_section (GCMemSection *section, const char *type)
2060 char *start = section->data;
2061 char *end = section->data + section->size;
2062 char *occ_start = NULL;
2064 char *old_start = NULL; /* just for debugging */
2066 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2068 while (start < end) {
2072 if (!*(void**)start) {
2074 sgen_dump_occupied (occ_start, start, section->data);
2077 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2080 g_assert (start < section->next_data);
2085 vt = (GCVTable*)LOAD_VTABLE (start);
2088 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2091 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2092 start - section->data,
2093 vt->klass->name_space, vt->klass->name,
2101 sgen_dump_occupied (occ_start, start, section->data);
2103 fprintf (heap_dump_file, "</section>\n");
2107 dump_object (MonoObject *obj, gboolean dump_location)
2109 static char class_name [1024];
2111 MonoClass *class = mono_object_class (obj);
2115 * Python's XML parser is too stupid to parse angle brackets
2116 * in strings, so we just ignore them;
2119 while (class->name [i] && j < sizeof (class_name) - 1) {
2120 if (!strchr ("<>\"", class->name [i]))
2121 class_name [j++] = class->name [i];
2124 g_assert (j < sizeof (class_name));
2127 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2128 class->name_space, class_name,
2129 safe_object_get_size (obj));
2130 if (dump_location) {
2131 const char *location;
2132 if (ptr_in_nursery (obj))
2133 location = "nursery";
2134 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2138 fprintf (heap_dump_file, " location=\"%s\"", location);
2140 fprintf (heap_dump_file, "/>\n");
2144 dump_heap (const char *type, int num, const char *reason)
2149 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2151 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2152 fprintf (heap_dump_file, ">\n");
2153 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2154 sgen_dump_internal_mem_usage (heap_dump_file);
2155 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
2156 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2157 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
2159 fprintf (heap_dump_file, "<pinned-objects>\n");
2160 for (list = sgen_pin_stats_get_object_list (); list; list = list->next)
2161 dump_object (list->obj, TRUE);
2162 fprintf (heap_dump_file, "</pinned-objects>\n");
2164 sgen_dump_section (nursery_section, "nursery");
2166 major_collector.dump_heap (heap_dump_file);
2168 fprintf (heap_dump_file, "<los>\n");
2169 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2170 dump_object ((MonoObject*)bigobj->data, FALSE);
2171 fprintf (heap_dump_file, "</los>\n");
2173 fprintf (heap_dump_file, "</collection>\n");
2177 sgen_register_moved_object (void *obj, void *destination)
2179 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2181 /* FIXME: handle this for parallel collector */
2182 g_assert (!sgen_collection_is_parallel ());
2184 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2185 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2186 moved_objects_idx = 0;
2188 moved_objects [moved_objects_idx++] = obj;
2189 moved_objects [moved_objects_idx++] = destination;
2195 static gboolean inited = FALSE;
2200 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_pre_collection_fragment_clear);
2201 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_pinning);
2202 mono_counters_register ("Minor scan remembered set", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_scan_remsets);
2203 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_scan_pinned);
2204 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_scan_registered_roots);
2205 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_scan_thread_data);
2206 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_finish_gray_stack);
2207 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_minor_fragment_creation);
2209 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_pre_collection_fragment_clear);
2210 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_pinning);
2211 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_scan_pinned);
2212 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_scan_registered_roots);
2213 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_scan_thread_data);
2214 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_scan_alloc_pinned);
2215 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_scan_finalized);
2216 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_scan_big_objects);
2217 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_finish_gray_stack);
2218 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_free_bigobjs);
2219 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_los_sweep);
2220 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_sweep);
2221 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &time_major_fragment_creation);
2223 mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2225 #ifdef HEAVY_STATISTICS
2226 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2227 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2228 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2229 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2230 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2231 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2232 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2234 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2235 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2237 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2238 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2239 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2240 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2242 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2243 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2245 mono_counters_register ("Slots allocated in vain", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_slots_allocated_in_vain);
2247 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2248 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2249 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2250 mono_counters_register ("# nursery copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_to_space);
2252 sgen_nursery_allocator_init_heavy_stats ();
2253 sgen_alloc_init_heavy_stats ();
2261 reset_pinned_from_failed_allocation (void)
2263 bytes_pinned_from_failed_allocation = 0;
2267 sgen_set_pinned_from_failed_allocation (mword objsize)
2269 bytes_pinned_from_failed_allocation += objsize;
2273 sgen_collection_is_parallel (void)
2275 switch (current_collection_generation) {
2276 case GENERATION_NURSERY:
2277 return nursery_collection_is_parallel;
2278 case GENERATION_OLD:
2279 return major_collector.is_parallel;
2281 g_error ("Invalid current generation %d", current_collection_generation);
2286 sgen_collection_is_concurrent (void)
2288 switch (current_collection_generation) {
2289 case GENERATION_NURSERY:
2291 case GENERATION_OLD:
2292 return major_collector.is_concurrent;
2294 g_error ("Invalid current generation %d", current_collection_generation);
2299 sgen_concurrent_collection_in_progress (void)
2301 return concurrent_collection_in_progress;
2308 } FinishRememberedSetScanJobData;
2311 job_finish_remembered_set_scan (WorkerData *worker_data, void *job_data_untyped)
2313 FinishRememberedSetScanJobData *job_data = job_data_untyped;
2315 remset.finish_scan_remsets (job_data->heap_start, job_data->heap_end, sgen_workers_get_job_gray_queue (worker_data));
2316 sgen_free_internal_dynamic (job_data, sizeof (FinishRememberedSetScanJobData), INTERNAL_MEM_WORKER_JOB_DATA);
2321 CopyOrMarkObjectFunc copy_or_mark_func;
2322 ScanObjectFunc scan_func;
2326 } ScanFromRegisteredRootsJobData;
2329 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
2331 ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
2332 ScanCopyContext ctx = { job_data->scan_func, job_data->copy_or_mark_func,
2333 sgen_workers_get_job_gray_queue (worker_data) };
2335 scan_from_registered_roots (job_data->heap_start, job_data->heap_end, job_data->root_type, ctx);
2336 sgen_free_internal_dynamic (job_data, sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA);
2343 } ScanThreadDataJobData;
2346 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
2348 ScanThreadDataJobData *job_data = job_data_untyped;
2350 scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
2351 sgen_workers_get_job_gray_queue (worker_data));
2352 sgen_free_internal_dynamic (job_data, sizeof (ScanThreadDataJobData), INTERNAL_MEM_WORKER_JOB_DATA);
2357 FinalizeReadyEntry *list;
2358 } ScanFinalizerEntriesJobData;
2361 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
2363 ScanFinalizerEntriesJobData *job_data = job_data_untyped;
2364 ScanCopyContext ctx = { NULL, current_object_ops.copy_or_mark_object, sgen_workers_get_job_gray_queue (worker_data) };
2366 scan_finalizer_entries (job_data->list, ctx);
2367 sgen_free_internal_dynamic (job_data, sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA);
2371 job_scan_major_mod_union_cardtable (WorkerData *worker_data, void *job_data_untyped)
2373 g_assert (concurrent_collection_in_progress);
2374 major_collector.scan_card_table (TRUE, sgen_workers_get_job_gray_queue (worker_data));
2378 job_scan_los_mod_union_cardtable (WorkerData *worker_data, void *job_data_untyped)
2380 g_assert (concurrent_collection_in_progress);
2381 sgen_los_scan_card_table (TRUE, sgen_workers_get_job_gray_queue (worker_data));
2385 verify_scan_starts (char *start, char *end)
2389 for (i = 0; i < nursery_section->num_scan_start; ++i) {
2390 char *addr = nursery_section->scan_starts [i];
2391 if (addr > start && addr < end)
2392 SGEN_LOG (1, "NFC-BAD SCAN START [%d] %p for obj [%p %p]", i, addr, start, end);
2397 verify_nursery (void)
2399 char *start, *end, *cur, *hole_start;
2401 if (!do_verify_nursery)
2404 /*This cleans up unused fragments */
2405 sgen_nursery_allocator_prepare_for_pinning ();
2407 hole_start = start = cur = sgen_get_nursery_start ();
2408 end = sgen_get_nursery_end ();
2413 if (!*(void**)cur) {
2414 cur += sizeof (void*);
2418 if (object_is_forwarded (cur))
2419 SGEN_LOG (1, "FORWARDED OBJ %p", cur);
2420 else if (object_is_pinned (cur))
2421 SGEN_LOG (1, "PINNED OBJ %p", cur);
2423 ss = safe_object_get_size ((MonoObject*)cur);
2424 size = ALIGN_UP (safe_object_get_size ((MonoObject*)cur));
2425 verify_scan_starts (cur, cur + size);
2426 if (do_dump_nursery_content) {
2427 if (cur > hole_start)
2428 SGEN_LOG (1, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
2429 SGEN_LOG (1, "OBJ [%p %p %d %d %s %d]", cur, cur + size, (int)size, (int)ss, sgen_safe_name ((MonoObject*)cur), (gpointer)LOAD_VTABLE (cur) == sgen_get_array_fill_vtable ());
2437 * Checks that no objects in the nursery are fowarded or pinned. This
2438 * is a precondition to restarting the mutator while doing a
2439 * concurrent collection. Note that we don't clear fragments because
2440 * we depend on that having happened earlier.
2443 check_nursery_is_clean (void)
2445 char *start, *end, *cur;
2447 start = cur = sgen_get_nursery_start ();
2448 end = sgen_get_nursery_end ();
2453 if (!*(void**)cur) {
2454 cur += sizeof (void*);
2458 g_assert (!object_is_forwarded (cur));
2459 g_assert (!object_is_pinned (cur));
2461 ss = safe_object_get_size ((MonoObject*)cur);
2462 size = ALIGN_UP (safe_object_get_size ((MonoObject*)cur));
2463 verify_scan_starts (cur, cur + size);
2470 init_gray_queue (void)
2472 if (sgen_collection_is_parallel () || sgen_collection_is_concurrent ()) {
2473 sgen_workers_init_distribute_gray_queue ();
2474 sgen_gray_object_queue_init_with_alloc_prepare (&gray_queue, NULL,
2475 gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
2477 sgen_gray_object_queue_init (&gray_queue, NULL);
2480 if (major_collector.is_concurrent) {
2481 sgen_gray_object_queue_init_with_alloc_prepare (&remember_major_objects_gray_queue, NULL,
2482 gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
2484 sgen_gray_object_queue_init_invalid (&remember_major_objects_gray_queue);
2489 pin_stage_object_callback (char *obj, size_t size, void *data)
2491 sgen_pin_stage_ptr (obj);
2492 /* FIXME: do pin stats if enabled */
2496 * Collect objects in the nursery. Returns whether to trigger a major
2500 collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
2502 gboolean needs_major;
2503 size_t max_garbage_amount;
2505 FinishRememberedSetScanJobData *frssjd;
2506 ScanFromRegisteredRootsJobData *scrrjd_normal, *scrrjd_wbarrier;
2507 ScanFinalizerEntriesJobData *sfejd_fin_ready, *sfejd_critical_fin;
2508 ScanThreadDataJobData *stdjd;
2509 mword fragment_total;
2510 ScanCopyContext ctx;
2511 TV_DECLARE (all_atv);
2512 TV_DECLARE (all_btv);
2516 if (disable_minor_collections)
2519 MONO_GC_BEGIN (GENERATION_NURSERY);
2520 binary_protocol_collection_begin (stat_minor_gcs, GENERATION_NURSERY);
2524 #ifndef DISABLE_PERFCOUNTERS
2525 mono_perfcounters->gc_collections0++;
2528 current_collection_generation = GENERATION_NURSERY;
2529 if (sgen_collection_is_parallel ())
2530 current_object_ops = sgen_minor_collector.parallel_ops;
2532 current_object_ops = sgen_minor_collector.serial_ops;
2534 reset_pinned_from_failed_allocation ();
2536 check_scan_starts ();
2538 sgen_nursery_alloc_prepare_for_minor ();
2542 nursery_next = sgen_nursery_alloc_get_upper_alloc_bound ();
2543 /* FIXME: optimize later to use the higher address where an object can be present */
2544 nursery_next = MAX (nursery_next, sgen_get_nursery_end ());
2546 SGEN_LOG (1, "Start nursery collection %d %p-%p, size: %d", stat_minor_gcs, sgen_get_nursery_start (), nursery_next, (int)(nursery_next - sgen_get_nursery_start ()));
2547 max_garbage_amount = nursery_next - sgen_get_nursery_start ();
2548 g_assert (nursery_section->size >= max_garbage_amount);
2550 /* world must be stopped already */
2551 TV_GETTIME (all_atv);
2555 time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
2557 if (xdomain_checks) {
2558 sgen_clear_nursery_fragments ();
2559 check_for_xdomain_refs ();
2562 nursery_section->next_data = nursery_next;
2564 major_collector.start_nursery_collection ();
2566 sgen_memgov_minor_collection_start ();
2571 gc_stats.minor_gc_count ++;
2573 if (remset.prepare_for_minor_collection)
2574 remset.prepare_for_minor_collection ();
2576 MONO_GC_CHECKPOINT_1 (GENERATION_NURSERY);
2578 sgen_process_fin_stage_entries ();
2579 sgen_process_dislink_stage_entries ();
2581 MONO_GC_CHECKPOINT_2 (GENERATION_NURSERY);
2583 /* pin from pinned handles */
2584 sgen_init_pinning ();
2585 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2586 pin_from_roots (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2587 /* pin cemented objects */
2588 sgen_cement_iterate (pin_stage_object_callback, NULL);
2589 /* identify pinned objects */
2590 sgen_optimize_pin_queue (0);
2591 sgen_pinning_setup_section (nursery_section);
2592 ctx.scan_func = NULL;
2593 ctx.copy_func = NULL;
2594 ctx.queue = WORKERS_DISTRIBUTE_GRAY_QUEUE;
2595 sgen_pin_objects_in_section (nursery_section, ctx);
2596 sgen_pinning_trim_queue_to_section (nursery_section);
2599 time_minor_pinning += TV_ELAPSED (btv, atv);
2600 SGEN_LOG (2, "Finding pinned pointers: %d in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (btv, atv));
2601 SGEN_LOG (4, "Start scan with %d pinned objects", sgen_get_pinned_count ());
2603 MONO_GC_CHECKPOINT_3 (GENERATION_NURSERY);
2605 if (whole_heap_check_before_collection) {
2606 sgen_clear_nursery_fragments ();
2607 sgen_check_whole_heap (finish_up_concurrent_mark);
2609 if (consistency_check_at_minor_collection)
2610 sgen_check_consistency ();
2612 sgen_workers_start_all_workers ();
2615 * Perform the sequential part of remembered set scanning.
2616 * This usually involves scanning global information that might later be produced by evacuation.
2618 if (remset.begin_scan_remsets)
2619 remset.begin_scan_remsets (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2621 sgen_workers_start_marking ();
2623 frssjd = sgen_alloc_internal_dynamic (sizeof (FinishRememberedSetScanJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2624 frssjd->heap_start = sgen_get_nursery_start ();
2625 frssjd->heap_end = nursery_next;
2626 sgen_workers_enqueue_job (job_finish_remembered_set_scan, frssjd);
2628 /* we don't have complete write barrier yet, so we scan all the old generation sections */
2630 time_minor_scan_remsets += TV_ELAPSED (atv, btv);
2631 SGEN_LOG (2, "Old generation scan: %d usecs", TV_ELAPSED (atv, btv));
2633 MONO_GC_CHECKPOINT_4 (GENERATION_NURSERY);
2635 if (!sgen_collection_is_parallel ()) {
2636 ctx.scan_func = current_object_ops.scan_object;
2637 ctx.copy_func = NULL;
2638 ctx.queue = &gray_queue;
2639 sgen_drain_gray_stack (-1, ctx);
2642 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2643 report_registered_roots ();
2644 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2645 report_finalizer_roots ();
2647 time_minor_scan_pinned += TV_ELAPSED (btv, atv);
2649 MONO_GC_CHECKPOINT_5 (GENERATION_NURSERY);
2651 /* registered roots, this includes static fields */
2652 scrrjd_normal = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2653 scrrjd_normal->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2654 scrrjd_normal->scan_func = current_object_ops.scan_object;
2655 scrrjd_normal->heap_start = sgen_get_nursery_start ();
2656 scrrjd_normal->heap_end = nursery_next;
2657 scrrjd_normal->root_type = ROOT_TYPE_NORMAL;
2658 sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_normal);
2660 scrrjd_wbarrier = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2661 scrrjd_wbarrier->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2662 scrrjd_wbarrier->scan_func = current_object_ops.scan_object;
2663 scrrjd_wbarrier->heap_start = sgen_get_nursery_start ();
2664 scrrjd_wbarrier->heap_end = nursery_next;
2665 scrrjd_wbarrier->root_type = ROOT_TYPE_WBARRIER;
2666 sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_wbarrier);
2669 time_minor_scan_registered_roots += TV_ELAPSED (atv, btv);
2671 MONO_GC_CHECKPOINT_6 (GENERATION_NURSERY);
2674 stdjd = sgen_alloc_internal_dynamic (sizeof (ScanThreadDataJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2675 stdjd->heap_start = sgen_get_nursery_start ();
2676 stdjd->heap_end = nursery_next;
2677 sgen_workers_enqueue_job (job_scan_thread_data, stdjd);
2680 time_minor_scan_thread_data += TV_ELAPSED (btv, atv);
2683 MONO_GC_CHECKPOINT_7 (GENERATION_NURSERY);
2685 g_assert (!sgen_collection_is_parallel () && !sgen_collection_is_concurrent ());
2687 if (sgen_collection_is_parallel () || sgen_collection_is_concurrent ())
2688 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2690 /* Scan the list of objects ready for finalization. If */
2691 sfejd_fin_ready = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2692 sfejd_fin_ready->list = fin_ready_list;
2693 sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_fin_ready);
2695 sfejd_critical_fin = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2696 sfejd_critical_fin->list = critical_fin_list;
2697 sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_critical_fin);
2699 MONO_GC_CHECKPOINT_8 (GENERATION_NURSERY);
2701 finish_gray_stack (sgen_get_nursery_start (), nursery_next, GENERATION_NURSERY, &gray_queue);
2703 time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
2704 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
2706 MONO_GC_CHECKPOINT_9 (GENERATION_NURSERY);
2709 * The (single-threaded) finalization code might have done
2710 * some copying/marking so we can only reset the GC thread's
2711 * worker data here instead of earlier when we joined the
2714 sgen_workers_reset_data ();
2716 if (objects_pinned) {
2717 sgen_optimize_pin_queue (0);
2718 sgen_pinning_setup_section (nursery_section);
2721 /* walk the pin_queue, build up the fragment list of free memory, unmark
2722 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2725 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
2726 fragment_total = sgen_build_nursery_fragments (nursery_section,
2727 nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries,
2729 if (!fragment_total)
2732 /* Clear TLABs for all threads */
2733 sgen_clear_tlabs ();
2735 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
2737 time_minor_fragment_creation += TV_ELAPSED (atv, btv);
2738 SGEN_LOG (2, "Fragment creation: %d usecs, %lu bytes available", TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
2740 if (consistency_check_at_minor_collection)
2741 sgen_check_major_refs ();
2743 major_collector.finish_nursery_collection ();
2745 TV_GETTIME (all_btv);
2746 gc_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2749 dump_heap ("minor", stat_minor_gcs - 1, NULL);
2751 /* prepare the pin queue for the next collection */
2752 sgen_finish_pinning ();
2753 if (fin_ready_list || critical_fin_list) {
2754 SGEN_LOG (4, "Finalizer-thread wakeup: ready %d", num_ready_finalizers);
2755 mono_gc_finalize_notify ();
2757 sgen_pin_stats_reset ();
2758 /* clear cemented hash */
2759 sgen_cement_clear_below_threshold ();
2761 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2763 if (remset.finish_minor_collection)
2764 remset.finish_minor_collection ();
2766 check_scan_starts ();
2768 binary_protocol_flush_buffers (FALSE);
2770 sgen_memgov_minor_collection_end ();
2772 /*objects are late pinned because of lack of memory, so a major is a good call*/
2773 needs_major = objects_pinned > 0;
2774 current_collection_generation = -1;
2777 MONO_GC_END (GENERATION_NURSERY);
2778 binary_protocol_collection_end (stat_minor_gcs - 1, GENERATION_NURSERY);
2780 if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
2781 sgen_check_nursery_objects_pinned (unpin_queue != NULL);
2787 scan_nursery_objects_callback (char *obj, size_t size, ScanCopyContext *ctx)
2789 ctx->scan_func (obj, ctx->queue);
2793 scan_nursery_objects (ScanCopyContext ctx)
2795 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
2796 (IterateObjectCallbackFunc)scan_nursery_objects_callback, (void*)&ctx, FALSE);
2800 major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concurrent_mark, gboolean scan_mod_union)
2805 /* FIXME: only use these values for the precise scan
2806 * note that to_space pointers should be excluded anyway...
2808 char *heap_start = NULL;
2809 char *heap_end = (char*)-1;
2810 gboolean profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
2811 GCRootReport root_report = { 0 };
2812 ScanFromRegisteredRootsJobData *scrrjd_normal, *scrrjd_wbarrier;
2813 ScanThreadDataJobData *stdjd;
2814 ScanFinalizerEntriesJobData *sfejd_fin_ready, *sfejd_critical_fin;
2815 ScanCopyContext ctx;
2817 if (major_collector.is_concurrent) {
2818 /*This cleans up unused fragments */
2819 sgen_nursery_allocator_prepare_for_pinning ();
2821 if (do_concurrent_checks)
2822 check_nursery_is_clean ();
2824 /* The concurrent collector doesn't touch the nursery. */
2825 sgen_nursery_alloc_prepare_for_major ();
2832 /* Pinning depends on this */
2833 sgen_clear_nursery_fragments ();
2835 if (whole_heap_check_before_collection)
2836 sgen_check_whole_heap (finish_up_concurrent_mark);
2838 if (!major_collector.is_concurrent)
2839 sgen_cement_reset ();
2842 time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
2844 if (!sgen_collection_is_concurrent ())
2845 nursery_section->next_data = sgen_get_nursery_end ();
2846 /* we should also coalesce scanning from sections close to each other
2847 * and deal with pointers outside of the sections later.
2851 *major_collector.have_swept = FALSE;
2853 if (xdomain_checks) {
2854 sgen_clear_nursery_fragments ();
2855 check_for_xdomain_refs ();
2858 if (!major_collector.is_concurrent) {
2859 /* Remsets are not useful for a major collection */
2860 remset.prepare_for_major_collection ();
2863 sgen_process_fin_stage_entries ();
2864 sgen_process_dislink_stage_entries ();
2867 sgen_init_pinning ();
2868 SGEN_LOG (6, "Collecting pinned addresses");
2869 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2870 sgen_optimize_pin_queue (0);
2873 * The concurrent collector doesn't move objects, neither on
2874 * the major heap nor in the nursery, so we can mark even
2875 * before pinning has finished. For the non-concurrent
2876 * collector we start the workers after pinning.
2878 if (major_collector.is_concurrent) {
2879 sgen_workers_start_all_workers ();
2880 sgen_workers_start_marking ();
2884 * pin_queue now contains all candidate pointers, sorted and
2885 * uniqued. We must do two passes now to figure out which
2886 * objects are pinned.
2888 * The first is to find within the pin_queue the area for each
2889 * section. This requires that the pin_queue be sorted. We
2890 * also process the LOS objects and pinned chunks here.
2892 * The second, destructive, pass is to reduce the section
2893 * areas to pointers to the actually pinned objects.
2895 SGEN_LOG (6, "Pinning from sections");
2896 /* first pass for the sections */
2897 sgen_find_section_pin_queue_start_end (nursery_section);
2898 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2899 /* identify possible pointers to the insize of large objects */
2900 SGEN_LOG (6, "Pinning from large objects");
2901 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2903 if (sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + sgen_los_object_size (bigobj), &dummy)) {
2904 binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (((MonoObject*)(bigobj->data))));
2906 #ifdef ENABLE_DTRACE
2907 if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
2908 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (bigobj->data);
2909 MONO_GC_OBJ_PINNED ((mword)bigobj->data, sgen_safe_object_get_size ((MonoObject*)bigobj->data), vt->klass->name_space, vt->klass->name, GENERATION_OLD);
2913 if (sgen_los_object_is_pinned (bigobj->data)) {
2914 g_assert (finish_up_concurrent_mark);
2917 sgen_los_pin_object (bigobj->data);
2918 /* FIXME: only enqueue if object has references */
2919 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
2920 if (G_UNLIKELY (do_pin_stats))
2921 sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
2922 SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data, safe_name (bigobj->data), (unsigned long)sgen_los_object_size (bigobj));
2925 add_profile_gc_root (&root_report, bigobj->data, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
2929 notify_gc_roots (&root_report);
2930 /* second pass for the sections */
2931 ctx.scan_func = concurrent_collection_in_progress ? current_object_ops.scan_object : NULL;
2932 ctx.copy_func = NULL;
2933 ctx.queue = WORKERS_DISTRIBUTE_GRAY_QUEUE;
2936 * Concurrent mark never follows references into the nursery.
2937 * In the start and finish pauses we must scan live nursery
2938 * objects, though. We could simply scan all nursery objects,
2939 * but that would be conservative. The easiest way is to do a
2940 * nursery collection, which copies all live nursery objects
2941 * (except pinned ones, with the simple nursery) to the major
2942 * heap. Scanning the mod union table later will then scan
2943 * those promoted objects, provided they're reachable. Pinned
2944 * objects in the nursery - which we can trivially find in the
2945 * pinning queue - are treated as roots in the mark pauses.
2947 * The split nursery complicates the latter part because
2948 * non-pinned objects can survive in the nursery. That's why
2949 * we need to do a full front-to-back scan of the nursery,
2950 * marking all objects.
2952 * Non-concurrent mark evacuates from the nursery, so it's
2953 * sufficient to just scan pinned nursery objects.
2955 if (major_collector.is_concurrent && sgen_minor_collector.is_split) {
2956 scan_nursery_objects (ctx);
2958 sgen_pin_objects_in_section (nursery_section, ctx);
2959 if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
2960 sgen_check_nursery_objects_pinned (!concurrent_collection_in_progress || finish_up_concurrent_mark);
2963 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2964 if (old_next_pin_slot)
2965 *old_next_pin_slot = sgen_get_pinned_count ();
2968 time_major_pinning += TV_ELAPSED (atv, btv);
2969 SGEN_LOG (2, "Finding pinned pointers: %d in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (atv, btv));
2970 SGEN_LOG (4, "Start scan with %d pinned objects", sgen_get_pinned_count ());
2972 major_collector.init_to_space ();
2974 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2975 main_gc_thread = mono_native_thread_self ();
2978 if (!major_collector.is_concurrent) {
2979 sgen_workers_start_all_workers ();
2980 sgen_workers_start_marking ();
2983 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2984 report_registered_roots ();
2986 time_major_scan_pinned += TV_ELAPSED (btv, atv);
2988 /* registered roots, this includes static fields */
2989 scrrjd_normal = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2990 scrrjd_normal->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2991 scrrjd_normal->scan_func = current_object_ops.scan_object;
2992 scrrjd_normal->heap_start = heap_start;
2993 scrrjd_normal->heap_end = heap_end;
2994 scrrjd_normal->root_type = ROOT_TYPE_NORMAL;
2995 sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_normal);
2997 scrrjd_wbarrier = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2998 scrrjd_wbarrier->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2999 scrrjd_wbarrier->scan_func = current_object_ops.scan_object;
3000 scrrjd_wbarrier->heap_start = heap_start;
3001 scrrjd_wbarrier->heap_end = heap_end;
3002 scrrjd_wbarrier->root_type = ROOT_TYPE_WBARRIER;
3003 sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_wbarrier);
3006 time_major_scan_registered_roots += TV_ELAPSED (atv, btv);
3009 stdjd = sgen_alloc_internal_dynamic (sizeof (ScanThreadDataJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
3010 stdjd->heap_start = heap_start;
3011 stdjd->heap_end = heap_end;
3012 sgen_workers_enqueue_job (job_scan_thread_data, stdjd);
3015 time_major_scan_thread_data += TV_ELAPSED (btv, atv);
3018 time_major_scan_alloc_pinned += TV_ELAPSED (atv, btv);
3020 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3021 report_finalizer_roots ();
3023 /* scan the list of objects ready for finalization */
3024 sfejd_fin_ready = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
3025 sfejd_fin_ready->list = fin_ready_list;
3026 sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_fin_ready);
3028 sfejd_critical_fin = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
3029 sfejd_critical_fin->list = critical_fin_list;
3030 sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_critical_fin);
3032 if (scan_mod_union) {
3033 g_assert (finish_up_concurrent_mark);
3035 /* Mod union card table */
3036 sgen_workers_enqueue_job (job_scan_major_mod_union_cardtable, NULL);
3037 sgen_workers_enqueue_job (job_scan_los_mod_union_cardtable, NULL);
3041 time_major_scan_finalized += TV_ELAPSED (btv, atv);
3042 SGEN_LOG (2, "Root scan: %d usecs", TV_ELAPSED (btv, atv));
3045 time_major_scan_big_objects += TV_ELAPSED (atv, btv);
3047 if (major_collector.is_concurrent) {
3048 /* prepare the pin queue for the next collection */
3049 sgen_finish_pinning ();
3051 sgen_pin_stats_reset ();
3053 if (do_concurrent_checks)
3054 check_nursery_is_clean ();
3059 major_start_collection (int *old_next_pin_slot)
3061 MONO_GC_BEGIN (GENERATION_OLD);
3062 binary_protocol_collection_begin (stat_major_gcs, GENERATION_OLD);
3064 current_collection_generation = GENERATION_OLD;
3065 #ifndef DISABLE_PERFCOUNTERS
3066 mono_perfcounters->gc_collections1++;
3069 g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
3071 if (major_collector.is_concurrent) {
3072 concurrent_collection_in_progress = TRUE;
3074 sgen_cement_concurrent_start ();
3077 current_object_ops = major_collector.major_ops;
3079 reset_pinned_from_failed_allocation ();
3081 sgen_memgov_major_collection_start ();
3083 //count_ref_nonref_objs ();
3084 //consistency_check ();
3086 check_scan_starts ();
3089 SGEN_LOG (1, "Start major collection %d", stat_major_gcs);
3091 gc_stats.major_gc_count ++;
3093 if (major_collector.start_major_collection)
3094 major_collector.start_major_collection ();
3096 major_copy_or_mark_from_roots (old_next_pin_slot, FALSE, FALSE);
3100 wait_for_workers_to_finish (void)
3102 g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue));
3104 if (major_collector.is_parallel || major_collector.is_concurrent) {
3105 gray_queue_redirect (&gray_queue);
3106 sgen_workers_join ();
3109 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
3111 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3112 main_gc_thread = NULL;
3117 major_finish_collection (const char *reason, int old_next_pin_slot, gboolean scan_mod_union)
3119 LOSObject *bigobj, *prevbo;
3122 char *heap_start = NULL;
3123 char *heap_end = (char*)-1;
3127 if (major_collector.is_concurrent || major_collector.is_parallel)
3128 wait_for_workers_to_finish ();
3130 current_object_ops = major_collector.major_ops;
3132 if (major_collector.is_concurrent) {
3133 major_copy_or_mark_from_roots (NULL, TRUE, scan_mod_union);
3134 wait_for_workers_to_finish ();
3136 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
3138 if (do_concurrent_checks)
3139 check_nursery_is_clean ();
3143 * The workers have stopped so we need to finish gray queue
3144 * work that might result from finalization in the main GC
3145 * thread. Redirection must therefore be turned off.
3147 sgen_gray_object_queue_disable_alloc_prepare (&gray_queue);
3148 g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
3150 /* all the objects in the heap */
3151 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3153 time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
3156 * The (single-threaded) finalization code might have done
3157 * some copying/marking so we can only reset the GC thread's
3158 * worker data here instead of earlier when we joined the
3161 sgen_workers_reset_data ();
3163 if (objects_pinned) {
3164 g_assert (!major_collector.is_concurrent);
3166 /*This is slow, but we just OOM'd*/
3167 sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3168 sgen_optimize_pin_queue (0);
3169 sgen_find_section_pin_queue_start_end (nursery_section);
3173 reset_heap_boundaries ();
3174 sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
3176 if (check_mark_bits_after_major_collection)
3177 sgen_check_major_heap_marked ();
3179 MONO_GC_SWEEP_BEGIN (GENERATION_OLD, !major_collector.sweeps_lazily);
3181 /* sweep the big objects list */
3183 for (bigobj = los_object_list; bigobj;) {
3184 g_assert (!object_is_pinned (bigobj->data));
3185 if (sgen_los_object_is_pinned (bigobj->data)) {
3186 sgen_los_unpin_object (bigobj->data);
3187 sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + sgen_los_object_size (bigobj));
3190 /* not referenced anywhere, so we can free it */
3192 prevbo->next = bigobj->next;
3194 los_object_list = bigobj->next;
3196 bigobj = bigobj->next;
3197 sgen_los_free_object (to_free);
3201 bigobj = bigobj->next;
3205 time_major_free_bigobjs += TV_ELAPSED (atv, btv);
3210 time_major_los_sweep += TV_ELAPSED (btv, atv);
3212 major_collector.sweep ();
3214 MONO_GC_SWEEP_END (GENERATION_OLD, !major_collector.sweeps_lazily);
3217 time_major_sweep += TV_ELAPSED (atv, btv);
3219 if (!major_collector.is_concurrent) {
3220 /* walk the pin_queue, build up the fragment list of free memory, unmark
3221 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3224 if (!sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries, NULL))
3227 /* prepare the pin queue for the next collection */
3228 sgen_finish_pinning ();
3230 /* Clear TLABs for all threads */
3231 sgen_clear_tlabs ();
3233 sgen_pin_stats_reset ();
3236 if (major_collector.is_concurrent)
3237 sgen_cement_concurrent_finish ();
3238 sgen_cement_clear_below_threshold ();
3241 time_major_fragment_creation += TV_ELAPSED (btv, atv);
3244 dump_heap ("major", stat_major_gcs - 1, reason);
3246 if (fin_ready_list || critical_fin_list) {
3247 SGEN_LOG (4, "Finalizer-thread wakeup: ready %d", num_ready_finalizers);
3248 mono_gc_finalize_notify ();
3251 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
3253 sgen_memgov_major_collection_end ();
3254 current_collection_generation = -1;
3256 major_collector.finish_major_collection ();
3258 g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
3260 if (major_collector.is_concurrent)
3261 concurrent_collection_in_progress = FALSE;
3263 check_scan_starts ();
3265 binary_protocol_flush_buffers (FALSE);
3267 //consistency_check ();
3269 MONO_GC_END (GENERATION_OLD);
3270 binary_protocol_collection_end (stat_major_gcs - 1, GENERATION_OLD);
3274 major_do_collection (const char *reason)
3276 TV_DECLARE (all_atv);
3277 TV_DECLARE (all_btv);
3278 int old_next_pin_slot;
3280 if (major_collector.get_and_reset_num_major_objects_marked) {
3281 long long num_marked = major_collector.get_and_reset_num_major_objects_marked ();
3282 g_assert (!num_marked);
3285 /* world must be stopped already */
3286 TV_GETTIME (all_atv);
3288 major_start_collection (&old_next_pin_slot);
3289 major_finish_collection (reason, old_next_pin_slot, FALSE);
3291 TV_GETTIME (all_btv);
3292 gc_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3294 /* FIXME: also report this to the user, preferably in gc-end. */
3295 if (major_collector.get_and_reset_num_major_objects_marked)
3296 major_collector.get_and_reset_num_major_objects_marked ();
3298 return bytes_pinned_from_failed_allocation > 0;
3301 static gboolean major_do_collection (const char *reason);
3304 major_start_concurrent_collection (const char *reason)
3306 long long num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
3308 g_assert (num_objects_marked == 0);
3310 MONO_GC_CONCURRENT_START_BEGIN (GENERATION_OLD);
3312 // FIXME: store reason and pass it when finishing
3313 major_start_collection (NULL);
3315 gray_queue_redirect (&gray_queue);
3316 sgen_workers_wait_for_jobs ();
3318 num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
3319 MONO_GC_CONCURRENT_START_END (GENERATION_OLD, num_objects_marked);
3321 current_collection_generation = -1;
3325 major_update_or_finish_concurrent_collection (gboolean force_finish)
3327 SgenGrayQueue unpin_queue;
3328 memset (&unpin_queue, 0, sizeof (unpin_queue));
3330 MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
3332 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
3333 if (!have_non_collection_major_object_remembers)
3334 g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue));
3336 major_collector.update_cardtable_mod_union ();
3337 sgen_los_update_cardtable_mod_union ();
3339 if (!force_finish && !sgen_workers_all_done ()) {
3340 MONO_GC_CONCURRENT_UPDATE_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
3344 collect_nursery (&unpin_queue, TRUE);
3345 redirect_major_object_remembers ();
3347 current_collection_generation = GENERATION_OLD;
3348 major_finish_collection ("finishing", -1, TRUE);
3350 if (whole_heap_check_before_collection)
3351 sgen_check_whole_heap (FALSE);
3353 unpin_objects_from_queue (&unpin_queue);
3354 sgen_gray_object_queue_deinit (&unpin_queue);
3356 MONO_GC_CONCURRENT_FINISH_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
3358 current_collection_generation = -1;
3364 * Ensure an allocation request for @size will succeed by freeing enough memory.
3366 * LOCKING: The GC lock MUST be held.
3369 sgen_ensure_free_space (size_t size)
3371 int generation_to_collect = -1;
3372 const char *reason = NULL;
3375 if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
3376 if (sgen_need_major_collection (size)) {
3377 reason = "LOS overflow";
3378 generation_to_collect = GENERATION_OLD;
3381 if (degraded_mode) {
3382 if (sgen_need_major_collection (size)) {
3383 reason = "Degraded mode overflow";
3384 generation_to_collect = GENERATION_OLD;
3386 } else if (sgen_need_major_collection (size)) {
3387 reason = "Minor allowance";
3388 generation_to_collect = GENERATION_OLD;
3390 generation_to_collect = GENERATION_NURSERY;
3391 reason = "Nursery full";
3395 if (generation_to_collect == -1) {
3396 if (concurrent_collection_in_progress && sgen_workers_all_done ()) {
3397 generation_to_collect = GENERATION_OLD;
3398 reason = "Finish concurrent collection";
3402 if (generation_to_collect == -1)
3404 sgen_perform_collection (size, generation_to_collect, reason, FALSE);
3408 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish)
3410 TV_DECLARE (gc_end);
3411 GGTimingInfo infos [2];
3412 int overflow_generation_to_collect = -1;
3413 int oldest_generation_collected = generation_to_collect;
3414 const char *overflow_reason = NULL;
3416 MONO_GC_REQUESTED (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
3418 g_assert (generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD);
3420 if (have_non_collection_major_object_remembers) {
3421 g_assert (concurrent_collection_in_progress);
3422 redirect_major_object_remembers ();
3425 memset (infos, 0, sizeof (infos));
3426 mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
3428 infos [0].generation = generation_to_collect;
3429 infos [0].reason = reason;
3430 infos [0].is_overflow = FALSE;
3431 TV_GETTIME (infos [0].total_time);
3432 infos [1].generation = -1;
3434 sgen_stop_world (generation_to_collect);
3436 if (concurrent_collection_in_progress) {
3437 if (major_update_or_finish_concurrent_collection (wait_to_finish && generation_to_collect == GENERATION_OLD)) {
3438 oldest_generation_collected = GENERATION_OLD;
3441 if (generation_to_collect == GENERATION_OLD)
3445 //FIXME extract overflow reason
3446 if (generation_to_collect == GENERATION_NURSERY) {
3447 if (collect_nursery (NULL, FALSE)) {
3448 overflow_generation_to_collect = GENERATION_OLD;
3449 overflow_reason = "Minor overflow";
3451 if (concurrent_collection_in_progress) {
3452 redirect_major_object_remembers ();
3453 sgen_workers_wake_up_all ();
3456 SgenGrayQueue unpin_queue;
3457 SgenGrayQueue *unpin_queue_ptr;
3458 memset (&unpin_queue, 0, sizeof (unpin_queue));
3460 if (major_collector.is_concurrent && wait_to_finish)
3461 unpin_queue_ptr = &unpin_queue;
3463 unpin_queue_ptr = NULL;
3465 if (major_collector.is_concurrent) {
3466 g_assert (!concurrent_collection_in_progress);
3467 collect_nursery (unpin_queue_ptr, FALSE);
3470 if (major_collector.is_concurrent && !wait_to_finish) {
3471 major_start_concurrent_collection (reason);
3472 // FIXME: set infos[0] properly
3475 if (major_do_collection (reason)) {
3476 overflow_generation_to_collect = GENERATION_NURSERY;
3477 overflow_reason = "Excessive pinning";
3481 if (unpin_queue_ptr) {
3482 unpin_objects_from_queue (unpin_queue_ptr);
3483 sgen_gray_object_queue_deinit (unpin_queue_ptr);
3487 TV_GETTIME (gc_end);
3488 infos [0].total_time = SGEN_TV_ELAPSED (infos [0].total_time, gc_end);
3491 if (!major_collector.is_concurrent && overflow_generation_to_collect != -1) {
3492 mono_profiler_gc_event (MONO_GC_EVENT_START, overflow_generation_to_collect);
3493 infos [1].generation = overflow_generation_to_collect;
3494 infos [1].reason = overflow_reason;
3495 infos [1].is_overflow = TRUE;
3496 infos [1].total_time = gc_end;
3498 if (overflow_generation_to_collect == GENERATION_NURSERY)
3499 collect_nursery (NULL, FALSE);
3501 major_do_collection (overflow_reason);
3503 TV_GETTIME (gc_end);
3504 infos [1].total_time = SGEN_TV_ELAPSED (infos [1].total_time, gc_end);
3506 /* keep events symmetric */
3507 mono_profiler_gc_event (MONO_GC_EVENT_END, overflow_generation_to_collect);
3509 oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
3512 SGEN_LOG (2, "Heap size: %lu, LOS size: %lu", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage);
3514 /* this also sets the proper pointers for the next allocation */
3515 if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
3516 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3517 SGEN_LOG (1, "nursery collection didn't find enough room for %zd alloc (%d pinned)", requested_size, sgen_get_pinned_count ());
3518 sgen_dump_pin_queue ();
3523 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
3524 g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue));
3526 sgen_restart_world (oldest_generation_collected, infos);
3528 mono_profiler_gc_event (MONO_GC_EVENT_END, generation_to_collect);
3532 * ######################################################################
3533 * ######## Memory allocation from the OS
3534 * ######################################################################
3535 * This section of code deals with getting memory from the OS and
3536 * allocating memory for GC-internal data structures.
3537 * Internal memory can be handled with a freelist for small objects.
3543 G_GNUC_UNUSED static void
3544 report_internal_mem_usage (void)
3546 printf ("Internal memory usage:\n");
3547 sgen_report_internal_mem_usage ();
3548 printf ("Pinned memory usage:\n");
3549 major_collector.report_pinned_memory_usage ();
3553 * ######################################################################
3554 * ######## Finalization support
3555 * ######################################################################
3558 static inline gboolean
3559 sgen_major_is_object_alive (void *object)
3563 /* Oldgen objects can be pinned and forwarded too */
3564 if (SGEN_OBJECT_IS_PINNED (object) || SGEN_OBJECT_IS_FORWARDED (object))
3568 * FIXME: major_collector.is_object_live() also calculates the
3569 * size. Avoid the double calculation.
3571 objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)object));
3572 if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
3573 return sgen_los_object_is_pinned (object);
3575 return major_collector.is_object_live (object);
3579 * If the object has been forwarded it means it's still referenced from a root.
3580 * If it is pinned it's still alive as well.
3581 * A LOS object is only alive if we have pinned it.
3582 * Return TRUE if @obj is ready to be finalized.
3584 static inline gboolean
3585 sgen_is_object_alive (void *object)
3587 if (ptr_in_nursery (object))
3588 return sgen_nursery_is_object_alive (object);
3590 return sgen_major_is_object_alive (object);
3594 * This function returns true if @object is either alive or it belongs to the old gen
3595 * and we're currently doing a minor collection.
3598 sgen_is_object_alive_for_current_gen (char *object)
3600 if (ptr_in_nursery (object))
3601 return sgen_nursery_is_object_alive (object);
3603 if (current_collection_generation == GENERATION_NURSERY)
3606 return sgen_major_is_object_alive (object);
3610 * This function returns true if @object is either alive and belongs to the
3611 * current collection - major collections are full heap, so old gen objects
3612 * are never alive during a minor collection.
3615 sgen_is_object_alive_and_on_current_collection (char *object)
3617 if (ptr_in_nursery (object))
3618 return sgen_nursery_is_object_alive (object);
3620 if (current_collection_generation == GENERATION_NURSERY)
3623 return sgen_major_is_object_alive (object);
3628 sgen_gc_is_object_ready_for_finalization (void *object)
3630 return !sgen_is_object_alive (object);
3634 has_critical_finalizer (MonoObject *obj)
3638 if (!mono_defaults.critical_finalizer_object)
3641 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3643 return mono_class_has_parent_fast (class, mono_defaults.critical_finalizer_object);
3647 sgen_queue_finalization_entry (MonoObject *obj)
3649 FinalizeReadyEntry *entry = sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
3650 gboolean critical = has_critical_finalizer (obj);
3651 entry->object = obj;
3653 entry->next = critical_fin_list;
3654 critical_fin_list = entry;
3656 entry->next = fin_ready_list;
3657 fin_ready_list = entry;
3660 #ifdef ENABLE_DTRACE
3661 if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) {
3662 int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
3663 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
3664 MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj),
3665 vt->klass->name_space, vt->klass->name, gen, critical);
3671 sgen_object_is_live (void *obj)
3673 return sgen_is_object_alive_and_on_current_collection (obj);
3676 /* LOCKING: requires that the GC lock is held */
3678 null_ephemerons_for_domain (MonoDomain *domain)
3680 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3683 MonoObject *object = (MonoObject*)current->array;
3685 if (object && !object->vtable) {
3686 EphemeronLinkNode *tmp = current;
3689 prev->next = current->next;
3691 ephemeron_list = current->next;
3693 current = current->next;
3694 sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3697 current = current->next;
3702 /* LOCKING: requires that the GC lock is held */
3704 clear_unreachable_ephemerons (gboolean concurrent_cementing, ScanCopyContext ctx)
3706 CopyOrMarkObjectFunc copy_func = ctx.copy_func;
3707 GrayQueue *queue = ctx.queue;
3708 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3710 Ephemeron *cur, *array_end;
3714 char *object = current->array;
3716 if (!sgen_is_object_alive_for_current_gen (object)) {
3717 EphemeronLinkNode *tmp = current;
3719 SGEN_LOG (5, "Dead Ephemeron array at %p", object);
3722 prev->next = current->next;
3724 ephemeron_list = current->next;
3726 current = current->next;
3727 sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3732 copy_func ((void**)&object, queue);
3733 current->array = object;
3735 SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", object);
3737 array = (MonoArray*)object;
3738 cur = mono_array_addr (array, Ephemeron, 0);
3739 array_end = cur + mono_array_length_fast (array);
3740 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3742 for (; cur < array_end; ++cur) {
3743 char *key = (char*)cur->key;
3745 if (!key || key == tombstone)
3748 SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
3749 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
3750 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
3752 if (!sgen_is_object_alive_for_current_gen (key)) {
3753 cur->key = tombstone;
3759 current = current->next;
3764 LOCKING: requires that the GC lock is held
3766 Limitations: We scan all ephemerons on every collection since the current design doesn't allow for a simple nursery/mature split.
3769 mark_ephemerons_in_range (ScanCopyContext ctx)
3771 CopyOrMarkObjectFunc copy_func = ctx.copy_func;
3772 GrayQueue *queue = ctx.queue;
3773 int nothing_marked = 1;
3774 EphemeronLinkNode *current = ephemeron_list;
3776 Ephemeron *cur, *array_end;
3779 for (current = ephemeron_list; current; current = current->next) {
3780 char *object = current->array;
3781 SGEN_LOG (5, "Ephemeron array at %p", object);
3783 /*It has to be alive*/
3784 if (!sgen_is_object_alive_for_current_gen (object)) {
3785 SGEN_LOG (5, "\tnot reachable");
3789 copy_func ((void**)&object, queue);
3791 array = (MonoArray*)object;
3792 cur = mono_array_addr (array, Ephemeron, 0);
3793 array_end = cur + mono_array_length_fast (array);
3794 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3796 for (; cur < array_end; ++cur) {
3797 char *key = cur->key;
3799 if (!key || key == tombstone)
3802 SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
3803 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
3804 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
3806 if (sgen_is_object_alive_for_current_gen (key)) {
3807 char *value = cur->value;
3809 copy_func ((void**)&cur->key, queue);
3811 if (!sgen_is_object_alive_for_current_gen (value))
3813 copy_func ((void**)&cur->value, queue);
3819 SGEN_LOG (5, "Ephemeron run finished. Is it done %d", nothing_marked);
3820 return nothing_marked;
3824 mono_gc_invoke_finalizers (void)
3826 FinalizeReadyEntry *entry = NULL;
3827 gboolean entry_is_critical = FALSE;
3830 /* FIXME: batch to reduce lock contention */
3831 while (fin_ready_list || critical_fin_list) {
3835 FinalizeReadyEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
3837 /* We have finalized entry in the last
3838 interation, now we need to remove it from
3841 *list = entry->next;
3843 FinalizeReadyEntry *e = *list;
3844 while (e->next != entry)
3846 e->next = entry->next;
3848 sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
3852 /* Now look for the first non-null entry. */
3853 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
3856 entry_is_critical = FALSE;
3858 entry_is_critical = TRUE;
3859 for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
3864 g_assert (entry->object);
3865 num_ready_finalizers--;
3866 obj = entry->object;
3867 entry->object = NULL;
3868 SGEN_LOG (7, "Finalizing object %p (%s)", obj, safe_name (obj));
3876 g_assert (entry->object == NULL);
3878 /* the object is on the stack so it is pinned */
3879 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3880 mono_gc_run_finalize (obj, NULL);
3887 mono_gc_pending_finalizers (void)
3889 return fin_ready_list || critical_fin_list;
3893 * ######################################################################
3894 * ######## registered roots support
3895 * ######################################################################
3899 * We do not coalesce roots.
3902 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
3904 RootRecord new_root;
3907 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
3908 RootRecord *root = sgen_hash_table_lookup (&roots_hash [i], start);
3909 /* we allow changing the size and the descriptor (for thread statics etc) */
3911 size_t old_size = root->end_root - start;
3912 root->end_root = start + size;
3913 g_assert (((root->root_desc != 0) && (descr != NULL)) ||
3914 ((root->root_desc == 0) && (descr == NULL)));
3915 root->root_desc = (mword)descr;
3917 roots_size -= old_size;
3923 new_root.end_root = start + size;
3924 new_root.root_desc = (mword)descr;
3926 sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
3929 SGEN_LOG (3, "Added root for range: %p-%p, descr: %p (%d/%d bytes)", start, new_root.end_root, descr, (int)size, (int)roots_size);
3936 mono_gc_register_root (char *start, size_t size, void *descr)
3938 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
3942 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
3944 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
3948 mono_gc_deregister_root (char* addr)
3954 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
3955 if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
3956 roots_size -= (root.end_root - addr);
3962 * ######################################################################
3963 * ######## Thread handling (stop/start code)
3964 * ######################################################################
3967 unsigned int sgen_global_stop_count = 0;
3970 sgen_fill_thread_info_for_suspend (SgenThreadInfo *info)
3972 if (remset.fill_thread_info_for_suspend)
3973 remset.fill_thread_info_for_suspend (info);
3977 sgen_get_current_collection_generation (void)
3979 return current_collection_generation;
3983 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
3985 gc_callbacks = *callbacks;
3989 mono_gc_get_gc_callbacks ()
3991 return &gc_callbacks;
3994 /* Variables holding start/end nursery so it won't have to be passed at every call */
3995 static void *scan_area_arg_start, *scan_area_arg_end;
3998 mono_gc_conservatively_scan_area (void *start, void *end)
4000 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
4004 mono_gc_scan_object (void *obj)
4006 UserCopyOrMarkData *data = mono_native_tls_get_value (user_copy_or_mark_key);
4007 current_object_ops.copy_or_mark_object (&obj, data->queue);
4012 * Mark from thread stacks and registers.
4015 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
4017 SgenThreadInfo *info;
4019 scan_area_arg_start = start_nursery;
4020 scan_area_arg_end = end_nursery;
4022 FOREACH_THREAD (info) {
4024 SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %td", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start);
4027 if (info->gc_disabled) {
4028 SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %td", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start);
4032 if (!info->joined_stw) {
4033 SGEN_LOG (3, "Skipping thread not seen in STW %p, range: %p-%p, size: %td", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start);
4037 SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, sgen_get_pinned_count ());
4038 if (!info->thread_is_dying) {
4039 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
4040 UserCopyOrMarkData data = { NULL, queue };
4041 set_user_copy_or_mark_data (&data);
4042 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
4043 set_user_copy_or_mark_data (NULL);
4044 } else if (!precise) {
4045 if (!conservative_stack_mark) {
4046 fprintf (stderr, "Precise stack mark not supported - disabling.\n");
4047 conservative_stack_mark = TRUE;
4049 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
4053 if (!info->thread_is_dying && !precise) {
4055 conservatively_pin_objects_from ((void**)&info->ctx, (void**)&info->ctx + ARCH_NUM_REGS,
4056 start_nursery, end_nursery, PIN_TYPE_STACK);
4058 conservatively_pin_objects_from (&info->regs, &info->regs + ARCH_NUM_REGS,
4059 start_nursery, end_nursery, PIN_TYPE_STACK);
4062 } END_FOREACH_THREAD
4066 ptr_on_stack (void *ptr)
4068 gpointer stack_start = &stack_start;
4069 SgenThreadInfo *info = mono_thread_info_current ();
4071 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
4077 sgen_thread_register (SgenThreadInfo* info, void *addr)
4079 #ifndef HAVE_KW_THREAD
4080 SgenThreadInfo *__thread_info__ = info;
4084 #ifndef HAVE_KW_THREAD
4085 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
4087 g_assert (!mono_native_tls_get_value (thread_info_key));
4088 mono_native_tls_set_value (thread_info_key, info);
4090 sgen_thread_info = info;
4093 #if !defined(__MACH__)
4094 info->stop_count = -1;
4098 info->joined_stw = FALSE;
4099 info->doing_handshake = FALSE;
4100 info->thread_is_dying = FALSE;
4101 info->stack_start = NULL;
4102 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
4103 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
4104 info->stopped_ip = NULL;
4105 info->stopped_domain = NULL;
4107 memset (&info->ctx, 0, sizeof (MonoContext));
4109 memset (&info->regs, 0, sizeof (info->regs));
4112 sgen_init_tlab_info (info);
4114 binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
4116 #ifdef HAVE_KW_THREAD
4117 store_remset_buffer_index_addr = &store_remset_buffer_index;
4120 /* try to get it with attributes first */
4121 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
4125 pthread_attr_t attr;
4126 pthread_getattr_np (pthread_self (), &attr);
4127 pthread_attr_getstack (&attr, &sstart, &size);
4128 info->stack_start_limit = sstart;
4129 info->stack_end = (char*)sstart + size;
4130 pthread_attr_destroy (&attr);
4132 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
4133 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
4134 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
4137 /* FIXME: we assume the stack grows down */
4138 gsize stack_bottom = (gsize)addr;
4139 stack_bottom += 4095;
4140 stack_bottom &= ~4095;
4141 info->stack_end = (char*)stack_bottom;
4145 #ifdef HAVE_KW_THREAD
4146 stack_end = info->stack_end;
4149 if (remset.register_thread)
4150 remset.register_thread (info);
4152 SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end);
4154 if (gc_callbacks.thread_attach_func)
4155 info->runtime_data = gc_callbacks.thread_attach_func ();
4162 sgen_wbarrier_cleanup_thread (SgenThreadInfo *p)
4164 if (remset.cleanup_thread)
4165 remset.cleanup_thread (p);
4169 sgen_thread_unregister (SgenThreadInfo *p)
4171 /* If a delegate is passed to native code and invoked on a thread we dont
4172 * know about, the jit will register it with mono_jit_thread_attach, but
4173 * we have no way of knowing when that thread goes away. SGen has a TSD
4174 * so we assume that if the domain is still registered, we can detach
4177 if (mono_domain_get ())
4178 mono_thread_detach (mono_thread_current ());
4180 p->thread_is_dying = TRUE;
4183 There is a race condition between a thread finishing executing and been removed
4184 from the GC thread set.
4185 This happens on posix systems when TLS data is been cleaned-up, libpthread will
4186 set the thread_info slot to NULL before calling the cleanup function. This
4187 opens a window in which the thread is registered but has a NULL TLS.
4189 The suspend signal handler needs TLS data to know where to store thread state
4190 data or otherwise it will simply ignore the thread.
4192 This solution works because the thread doing STW will wait until all threads been
4193 suspended handshake back, so there is no race between the doing_hankshake test
4194 and the suspend_thread call.
4196 This is not required on systems that do synchronous STW as those can deal with
4197 the above race at suspend time.
4199 FIXME: I believe we could avoid this by using mono_thread_info_lookup when
4200 mono_thread_info_current returns NULL. Or fix mono_thread_info_lookup to do so.
4202 #if (defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED) || !defined(HAVE_PTHREAD_KILL)
4205 while (!TRYLOCK_GC) {
4206 if (!sgen_park_current_thread_if_doing_handshake (p))
4212 binary_protocol_thread_unregister ((gpointer)mono_thread_info_get_tid (p));
4213 SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)mono_thread_info_get_tid (p));
4215 if (gc_callbacks.thread_detach_func) {
4216 gc_callbacks.thread_detach_func (p->runtime_data);
4217 p->runtime_data = NULL;
4219 sgen_wbarrier_cleanup_thread (p);
4221 mono_threads_unregister_current_thread (p);
4227 sgen_thread_attach (SgenThreadInfo *info)
4230 /*this is odd, can we get attached before the gc is inited?*/
4234 if (gc_callbacks.thread_attach_func && !info->runtime_data)
4235 info->runtime_data = gc_callbacks.thread_attach_func ();
4238 mono_gc_register_thread (void *baseptr)
4240 return mono_thread_info_attach (baseptr) != NULL;
4244 * mono_gc_set_stack_end:
4246 * Set the end of the current threads stack to STACK_END. The stack space between
4247 * STACK_END and the real end of the threads stack will not be scanned during collections.
4250 mono_gc_set_stack_end (void *stack_end)
4252 SgenThreadInfo *info;
4255 info = mono_thread_info_current ();
4257 g_assert (stack_end < info->stack_end);
4258 info->stack_end = stack_end;
4263 #if USE_PTHREAD_INTERCEPT
4267 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
4269 return pthread_create (new_thread, attr, start_routine, arg);
4273 mono_gc_pthread_join (pthread_t thread, void **retval)
4275 return pthread_join (thread, retval);
4279 mono_gc_pthread_detach (pthread_t thread)
4281 return pthread_detach (thread);
4285 mono_gc_pthread_exit (void *retval)
4287 pthread_exit (retval);
4290 #endif /* USE_PTHREAD_INTERCEPT */
4293 * ######################################################################
4294 * ######## Write barriers
4295 * ######################################################################
4299 * Note: the write barriers first do the needed GC work and then do the actual store:
4300 * this way the value is visible to the conservative GC scan after the write barrier
4301 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
4302 * the conservative scan, otherwise by the remembered set scan.
4305 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
4307 HEAVY_STAT (++stat_wbarrier_set_field);
4308 if (ptr_in_nursery (field_ptr)) {
4309 *(void**)field_ptr = value;
4312 SGEN_LOG (8, "Adding remset at %p", field_ptr);
4314 binary_protocol_wbarrier (field_ptr, value, value->vtable);
4316 remset.wbarrier_set_field (obj, field_ptr, value);
4320 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
4322 HEAVY_STAT (++stat_wbarrier_set_arrayref);
4323 if (ptr_in_nursery (slot_ptr)) {
4324 *(void**)slot_ptr = value;
4327 SGEN_LOG (8, "Adding remset at %p", slot_ptr);
4329 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
4331 remset.wbarrier_set_arrayref (arr, slot_ptr, value);
4335 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
4337 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
4338 /*This check can be done without taking a lock since dest_ptr array is pinned*/
4339 if (ptr_in_nursery (dest_ptr) || count <= 0) {
4340 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
4344 #ifdef SGEN_BINARY_PROTOCOL
4347 for (i = 0; i < count; ++i) {
4348 gpointer dest = (gpointer*)dest_ptr + i;
4349 gpointer obj = *((gpointer*)src_ptr + i);
4351 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
4356 remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
4359 static char *found_obj;
4362 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
4364 char *ptr = user_data;
4366 if (ptr >= obj && ptr < obj + size) {
4367 g_assert (!found_obj);
4372 /* for use in the debugger */
4373 char* find_object_for_ptr (char *ptr);
4375 find_object_for_ptr (char *ptr)
4377 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
4379 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
4380 find_object_for_ptr_callback, ptr, TRUE);
4386 sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
4391 * Very inefficient, but this is debugging code, supposed to
4392 * be called from gdb, so we don't care.
4395 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
4400 mono_gc_wbarrier_generic_nostore (gpointer ptr)
4404 HEAVY_STAT (++stat_wbarrier_generic_store);
4406 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
4407 /* FIXME: ptr_in_heap must be called with the GC lock held */
4408 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
4409 char *start = find_object_for_ptr (ptr);
4410 MonoObject *value = *(MonoObject**)ptr;
4414 MonoObject *obj = (MonoObject*)start;
4415 if (obj->vtable->domain != value->vtable->domain)
4416 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
4422 obj = *(gpointer*)ptr;
4424 binary_protocol_wbarrier (ptr, obj, (gpointer)LOAD_VTABLE (obj));
4426 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr)) {
4427 SGEN_LOG (8, "Skipping remset at %p", ptr);
4432 * We need to record old->old pointer locations for the
4433 * concurrent collector.
4435 if (!ptr_in_nursery (obj) && !concurrent_collection_in_progress) {
4436 SGEN_LOG (8, "Skipping remset at %p", ptr);
4440 SGEN_LOG (8, "Adding remset at %p", ptr);
4442 remset.wbarrier_generic_nostore (ptr);
4446 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
4448 SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? safe_name (value) : "null");
4449 *(void**)ptr = value;
4450 if (ptr_in_nursery (value))
4451 mono_gc_wbarrier_generic_nostore (ptr);
4452 sgen_dummy_use (value);
4455 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
4457 mword *dest = _dest;
4462 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
4467 size -= SIZEOF_VOID_P;
4472 #ifdef SGEN_BINARY_PROTOCOL
4474 #define HANDLE_PTR(ptr,obj) do { \
4475 gpointer o = *(gpointer*)(ptr); \
4477 gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
4478 binary_protocol_wbarrier (d, o, (gpointer) LOAD_VTABLE (o)); \
4483 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
4485 #define SCAN_OBJECT_NOVTABLE
4486 #include "sgen-scan-object.h"
4491 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
4493 HEAVY_STAT (++stat_wbarrier_value_copy);
4494 g_assert (klass->valuetype);
4496 SGEN_LOG (8, "Adding value remset at %p, count %d, descr %p for class %s (%p)", dest, count, klass->gc_descr, klass->name, klass);
4498 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
4499 size_t element_size = mono_class_value_size (klass, NULL);
4500 size_t size = count * element_size;
4501 mono_gc_memmove (dest, src, size);
4505 #ifdef SGEN_BINARY_PROTOCOL
4507 size_t element_size = mono_class_value_size (klass, NULL);
4509 for (i = 0; i < count; ++i) {
4510 scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
4511 (char*)src + i * element_size - sizeof (MonoObject),
4512 (mword) klass->gc_descr);
4517 remset.wbarrier_value_copy (dest, src, count, klass);
4521 * mono_gc_wbarrier_object_copy:
4523 * Write barrier to call when obj is the result of a clone or copy of an object.
4526 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
4530 HEAVY_STAT (++stat_wbarrier_object_copy);
4532 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
4533 size = mono_object_class (obj)->instance_size;
4534 mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
4535 size - sizeof (MonoObject));
4539 #ifdef SGEN_BINARY_PROTOCOL
4540 scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
4543 remset.wbarrier_object_copy (obj, src);
4548 * ######################################################################
4549 * ######## Other mono public interface functions.
4550 * ######################################################################
4553 #define REFS_SIZE 128
4556 MonoGCReferences callback;
4560 MonoObject *refs [REFS_SIZE];
4561 uintptr_t offsets [REFS_SIZE];
4565 #define HANDLE_PTR(ptr,obj) do { \
4567 if (hwi->count == REFS_SIZE) { \
4568 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
4572 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
4573 hwi->refs [hwi->count++] = *(ptr); \
4578 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
4580 #include "sgen-scan-object.h"
4584 walk_references (char *start, size_t size, void *data)
4586 HeapWalkInfo *hwi = data;
4589 collect_references (hwi, start, size);
4590 if (hwi->count || !hwi->called)
4591 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
4595 * mono_gc_walk_heap:
4596 * @flags: flags for future use
4597 * @callback: a function pointer called for each object in the heap
4598 * @data: a user data pointer that is passed to callback
4600 * This function can be used to iterate over all the live objects in the heap:
4601 * for each object, @callback is invoked, providing info about the object's
4602 * location in memory, its class, its size and the objects it references.
4603 * For each referenced object it's offset from the object address is
4604 * reported in the offsets array.
4605 * The object references may be buffered, so the callback may be invoked
4606 * multiple times for the same object: in all but the first call, the size
4607 * argument will be zero.
4608 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
4609 * profiler event handler.
4611 * Returns: a non-zero value if the GC doesn't support heap walking
4614 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
4619 hwi.callback = callback;
4622 sgen_clear_nursery_fragments ();
4623 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
4625 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
4626 sgen_los_iterate_objects (walk_references, &hwi);
4632 mono_gc_collect (int generation)
4637 sgen_perform_collection (0, generation, "user request", TRUE);
4642 mono_gc_max_generation (void)
4648 mono_gc_collection_count (int generation)
4650 if (generation == 0)
4651 return stat_minor_gcs;
4652 return stat_major_gcs;
4656 mono_gc_get_used_size (void)
4660 tot = los_memory_usage;
4661 tot += nursery_section->next_data - nursery_section->data;
4662 tot += major_collector.get_used_size ();
4663 /* FIXME: account for pinned objects */
4669 mono_gc_disable (void)
4677 mono_gc_enable (void)
4685 mono_gc_get_los_limit (void)
4687 return MAX_SMALL_OBJ_SIZE;
4691 mono_gc_user_markers_supported (void)
4697 mono_object_is_alive (MonoObject* o)
4703 mono_gc_get_generation (MonoObject *obj)
4705 if (ptr_in_nursery (obj))
4711 mono_gc_enable_events (void)
4716 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
4718 sgen_register_disappearing_link (obj, link_addr, track, FALSE);
4722 mono_gc_weak_link_remove (void **link_addr, gboolean track)
4724 sgen_register_disappearing_link (NULL, link_addr, track, FALSE);
4728 mono_gc_weak_link_get (void **link_addr)
4731 * We must only load *link_addr once because it might change
4732 * under our feet, and REVEAL_POINTER (NULL) results in an
4733 * invalid reference.
4735 void *ptr = *link_addr;
4740 * During the second bridge processing step the world is
4741 * running again. That step processes all weak links once
4742 * more to null those that refer to dead objects. Before that
4743 * is completed, those links must not be followed, so we
4744 * conservatively wait for bridge processing when any weak
4745 * link is dereferenced.
4747 if (G_UNLIKELY (bridge_processing_in_progress))
4748 mono_gc_wait_for_bridge_processing ();
4750 return (MonoObject*) REVEAL_POINTER (ptr);
4754 mono_gc_ephemeron_array_add (MonoObject *obj)
4756 EphemeronLinkNode *node;
4760 node = sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
4765 node->array = (char*)obj;
4766 node->next = ephemeron_list;
4767 ephemeron_list = node;
4769 SGEN_LOG (5, "Registered ephemeron array %p", obj);
4776 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
4780 result = func (data);
4781 UNLOCK_INTERRUPTION;
4786 mono_gc_is_gc_thread (void)
4790 result = mono_thread_info_current () != NULL;
4796 is_critical_method (MonoMethod *method)
4798 return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
4802 mono_gc_base_init (void)
4804 MonoThreadInfoCallbacks cb;
4807 char *major_collector_opt = NULL;
4808 char *minor_collector_opt = NULL;
4810 glong soft_limit = 0;
4814 gboolean debug_print_allowance = FALSE;
4815 double allowance_ratio = 0, save_target = 0;
4816 gboolean have_split_nursery = FALSE;
4817 gboolean cement_enabled = TRUE;
4820 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
4823 /* already inited */
4826 /* being inited by another thread */
4830 /* we will init it */
4833 g_assert_not_reached ();
4835 } while (result != 0);
4837 LOCK_INIT (gc_mutex);
4839 pagesize = mono_pagesize ();
4840 gc_debug_file = stderr;
4842 cb.thread_register = sgen_thread_register;
4843 cb.thread_unregister = sgen_thread_unregister;
4844 cb.thread_attach = sgen_thread_attach;
4845 cb.mono_method_is_critical = (gpointer)is_critical_method;
4847 cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
4850 mono_threads_init (&cb, sizeof (SgenThreadInfo));
4852 LOCK_INIT (sgen_interruption_mutex);
4853 LOCK_INIT (pin_queue_mutex);
4855 init_user_copy_or_mark_key ();
4857 if ((env = getenv ("MONO_GC_PARAMS"))) {
4858 opts = g_strsplit (env, ",", -1);
4859 for (ptr = opts; *ptr; ++ptr) {
4861 if (g_str_has_prefix (opt, "major=")) {
4862 opt = strchr (opt, '=') + 1;
4863 major_collector_opt = g_strdup (opt);
4864 } else if (g_str_has_prefix (opt, "minor=")) {
4865 opt = strchr (opt, '=') + 1;
4866 minor_collector_opt = g_strdup (opt);
4874 sgen_init_internal_allocator ();
4875 sgen_init_nursery_allocator ();
4877 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
4878 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
4879 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
4880 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
4881 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
4882 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
4884 #ifndef HAVE_KW_THREAD
4885 mono_native_tls_alloc (&thread_info_key, NULL);
4889 * This needs to happen before any internal allocations because
4890 * it inits the small id which is required for hazard pointer
4895 mono_thread_info_attach (&dummy);
4897 if (!minor_collector_opt) {
4898 sgen_simple_nursery_init (&sgen_minor_collector);
4900 if (!strcmp (minor_collector_opt, "simple")) {
4901 sgen_simple_nursery_init (&sgen_minor_collector);
4902 } else if (!strcmp (minor_collector_opt, "split")) {
4903 sgen_split_nursery_init (&sgen_minor_collector);
4904 have_split_nursery = TRUE;
4906 fprintf (stderr, "Unknown minor collector `%s'.\n", minor_collector_opt);
4911 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
4912 sgen_marksweep_init (&major_collector);
4913 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
4914 sgen_marksweep_fixed_init (&major_collector);
4915 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
4916 sgen_marksweep_par_init (&major_collector);
4917 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
4918 sgen_marksweep_fixed_par_init (&major_collector);
4919 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
4920 sgen_marksweep_conc_init (&major_collector);
4922 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
4926 #ifdef SGEN_HAVE_CARDTABLE
4927 use_cardtable = major_collector.supports_cardtable;
4929 use_cardtable = FALSE;
4932 num_workers = mono_cpu_count ();
4933 g_assert (num_workers > 0);
4934 if (num_workers > 16)
4937 ///* Keep this the default for now */
4938 /* Precise marking is broken on all supported targets. Disable until fixed. */
4939 conservative_stack_mark = TRUE;
4941 sgen_nursery_size = DEFAULT_NURSERY_SIZE;
4944 for (ptr = opts; *ptr; ++ptr) {
4946 if (g_str_has_prefix (opt, "major="))
4948 if (g_str_has_prefix (opt, "minor="))
4950 if (g_str_has_prefix (opt, "wbarrier=")) {
4951 opt = strchr (opt, '=') + 1;
4952 if (strcmp (opt, "remset") == 0) {
4953 if (major_collector.is_concurrent) {
4954 fprintf (stderr, "The concurrent collector does not support the SSB write barrier.\n");
4957 use_cardtable = FALSE;
4958 } else if (strcmp (opt, "cardtable") == 0) {
4959 if (!use_cardtable) {
4960 if (major_collector.supports_cardtable)
4961 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
4963 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
4967 fprintf (stderr, "wbarrier must either be `remset' or `cardtable'.");
4972 if (g_str_has_prefix (opt, "max-heap-size=")) {
4973 opt = strchr (opt, '=') + 1;
4974 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
4975 if ((max_heap & (mono_pagesize () - 1))) {
4976 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
4980 fprintf (stderr, "max-heap-size must be an integer.\n");
4985 if (g_str_has_prefix (opt, "soft-heap-limit=")) {
4986 opt = strchr (opt, '=') + 1;
4987 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
4988 if (soft_limit <= 0) {
4989 fprintf (stderr, "soft-heap-limit must be positive.\n");
4993 fprintf (stderr, "soft-heap-limit must be an integer.\n");
4998 if (g_str_has_prefix (opt, "workers=")) {
5001 if (!major_collector.is_parallel) {
5002 fprintf (stderr, "The workers= option can only be used for parallel collectors.");
5005 opt = strchr (opt, '=') + 1;
5006 val = strtol (opt, &endptr, 10);
5007 if (!*opt || *endptr) {
5008 fprintf (stderr, "Cannot parse the workers= option value.");
5011 if (val <= 0 || val > 16) {
5012 fprintf (stderr, "The number of workers must be in the range 1 to 16.");
5015 num_workers = (int)val;
5018 if (g_str_has_prefix (opt, "stack-mark=")) {
5019 opt = strchr (opt, '=') + 1;
5020 if (!strcmp (opt, "precise")) {
5021 conservative_stack_mark = FALSE;
5022 } else if (!strcmp (opt, "conservative")) {
5023 conservative_stack_mark = TRUE;
5025 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
5030 if (g_str_has_prefix (opt, "bridge=")) {
5031 opt = strchr (opt, '=') + 1;
5032 sgen_register_test_bridge_callbacks (g_strdup (opt));
5036 if (g_str_has_prefix (opt, "nursery-size=")) {
5038 opt = strchr (opt, '=') + 1;
5039 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
5040 sgen_nursery_size = val;
5041 #ifdef SGEN_ALIGN_NURSERY
5042 if ((val & (val - 1))) {
5043 fprintf (stderr, "The nursery size must be a power of two.\n");
5047 if (val < SGEN_MAX_NURSERY_WASTE) {
5048 fprintf (stderr, "The nursery size must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE);
5052 sgen_nursery_bits = 0;
5053 while (1 << (++ sgen_nursery_bits) != sgen_nursery_size)
5057 fprintf (stderr, "nursery-size must be an integer.\n");
5063 if (g_str_has_prefix (opt, "save-target-ratio=")) {
5065 opt = strchr (opt, '=') + 1;
5066 save_target = strtod (opt, &endptr);
5067 if (endptr == opt) {
5068 fprintf (stderr, "save-target-ratio must be a number.");
5071 if (save_target < SGEN_MIN_SAVE_TARGET_RATIO || save_target > SGEN_MAX_SAVE_TARGET_RATIO) {
5072 fprintf (stderr, "save-target-ratio must be between %.2f - %.2f.", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
5077 if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
5079 opt = strchr (opt, '=') + 1;
5081 allowance_ratio = strtod (opt, &endptr);
5082 if (endptr == opt) {
5083 fprintf (stderr, "save-target-ratio must be a number.");
5086 if (allowance_ratio < SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO || allowance_ratio > SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO) {
5087 fprintf (stderr, "default-allowance-ratio must be between %.2f - %.2f.", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO);
5093 if (!strcmp (opt, "cementing")) {
5094 cement_enabled = TRUE;
5097 if (!strcmp (opt, "no-cementing")) {
5098 cement_enabled = FALSE;
5102 if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
5105 if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
5108 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
5109 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
5110 fprintf (stderr, " soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
5111 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
5112 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par', 'marksweep-fixed' or 'marksweep-fixed-par')\n");
5113 fprintf (stderr, " minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
5114 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
5115 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
5116 fprintf (stderr, " [no-]cementing\n");
5117 if (major_collector.print_gc_param_usage)
5118 major_collector.print_gc_param_usage ();
5119 if (sgen_minor_collector.print_gc_param_usage)
5120 sgen_minor_collector.print_gc_param_usage ();
5121 fprintf (stderr, " Experimental options:\n");
5122 fprintf (stderr, " save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
5123 fprintf (stderr, " default-allowance-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MAX_ALLOWANCE_NURSERY_SIZE_RATIO);
5129 if (major_collector.is_parallel)
5130 sgen_workers_init (num_workers);
5131 else if (major_collector.is_concurrent)
5132 sgen_workers_init (1);
5134 if (major_collector_opt)
5135 g_free (major_collector_opt);
5137 if (minor_collector_opt)
5138 g_free (minor_collector_opt);
5142 sgen_cement_init (cement_enabled);
5144 if ((env = getenv ("MONO_GC_DEBUG"))) {
5145 opts = g_strsplit (env, ",", -1);
5146 for (ptr = opts; ptr && *ptr; ptr ++) {
5148 if (opt [0] >= '0' && opt [0] <= '9') {
5149 gc_debug_level = atoi (opt);
5155 char *rf = g_strdup_printf ("%s.%d", opt, GetCurrentProcessId ());
5157 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
5159 gc_debug_file = fopen (rf, "wb");
5161 gc_debug_file = stderr;
5164 } else if (!strcmp (opt, "print-allowance")) {
5165 debug_print_allowance = TRUE;
5166 } else if (!strcmp (opt, "print-pinning")) {
5167 do_pin_stats = TRUE;
5168 } else if (!strcmp (opt, "verify-before-allocs")) {
5169 verify_before_allocs = 1;
5170 has_per_allocation_action = TRUE;
5171 } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
5172 char *arg = strchr (opt, '=') + 1;
5173 verify_before_allocs = atoi (arg);
5174 has_per_allocation_action = TRUE;
5175 } else if (!strcmp (opt, "collect-before-allocs")) {
5176 collect_before_allocs = 1;
5177 has_per_allocation_action = TRUE;
5178 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
5179 char *arg = strchr (opt, '=') + 1;
5180 has_per_allocation_action = TRUE;
5181 collect_before_allocs = atoi (arg);
5182 } else if (!strcmp (opt, "verify-before-collections")) {
5183 whole_heap_check_before_collection = TRUE;
5184 } else if (!strcmp (opt, "check-at-minor-collections")) {
5185 consistency_check_at_minor_collection = TRUE;
5186 nursery_clear_policy = CLEAR_AT_GC;
5187 } else if (!strcmp (opt, "check-mark-bits")) {
5188 check_mark_bits_after_major_collection = TRUE;
5189 } else if (!strcmp (opt, "check-nursery-pinned")) {
5190 check_nursery_objects_pinned = TRUE;
5191 } else if (!strcmp (opt, "xdomain-checks")) {
5192 xdomain_checks = TRUE;
5193 } else if (!strcmp (opt, "clear-at-gc")) {
5194 nursery_clear_policy = CLEAR_AT_GC;
5195 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
5196 nursery_clear_policy = CLEAR_AT_GC;
5197 } else if (!strcmp (opt, "check-scan-starts")) {
5198 do_scan_starts_check = TRUE;
5199 } else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
5200 do_verify_nursery = TRUE;
5201 } else if (!strcmp (opt, "check-concurrent")) {
5202 if (!major_collector.is_concurrent) {
5203 fprintf (stderr, "Error: check-concurrent only world with concurrent major collectors.\n");
5206 do_concurrent_checks = TRUE;
5207 } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
5208 do_dump_nursery_content = TRUE;
5209 } else if (!strcmp (opt, "no-managed-allocator")) {
5210 sgen_set_use_managed_allocator (FALSE);
5211 } else if (!strcmp (opt, "disable-minor")) {
5212 disable_minor_collections = TRUE;
5213 } else if (!strcmp (opt, "disable-major")) {
5214 disable_major_collections = TRUE;
5215 } else if (g_str_has_prefix (opt, "heap-dump=")) {
5216 char *filename = strchr (opt, '=') + 1;
5217 nursery_clear_policy = CLEAR_AT_GC;
5218 heap_dump_file = fopen (filename, "w");
5219 if (heap_dump_file) {
5220 fprintf (heap_dump_file, "<sgen-dump>\n");
5221 do_pin_stats = TRUE;
5223 #ifdef SGEN_BINARY_PROTOCOL
5224 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
5225 char *filename = strchr (opt, '=') + 1;
5226 binary_protocol_init (filename);
5228 fprintf (stderr, "Warning: Cardtable write barriers will not be binary-protocolled.\n");
5231 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
5232 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
5233 fprintf (stderr, "Valid options are:\n");
5234 fprintf (stderr, " collect-before-allocs[=<n>]\n");
5235 fprintf (stderr, " verify-before-allocs[=<n>]\n");
5236 fprintf (stderr, " check-at-minor-collections\n");
5237 fprintf (stderr, " check-mark-bits\n");
5238 fprintf (stderr, " check-nursery-pinned\n");
5239 fprintf (stderr, " verify-before-collections\n");
5240 fprintf (stderr, " verify-nursery-at-minor-gc\n");
5241 fprintf (stderr, " dump-nursery-at-minor-gc\n");
5242 fprintf (stderr, " disable-minor\n");
5243 fprintf (stderr, " disable-major\n");
5244 fprintf (stderr, " xdomain-checks\n");
5245 fprintf (stderr, " check-concurrent\n");
5246 fprintf (stderr, " clear-at-gc\n");
5247 fprintf (stderr, " clear-nursery-at-gc\n");
5248 fprintf (stderr, " check-scan-starts\n");
5249 fprintf (stderr, " no-managed-allocator\n");
5250 fprintf (stderr, " print-allowance\n");
5251 fprintf (stderr, " print-pinning\n");
5252 fprintf (stderr, " heap-dump=<filename>\n");
5253 #ifdef SGEN_BINARY_PROTOCOL
5254 fprintf (stderr, " binary-protocol=<filename>\n");
5262 if (major_collector.is_parallel) {
5263 if (heap_dump_file) {
5264 fprintf (stderr, "Error: Cannot do heap dump with the parallel collector.\n");
5268 fprintf (stderr, "Error: Cannot gather pinning statistics with the parallel collector.\n");
5273 if (major_collector.post_param_init)
5274 major_collector.post_param_init (&major_collector);
5276 sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
5278 memset (&remset, 0, sizeof (remset));
5280 #ifdef SGEN_HAVE_CARDTABLE
5282 sgen_card_table_init (&remset);
5285 sgen_ssb_init (&remset);
5287 if (remset.register_thread)
5288 remset.register_thread (mono_thread_info_current ());
5294 mono_gc_get_gc_name (void)
5299 static MonoMethod *write_barrier_method;
5302 sgen_is_critical_method (MonoMethod *method)
5304 return (method == write_barrier_method || sgen_is_managed_allocator (method));
5308 sgen_has_critical_method (void)
5310 return write_barrier_method || sgen_has_managed_allocator ();
5314 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
5316 memset (nursery_check_return_labels, 0, sizeof (int) * 3);
5317 #ifdef SGEN_ALIGN_NURSERY
5318 // if (ptr_in_nursery (ptr)) return;
5320 * Masking out the bits might be faster, but we would have to use 64 bit
5321 * immediates, which might be slower.
5323 mono_mb_emit_ldarg (mb, 0);
5324 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
5325 mono_mb_emit_byte (mb, CEE_SHR_UN);
5326 mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
5327 nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
5329 if (!major_collector.is_concurrent) {
5330 // if (!ptr_in_nursery (*ptr)) return;
5331 mono_mb_emit_ldarg (mb, 0);
5332 mono_mb_emit_byte (mb, CEE_LDIND_I);
5333 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
5334 mono_mb_emit_byte (mb, CEE_SHR_UN);
5335 mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
5336 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
5339 int label_continue1, label_continue2;
5340 int dereferenced_var;
5342 // if (ptr < (sgen_get_nursery_start ())) goto continue;
5343 mono_mb_emit_ldarg (mb, 0);
5344 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
5345 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
5347 // if (ptr >= sgen_get_nursery_end ())) goto continue;
5348 mono_mb_emit_ldarg (mb, 0);
5349 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
5350 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
5353 nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BR);
5356 mono_mb_patch_branch (mb, label_continue_1);
5357 mono_mb_patch_branch (mb, label_continue_2);
5359 // Dereference and store in local var
5360 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5361 mono_mb_emit_ldarg (mb, 0);
5362 mono_mb_emit_byte (mb, CEE_LDIND_I);
5363 mono_mb_emit_stloc (mb, dereferenced_var);
5365 if (!major_collector.is_concurrent) {
5366 // if (*ptr < sgen_get_nursery_start ()) return;
5367 mono_mb_emit_ldloc (mb, dereferenced_var);
5368 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
5369 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BLT);
5371 // if (*ptr >= sgen_get_nursery_end ()) return;
5372 mono_mb_emit_ldloc (mb, dereferenced_var);
5373 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
5374 nursery_check_return_labels [2] = mono_mb_emit_branch (mb, CEE_BGE);
5380 mono_gc_get_write_barrier (void)
5383 MonoMethodBuilder *mb;
5384 MonoMethodSignature *sig;
5385 #ifdef MANAGED_WBARRIER
5386 int i, nursery_check_labels [3];
5387 int label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
5388 int buffer_var, buffer_index_var, dummy_var;
5390 #ifdef HAVE_KW_THREAD
5391 int stack_end_offset = -1, store_remset_buffer_offset = -1;
5392 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
5394 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
5395 g_assert (stack_end_offset != -1);
5396 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
5397 g_assert (store_remset_buffer_offset != -1);
5398 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
5399 g_assert (store_remset_buffer_index_offset != -1);
5400 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
5401 g_assert (store_remset_buffer_index_addr_offset != -1);
5405 // FIXME: Maybe create a separate version for ctors (the branch would be
5406 // correctly predicted more times)
5407 if (write_barrier_method)
5408 return write_barrier_method;
5410 /* Create the IL version of mono_gc_barrier_generic_store () */
5411 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
5412 sig->ret = &mono_defaults.void_class->byval_arg;
5413 sig->params [0] = &mono_defaults.int_class->byval_arg;
5415 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
5417 #ifdef MANAGED_WBARRIER
5418 if (use_cardtable) {
5419 emit_nursery_check (mb, nursery_check_labels);
5421 addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
5425 LDC_PTR sgen_cardtable
5427 address >> CARD_BITS
5431 if (SGEN_HAVE_OVERLAPPING_CARDS) {
5432 LDC_PTR card_table_mask
5439 mono_mb_emit_ptr (mb, sgen_cardtable);
5440 mono_mb_emit_ldarg (mb, 0);
5441 mono_mb_emit_icon (mb, CARD_BITS);
5442 mono_mb_emit_byte (mb, CEE_SHR_UN);
5443 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
5444 mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
5445 mono_mb_emit_byte (mb, CEE_AND);
5447 mono_mb_emit_byte (mb, CEE_ADD);
5448 mono_mb_emit_icon (mb, 1);
5449 mono_mb_emit_byte (mb, CEE_STIND_I1);
5452 for (i = 0; i < 3; ++i) {
5453 if (nursery_check_labels [i])
5454 mono_mb_patch_branch (mb, nursery_check_labels [i]);
5456 mono_mb_emit_byte (mb, CEE_RET);
5457 } else if (mono_runtime_has_tls_get ()) {
5458 emit_nursery_check (mb, nursery_check_labels);
5460 // if (ptr >= stack_end) goto need_wb;
5461 mono_mb_emit_ldarg (mb, 0);
5462 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
5463 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
5465 // if (ptr >= stack_start) return;
5466 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5467 mono_mb_emit_ldarg (mb, 0);
5468 mono_mb_emit_ldloc_addr (mb, dummy_var);
5469 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
5472 mono_mb_patch_branch (mb, label_need_wb);
5474 // buffer = STORE_REMSET_BUFFER;
5475 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5476 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
5477 mono_mb_emit_stloc (mb, buffer_var);
5479 // buffer_index = STORE_REMSET_BUFFER_INDEX;
5480 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5481 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
5482 mono_mb_emit_stloc (mb, buffer_index_var);
5484 // if (buffer [buffer_index] == ptr) return;
5485 mono_mb_emit_ldloc (mb, buffer_var);
5486 mono_mb_emit_ldloc (mb, buffer_index_var);
5487 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
5488 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
5489 mono_mb_emit_byte (mb, CEE_SHL);
5490 mono_mb_emit_byte (mb, CEE_ADD);
5491 mono_mb_emit_byte (mb, CEE_LDIND_I);
5492 mono_mb_emit_ldarg (mb, 0);
5493 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
5496 mono_mb_emit_ldloc (mb, buffer_index_var);
5497 mono_mb_emit_icon (mb, 1);
5498 mono_mb_emit_byte (mb, CEE_ADD);
5499 mono_mb_emit_stloc (mb, buffer_index_var);
5501 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
5502 mono_mb_emit_ldloc (mb, buffer_index_var);
5503 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
5504 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
5506 // buffer [buffer_index] = ptr;
5507 mono_mb_emit_ldloc (mb, buffer_var);
5508 mono_mb_emit_ldloc (mb, buffer_index_var);
5509 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
5510 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
5511 mono_mb_emit_byte (mb, CEE_SHL);
5512 mono_mb_emit_byte (mb, CEE_ADD);
5513 mono_mb_emit_ldarg (mb, 0);
5514 mono_mb_emit_byte (mb, CEE_STIND_I);
5516 // STORE_REMSET_BUFFER_INDEX = buffer_index;
5517 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
5518 mono_mb_emit_ldloc (mb, buffer_index_var);
5519 mono_mb_emit_byte (mb, CEE_STIND_I);
5522 for (i = 0; i < 3; ++i) {
5523 if (nursery_check_labels [i])
5524 mono_mb_patch_branch (mb, nursery_check_labels [i]);
5526 mono_mb_patch_branch (mb, label_no_wb_3);
5527 mono_mb_patch_branch (mb, label_no_wb_4);
5528 mono_mb_emit_byte (mb, CEE_RET);
5531 mono_mb_patch_branch (mb, label_slow_path);
5533 mono_mb_emit_ldarg (mb, 0);
5534 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
5535 mono_mb_emit_byte (mb, CEE_RET);
5539 mono_mb_emit_ldarg (mb, 0);
5540 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
5541 mono_mb_emit_byte (mb, CEE_RET);
5544 res = mono_mb_create_method (mb, sig, 16);
5547 mono_loader_lock ();
5548 if (write_barrier_method) {
5549 /* Already created */
5550 mono_free_method (res);
5552 /* double-checked locking */
5553 mono_memory_barrier ();
5554 write_barrier_method = res;
5556 mono_loader_unlock ();
5558 return write_barrier_method;
5562 mono_gc_get_description (void)
5564 return g_strdup ("sgen");
5568 mono_gc_set_desktop_mode (void)
5573 mono_gc_is_moving (void)
5579 mono_gc_is_disabled (void)
5585 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
5592 sgen_get_nursery_clear_policy (void)
5594 return nursery_clear_policy;
5598 sgen_get_array_fill_vtable (void)
5600 if (!array_fill_vtable) {
5601 static MonoClass klass;
5602 static MonoVTable vtable;
5605 MonoDomain *domain = mono_get_root_domain ();
5608 klass.element_class = mono_defaults.byte_class;
5610 klass.instance_size = sizeof (MonoArray);
5611 klass.sizes.element_size = 1;
5612 klass.name = "array_filler_type";
5614 vtable.klass = &klass;
5616 vtable.gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
5619 array_fill_vtable = &vtable;
5621 return array_fill_vtable;
5631 sgen_gc_unlock (void)
5637 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
5639 major_collector.iterate_live_block_ranges (callback);
5643 sgen_major_collector_scan_card_table (SgenGrayQueue *queue)
5645 major_collector.scan_card_table (FALSE, queue);
5649 sgen_get_major_collector (void)
5651 return &major_collector;
5654 void mono_gc_set_skip_thread (gboolean skip)
5656 SgenThreadInfo *info = mono_thread_info_current ();
5659 info->gc_disabled = skip;
5664 sgen_get_remset (void)
5670 mono_gc_get_vtable_bits (MonoClass *class)
5672 if (sgen_need_bridge_processing () && sgen_is_bridge_class (class))
5673 return SGEN_GC_BIT_BRIDGE_OBJECT;
5678 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
5685 sgen_check_whole_heap_stw (void)
5687 sgen_stop_world (0);
5688 sgen_clear_nursery_fragments ();
5689 sgen_check_whole_heap (FALSE);
5690 sgen_restart_world (0, NULL);
5694 sgen_gc_event_moves (void)
5696 if (moved_objects_idx) {
5697 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5698 moved_objects_idx = 0;
5702 #endif /* HAVE_SGEN_GC */