2 * sgen-marksweep.c: The Mark & Sweep major collector.
5 * Mark Probst <mark.probst@gmail.com>
7 * Copyright 2009-2010 Novell, Inc.
8 * Copyright (C) 2012 Xamarin Inc
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License 2.0 as published by the Free Software Foundation;
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License 2.0 along with this library; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 #include "utils/mono-counters.h"
32 #include "utils/mono-semaphore.h"
33 #include "utils/mono-time.h"
34 #include "metadata/object-internals.h"
35 #include "metadata/profiler-private.h"
37 #include "metadata/sgen-gc.h"
38 #include "metadata/sgen-protocol.h"
39 #include "metadata/sgen-cardtable.h"
40 #include "metadata/sgen-memory-governor.h"
41 #include "metadata/sgen-layout-stats.h"
42 #include "metadata/gc-internal.h"
43 #include "metadata/sgen-pointer-queue.h"
44 #include "metadata/sgen-pinning.h"
45 #include "metadata/sgen-workers.h"
47 #if defined(ARCH_MIN_MS_BLOCK_SIZE) && defined(ARCH_MIN_MS_BLOCK_SIZE_SHIFT)
48 #define MS_BLOCK_SIZE ARCH_MIN_MS_BLOCK_SIZE
49 #define MS_BLOCK_SIZE_SHIFT ARCH_MIN_MS_BLOCK_SIZE_SHIFT
51 #define MS_BLOCK_SIZE_SHIFT 14 /* INT FASTENABLE */
52 #define MS_BLOCK_SIZE (1 << MS_BLOCK_SIZE_SHIFT)
54 #define MAJOR_SECTION_SIZE MS_BLOCK_SIZE
55 #define CARDS_PER_BLOCK (MS_BLOCK_SIZE / CARD_SIZE_IN_BYTES)
58 * Don't allocate single blocks, but alloc a contingent of this many
59 * blocks in one swoop. This must be a power of two.
61 #define MS_BLOCK_ALLOC_NUM 32
64 * Number of bytes before the first object in a block. At the start
65 * of a block is the MSBlockHeader, then opional padding, then come
66 * the objects, so this must be >= sizeof (MSBlockHeader).
68 #define MS_BLOCK_SKIP ((sizeof (MSBlockHeader) + 15) & ~15)
70 #define MS_BLOCK_FREE (MS_BLOCK_SIZE - MS_BLOCK_SKIP)
72 #define MS_NUM_MARK_WORDS ((MS_BLOCK_SIZE / SGEN_ALLOC_ALIGN + sizeof (mword) * 8 - 1) / (sizeof (mword) * 8))
75 * Blocks progress from one state to the next:
77 * SWEPT The block is fully swept. It might or might not be in
80 * MARKING The block might or might not contain live objects. If
81 * we're in between an initial collection pause and the
82 * finishing pause, the block might or might not be in a
85 * CHECKING The sweep thread is investigating the block to determine
86 * whether or not it contains live objects. The block is
89 * NEED_SWEEPING The block contains live objects but has not yet been
90 * swept. It also contains free slots. It is in a block
93 * SWEEPING The block is being swept. It might be in a free list.
100 BLOCK_STATE_NEED_SWEEPING,
104 typedef struct _MSBlockInfo MSBlockInfo;
105 struct _MSBlockInfo {
108 * FIXME: Do we even need this? It's only used during sweep and might be worth
109 * recalculating to save the space.
111 guint16 obj_size_index;
112 /* FIXME: reduce this */
113 volatile gint32 state;
114 unsigned int pinned : 1;
115 unsigned int has_references : 1;
116 unsigned int has_pinned : 1; /* means cannot evacuate */
117 unsigned int is_to_space : 1;
118 void ** volatile free_list;
119 MSBlockInfo * volatile next_free;
120 guint8 *cardtable_mod_union;
121 mword mark_words [MS_NUM_MARK_WORDS];
124 #define MS_BLOCK_FOR_BLOCK_INFO(b) ((char*)(b))
126 #define MS_BLOCK_OBJ(b,i) (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP + (b)->obj_size * (i))
127 #define MS_BLOCK_OBJ_FOR_SIZE(b,i,obj_size) (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP + (obj_size) * (i))
128 #define MS_BLOCK_DATA_FOR_OBJ(o) ((char*)((mword)(o) & ~(mword)(MS_BLOCK_SIZE - 1)))
134 #define MS_BLOCK_FOR_OBJ(o) (&((MSBlockHeader*)MS_BLOCK_DATA_FOR_OBJ ((o)))->info)
136 /* object index will always be small */
137 #define MS_BLOCK_OBJ_INDEX(o,b) ((int)(((char*)(o) - (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP)) / (b)->obj_size))
139 //casting to int is fine since blocks are 32k
140 #define MS_CALC_MARK_BIT(w,b,o) do { \
141 int i = ((int)((char*)(o) - MS_BLOCK_DATA_FOR_OBJ ((o)))) >> SGEN_ALLOC_ALIGN_BITS; \
142 if (sizeof (mword) == 4) { \
151 #define MS_MARK_BIT(bl,w,b) ((bl)->mark_words [(w)] & (ONE_P << (b)))
152 #define MS_SET_MARK_BIT(bl,w,b) ((bl)->mark_words [(w)] |= (ONE_P << (b)))
154 #define MS_OBJ_ALLOCED(o,b) (*(void**)(o) && (*(char**)(o) < MS_BLOCK_FOR_BLOCK_INFO (b) || *(char**)(o) >= MS_BLOCK_FOR_BLOCK_INFO (b) + MS_BLOCK_SIZE))
156 #define MS_BLOCK_OBJ_SIZE_FACTOR (pow (2.0, 1.0 / 3))
159 * This way we can lookup block object size indexes for sizes up to
160 * 256 bytes with a single load.
162 #define MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES 32
164 static int *block_obj_sizes;
165 static int num_block_obj_sizes;
166 static int fast_block_obj_size_indexes [MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES];
168 #define MS_BLOCK_FLAG_PINNED 1
169 #define MS_BLOCK_FLAG_REFS 2
171 #define MS_BLOCK_TYPE_MAX 4
173 static gboolean *evacuate_block_obj_sizes;
174 static float evacuation_threshold = 0.666f;
175 static float concurrent_evacuation_threshold = 0.666f;
176 static gboolean want_evacuation = FALSE;
178 static gboolean lazy_sweep = FALSE;
182 SWEEP_STATE_NEED_SWEEPING,
183 SWEEP_STATE_SWEEPING,
184 SWEEP_STATE_SWEEPING_AND_ITERATING,
185 SWEEP_STATE_COMPACTING
188 static volatile int sweep_state = SWEEP_STATE_SWEPT;
190 static gboolean concurrent_mark;
192 #define BLOCK_IS_TAGGED_HAS_REFERENCES(bl) SGEN_POINTER_IS_TAGGED_1 ((bl))
193 #define BLOCK_TAG_HAS_REFERENCES(bl) SGEN_POINTER_TAG_1 ((bl))
195 #define BLOCK_IS_TAGGED_CHECKING(bl) SGEN_POINTER_IS_TAGGED_2 ((bl))
196 #define BLOCK_TAG_CHECKING(bl) SGEN_POINTER_TAG_2 ((bl))
198 #define BLOCK_UNTAG(bl) SGEN_POINTER_UNTAG_12 ((bl))
200 #define BLOCK_TAG(bl) ((bl)->has_references ? BLOCK_TAG_HAS_REFERENCES ((bl)) : (bl))
202 /* all allocated blocks in the system */
203 static SgenPointerQueue allocated_blocks;
205 /* non-allocated block free-list */
206 static void *empty_blocks = NULL;
207 static size_t num_empty_blocks = 0;
209 #define FOREACH_BLOCK_NO_LOCK(bl) { size_t __index; SGEN_ASSERT (0, sgen_is_world_stopped () && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { (bl) = BLOCK_UNTAG (allocated_blocks.data [__index]);
210 #define FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK(bl,hr) { size_t __index; SGEN_ASSERT (0, sgen_is_world_stopped () && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { (bl) = allocated_blocks.data [__index]; (hr) = BLOCK_IS_TAGGED_HAS_REFERENCES ((bl)); (bl) = BLOCK_UNTAG ((bl));
211 #define END_FOREACH_BLOCK_NO_LOCK } }
213 static volatile size_t num_major_sections = 0;
215 * One free block list for each block object size. We add and remove blocks from these
216 * lists lock-free via CAS.
218 * Blocks accessed/removed from `free_block_lists`:
219 * from the mutator (with GC lock held)
220 * in nursery collections
221 * in non-concurrent major collections
222 * in the finishing pause of concurrent major collections (whole list is cleared)
224 * Blocks added to `free_block_lists`:
225 * in the sweeping thread
226 * during nursery collections
227 * from domain clearing (with the world stopped and no sweeping happening)
229 * The only item of those that doesn't require the GC lock is the sweep thread. The sweep
230 * thread only ever adds blocks to the free list, so the ABA problem can't occur.
232 static MSBlockInfo * volatile *free_block_lists [MS_BLOCK_TYPE_MAX];
234 static guint64 stat_major_blocks_alloced = 0;
235 static guint64 stat_major_blocks_freed = 0;
236 static guint64 stat_major_blocks_lazy_swept = 0;
237 static guint64 stat_major_objects_evacuated = 0;
239 #if SIZEOF_VOID_P != 8
240 static guint64 stat_major_blocks_freed_ideal = 0;
241 static guint64 stat_major_blocks_freed_less_ideal = 0;
242 static guint64 stat_major_blocks_freed_individual = 0;
243 static guint64 stat_major_blocks_alloced_less_ideal = 0;
246 #ifdef SGEN_COUNT_NUMBER_OF_MAJOR_OBJECTS_MARKED
247 static guint64 num_major_objects_marked = 0;
248 #define INC_NUM_MAJOR_OBJECTS_MARKED() (++num_major_objects_marked)
250 #define INC_NUM_MAJOR_OBJECTS_MARKED()
253 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
254 static mono_mutex_t scanned_objects_list_lock;
255 static SgenPointerQueue scanned_objects_list;
258 add_scanned_object (void *ptr)
260 if (!binary_protocol_is_enabled ())
263 mono_mutex_lock (&scanned_objects_list_lock);
264 sgen_pointer_queue_add (&scanned_objects_list, ptr);
265 mono_mutex_unlock (&scanned_objects_list_lock);
269 static gboolean sweep_block (MSBlockInfo *block);
272 ms_find_block_obj_size_index (size_t size)
275 SGEN_ASSERT (9, size <= SGEN_MAX_SMALL_OBJ_SIZE, "size %d is bigger than max small object size %d", size, SGEN_MAX_SMALL_OBJ_SIZE);
276 for (i = 0; i < num_block_obj_sizes; ++i)
277 if (block_obj_sizes [i] >= size)
279 g_error ("no object of size %d\n", size);
282 #define FREE_BLOCKS_FROM(lists,p,r) (lists [((p) ? MS_BLOCK_FLAG_PINNED : 0) | ((r) ? MS_BLOCK_FLAG_REFS : 0)])
283 #define FREE_BLOCKS(p,r) (FREE_BLOCKS_FROM (free_block_lists, (p), (r)))
285 #define MS_BLOCK_OBJ_SIZE_INDEX(s) \
286 (((s)+7)>>3 < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES ? \
287 fast_block_obj_size_indexes [((s)+7)>>3] : \
288 ms_find_block_obj_size_index ((s)))
291 major_alloc_heap (mword nursery_size, mword nursery_align, int the_nursery_bits)
295 start = sgen_alloc_os_memory_aligned (nursery_size, nursery_align, SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE, "nursery");
297 start = sgen_alloc_os_memory (nursery_size, SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE, "nursery");
303 update_heap_boundaries_for_block (MSBlockInfo *block)
305 sgen_update_heap_boundaries ((mword)MS_BLOCK_FOR_BLOCK_INFO (block), (mword)MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE);
312 ms_get_empty_block (void)
316 void *block, *empty, *next;
321 * We try allocating MS_BLOCK_ALLOC_NUM blocks first. If that's
322 * unsuccessful, we halve the number of blocks and try again, until we're at
323 * 1. If that doesn't work, either, we assert.
325 int alloc_num = MS_BLOCK_ALLOC_NUM;
327 p = sgen_alloc_os_memory_aligned (MS_BLOCK_SIZE * alloc_num, MS_BLOCK_SIZE, SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE,
328 alloc_num == 1 ? "major heap section" : NULL);
334 for (i = 0; i < alloc_num; ++i) {
337 * We do the free list update one after the
338 * other so that other threads can use the new
339 * blocks as quickly as possible.
342 empty = empty_blocks;
343 *(void**)block = empty;
344 } while (SGEN_CAS_PTR ((gpointer*)&empty_blocks, block, empty) != empty);
348 SGEN_ATOMIC_ADD_P (num_empty_blocks, alloc_num);
350 stat_major_blocks_alloced += alloc_num;
351 #if SIZEOF_VOID_P != 8
352 if (alloc_num != MS_BLOCK_ALLOC_NUM)
353 stat_major_blocks_alloced_less_ideal += alloc_num;
358 empty = empty_blocks;
362 next = *(void**)block;
363 } while (SGEN_CAS_PTR (&empty_blocks, next, empty) != empty);
365 SGEN_ATOMIC_ADD_P (num_empty_blocks, -1);
367 *(void**)block = NULL;
369 g_assert (!((mword)block & (MS_BLOCK_SIZE - 1)));
375 * This doesn't actually free a block immediately, but enqueues it into the `empty_blocks`
376 * list, where it will either be freed later on, or reused in nursery collections.
379 ms_free_block (void *block)
383 sgen_memgov_release_space (MS_BLOCK_SIZE, SPACE_MAJOR);
384 memset (block, 0, MS_BLOCK_SIZE);
387 empty = empty_blocks;
388 *(void**)block = empty;
389 } while (SGEN_CAS_PTR (&empty_blocks, block, empty) != empty);
391 SGEN_ATOMIC_ADD_P (num_empty_blocks, 1);
393 binary_protocol_block_free (block, MS_BLOCK_SIZE);
397 sweep_in_progress (void)
399 int state = sweep_state;
400 return state == SWEEP_STATE_SWEEPING ||
401 state == SWEEP_STATE_SWEEPING_AND_ITERATING ||
402 state == SWEEP_STATE_COMPACTING;
405 static inline gboolean
406 block_is_swept_or_marking (MSBlockInfo *block)
408 gint32 state = block->state;
409 return state == BLOCK_STATE_SWEPT || state == BLOCK_STATE_MARKING;
412 //#define MARKSWEEP_CONSISTENCY_CHECK
414 #ifdef MARKSWEEP_CONSISTENCY_CHECK
416 check_block_free_list (MSBlockInfo *block, int size, gboolean pinned)
418 SGEN_ASSERT (0, !sweep_in_progress (), "Can't examine allocated blocks during sweep");
419 for (; block; block = block->next_free) {
420 SGEN_ASSERT (0, block->state != BLOCK_STATE_CHECKING, "Can't have a block we're checking in a free list.");
421 g_assert (block->obj_size == size);
422 g_assert ((pinned && block->pinned) || (!pinned && !block->pinned));
424 /* blocks in the free lists must have at least
426 g_assert (block->free_list);
428 /* the block must be in the allocated_blocks array */
429 g_assert (sgen_pointer_queue_find (&allocated_blocks, BLOCK_TAG (block)) != (size_t)-1);
434 check_empty_blocks (void)
438 for (p = empty_blocks; p; p = *(void**)p)
440 g_assert (i == num_empty_blocks);
444 consistency_check (void)
449 /* check all blocks */
450 FOREACH_BLOCK_NO_LOCK (block) {
451 int count = MS_BLOCK_FREE / block->obj_size;
455 /* count number of free slots */
456 for (i = 0; i < count; ++i) {
457 void **obj = (void**) MS_BLOCK_OBJ (block, i);
458 if (!MS_OBJ_ALLOCED (obj, block))
462 /* check free list */
463 for (free = block->free_list; free; free = (void**)*free) {
464 g_assert (MS_BLOCK_FOR_OBJ (free) == block);
467 g_assert (num_free == 0);
469 /* check all mark words are zero */
470 if (!sgen_concurrent_collection_in_progress () && block_is_swept_or_marking (block)) {
471 for (i = 0; i < MS_NUM_MARK_WORDS; ++i)
472 g_assert (block->mark_words [i] == 0);
474 } END_FOREACH_BLOCK_NO_LOCK;
476 /* check free blocks */
477 for (i = 0; i < num_block_obj_sizes; ++i) {
479 for (j = 0; j < MS_BLOCK_TYPE_MAX; ++j)
480 check_block_free_list (free_block_lists [j][i], block_obj_sizes [i], j & MS_BLOCK_FLAG_PINNED);
483 check_empty_blocks ();
488 add_free_block (MSBlockInfo * volatile *free_blocks, int size_index, MSBlockInfo *block)
492 block->next_free = old = free_blocks [size_index];
493 } while (SGEN_CAS_PTR ((gpointer)&free_blocks [size_index], block, old) != old);
496 static void major_finish_sweep_checking (void);
499 ms_alloc_block (int size_index, gboolean pinned, gboolean has_references)
501 int size = block_obj_sizes [size_index];
502 int count = MS_BLOCK_FREE / size;
504 MSBlockInfo * volatile * free_blocks = FREE_BLOCKS (pinned, has_references);
508 if (!sgen_memgov_try_alloc_space (MS_BLOCK_SIZE, SPACE_MAJOR))
511 info = (MSBlockInfo*)ms_get_empty_block ();
513 SGEN_ASSERT (9, count >= 2, "block with %d objects, it must hold at least 2", count);
515 info->obj_size = size;
516 info->obj_size_index = size_index;
517 info->pinned = pinned;
518 info->has_references = has_references;
519 info->has_pinned = pinned;
521 * Blocks that are to-space are not evacuated from. During an major collection
522 * blocks are allocated for two reasons: evacuating objects from the nursery and
523 * evacuating them from major blocks marked for evacuation. In both cases we don't
524 * want further evacuation.
526 info->is_to_space = (sgen_get_current_collection_generation () == GENERATION_OLD);
527 info->state = (info->is_to_space || sgen_concurrent_collection_in_progress ()) ? BLOCK_STATE_MARKING : BLOCK_STATE_SWEPT;
528 if (sweep_in_progress ())
529 SGEN_ASSERT (0, info->state == BLOCK_STATE_SWEPT, "How do we add a new block to be swept while sweeping?");
530 info->cardtable_mod_union = NULL;
532 update_heap_boundaries_for_block (info);
534 binary_protocol_block_alloc (info, MS_BLOCK_SIZE);
536 /* build free list */
537 obj_start = MS_BLOCK_FOR_BLOCK_INFO (info) + MS_BLOCK_SKIP;
538 info->free_list = (void**)obj_start;
539 /* we're skipping the last one - it must be nulled */
540 for (i = 0; i < count - 1; ++i) {
541 char *next_obj_start = obj_start + size;
542 *(void**)obj_start = next_obj_start;
543 obj_start = next_obj_start;
546 *(void**)obj_start = NULL;
548 add_free_block (free_blocks, size_index, info);
551 * This is the only place where the `allocated_blocks` array can potentially grow.
552 * We need to make sure concurrent sweep isn't running when that happens, so in that
553 * specific case we just wait for sweep to finish.
555 if (sgen_pointer_queue_will_grow (&allocated_blocks))
556 major_finish_sweep_checking ();
558 sgen_pointer_queue_add (&allocated_blocks, BLOCK_TAG (info));
560 SGEN_ATOMIC_ADD_P (num_major_sections, 1);
565 obj_is_from_pinned_alloc (char *ptr)
569 FOREACH_BLOCK_NO_LOCK (block) {
570 if (ptr >= MS_BLOCK_FOR_BLOCK_INFO (block) && ptr <= MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE)
571 return block->pinned;
572 } END_FOREACH_BLOCK_NO_LOCK;
577 ensure_can_access_block_free_list (MSBlockInfo *block)
581 switch (block->state) {
582 case BLOCK_STATE_SWEPT:
583 case BLOCK_STATE_MARKING:
585 case BLOCK_STATE_CHECKING:
586 SGEN_ASSERT (0, FALSE, "How did we get a block that's being checked from a free list?");
588 case BLOCK_STATE_NEED_SWEEPING:
589 if (sweep_block (block))
590 ++stat_major_blocks_lazy_swept;
592 case BLOCK_STATE_SWEEPING:
593 /* FIXME: do this more elegantly */
597 SGEN_ASSERT (0, FALSE, "Illegal block state");
604 unlink_slot_from_free_list_uncontested (MSBlockInfo * volatile *free_blocks, int size_index)
606 MSBlockInfo *block, *next_free_block;
607 void *obj, *next_free_slot;
610 block = free_blocks [size_index];
611 SGEN_ASSERT (9, block, "no free block to unlink from free_blocks %p size_index %d", free_blocks, size_index);
613 ensure_can_access_block_free_list (block);
615 obj = block->free_list;
616 SGEN_ASSERT (0, obj, "block %p in free list had no available object to alloc from", block);
618 next_free_slot = *(void**)obj;
619 if (next_free_slot) {
620 block->free_list = next_free_slot;
624 next_free_block = block->next_free;
625 if (SGEN_CAS_PTR ((gpointer)&free_blocks [size_index], next_free_block, block) != block)
628 block->free_list = NULL;
629 block->next_free = NULL;
635 alloc_obj (MonoVTable *vtable, size_t size, gboolean pinned, gboolean has_references)
637 int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
638 MSBlockInfo * volatile * free_blocks = FREE_BLOCKS (pinned, has_references);
641 if (!free_blocks [size_index]) {
642 if (G_UNLIKELY (!ms_alloc_block (size_index, pinned, has_references)))
646 obj = unlink_slot_from_free_list_uncontested (free_blocks, size_index);
648 *(MonoVTable**)obj = vtable;
654 major_alloc_object (MonoVTable *vtable, size_t size, gboolean has_references)
656 return alloc_obj (vtable, size, FALSE, has_references);
660 * We're not freeing the block if it's empty. We leave that work for
661 * the next major collection.
663 * This is just called from the domain clearing code, which runs in a
664 * single thread and has the GC lock, so we don't need an extra lock.
667 free_object (char *obj, size_t size, gboolean pinned)
669 MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
671 gboolean in_free_list;
673 SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Should have waited for sweep to free objects.");
675 ensure_can_access_block_free_list (block);
676 SGEN_ASSERT (9, (pinned && block->pinned) || (!pinned && !block->pinned), "free-object pinning mixup object %p pinned %d block %p pinned %d", obj, pinned, block, block->pinned);
677 SGEN_ASSERT (9, MS_OBJ_ALLOCED (obj, block), "object %p is already free", obj);
678 MS_CALC_MARK_BIT (word, bit, obj);
679 SGEN_ASSERT (9, !MS_MARK_BIT (block, word, bit), "object %p has mark bit set");
681 memset (obj, 0, size);
683 in_free_list = !!block->free_list;
684 *(void**)obj = block->free_list;
685 block->free_list = (void**)obj;
688 MSBlockInfo * volatile *free_blocks = FREE_BLOCKS (pinned, block->has_references);
689 int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
690 SGEN_ASSERT (9, !block->next_free, "block %p doesn't have a free-list of object but belongs to a free-list of blocks");
691 add_free_block (free_blocks, size_index, block);
696 major_free_non_pinned_object (char *obj, size_t size)
698 free_object (obj, size, FALSE);
701 /* size is a multiple of SGEN_ALLOC_ALIGN */
703 major_alloc_small_pinned_obj (MonoVTable *vtable, size_t size, gboolean has_references)
707 res = alloc_obj (vtable, size, TRUE, has_references);
708 /*If we failed to alloc memory, we better try releasing memory
709 *as pinned alloc is requested by the runtime.
712 sgen_perform_collection (0, GENERATION_OLD, "pinned alloc failure", TRUE);
713 res = alloc_obj (vtable, size, TRUE, has_references);
719 free_pinned_object (char *obj, size_t size)
721 free_object (obj, size, TRUE);
725 * size is already rounded up and we hold the GC lock.
728 major_alloc_degraded (MonoVTable *vtable, size_t size)
730 void *obj = alloc_obj (vtable, size, FALSE, SGEN_VTABLE_HAS_REFERENCES (vtable));
731 if (G_LIKELY (obj)) {
732 HEAVY_STAT (++stat_objects_alloced_degraded);
733 HEAVY_STAT (stat_bytes_alloced_degraded += size);
739 * obj is some object. If it's not in the major heap (i.e. if it's in
740 * the nursery or LOS), return FALSE. Otherwise return whether it's
741 * been marked or copied.
744 major_is_object_live (char *obj)
750 if (sgen_ptr_in_nursery (obj))
753 objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj));
756 if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
759 /* now we know it's in a major block */
760 block = MS_BLOCK_FOR_OBJ (obj);
761 SGEN_ASSERT (9, !block->pinned, "block %p is pinned, BTW why is this bad?");
762 MS_CALC_MARK_BIT (word, bit, obj);
763 return MS_MARK_BIT (block, word, bit) ? TRUE : FALSE;
767 major_ptr_is_in_non_pinned_space (char *ptr, char **start)
771 FOREACH_BLOCK_NO_LOCK (block) {
772 if (ptr >= MS_BLOCK_FOR_BLOCK_INFO (block) && ptr <= MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) {
773 int count = MS_BLOCK_FREE / block->obj_size;
777 for (i = 0; i <= count; ++i) {
778 if (ptr >= MS_BLOCK_OBJ (block, i) && ptr < MS_BLOCK_OBJ (block, i + 1)) {
779 *start = MS_BLOCK_OBJ (block, i);
783 return !block->pinned;
785 } END_FOREACH_BLOCK_NO_LOCK;
790 try_set_sweep_state (int new, int expected)
792 int old = SGEN_CAS (&sweep_state, new, expected);
793 return old == expected;
797 set_sweep_state (int new, int expected)
799 gboolean success = try_set_sweep_state (new, expected);
800 SGEN_ASSERT (0, success, "Could not set sweep state.");
803 static gboolean ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *have_checked);
806 major_finish_sweep_checking (void)
811 switch (sweep_state) {
812 case SWEEP_STATE_SWEPT:
813 case SWEEP_STATE_NEED_SWEEPING:
815 case SWEEP_STATE_SWEEPING:
816 if (try_set_sweep_state (SWEEP_STATE_SWEEPING_AND_ITERATING, SWEEP_STATE_SWEEPING))
819 case SWEEP_STATE_SWEEPING_AND_ITERATING:
820 SGEN_ASSERT (0, FALSE, "Is there another minor collection running?");
822 case SWEEP_STATE_COMPACTING:
826 SGEN_ASSERT (0, FALSE, "Invalid sweep state.");
831 * We're running with the world stopped and the only other thread doing work is the
832 * sweep thread, which doesn't add blocks to the array, so we can safely access
835 for (block_index = 0; block_index < allocated_blocks.next_slot; ++block_index)
836 ensure_block_is_checked_for_sweeping (block_index, FALSE, NULL);
838 set_sweep_state (SWEEP_STATE_SWEEPING, SWEEP_STATE_SWEEPING_AND_ITERATING);
839 while (sweep_state != SWEEP_STATE_SWEPT)
844 major_iterate_objects (IterateObjectsFlags flags, IterateObjectCallbackFunc callback, void *data)
846 gboolean sweep = flags & ITERATE_OBJECTS_SWEEP;
847 gboolean non_pinned = flags & ITERATE_OBJECTS_NON_PINNED;
848 gboolean pinned = flags & ITERATE_OBJECTS_PINNED;
851 major_finish_sweep_checking ();
852 FOREACH_BLOCK_NO_LOCK (block) {
853 int count = MS_BLOCK_FREE / block->obj_size;
856 if (block->pinned && !pinned)
858 if (!block->pinned && !non_pinned)
860 if (sweep && lazy_sweep) {
861 /* FIXME: We can't just call `sweep_block` willy-nilly. */
863 SGEN_ASSERT (0, block->state == BLOCK_STATE_SWEPT, "Block must be swept after sweeping");
866 for (i = 0; i < count; ++i) {
867 void **obj = (void**) MS_BLOCK_OBJ (block, i);
868 /* FIXME: This condition is probably incorrect. */
869 if (!block_is_swept_or_marking (block)) {
871 MS_CALC_MARK_BIT (word, bit, obj);
872 if (!MS_MARK_BIT (block, word, bit))
875 if (MS_OBJ_ALLOCED (obj, block))
876 callback ((char*)obj, block->obj_size, data);
878 } END_FOREACH_BLOCK_NO_LOCK;
882 major_is_valid_object (char *object)
886 FOREACH_BLOCK_NO_LOCK (block) {
890 if ((MS_BLOCK_FOR_BLOCK_INFO (block) > object) || ((MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) <= object))
893 idx = MS_BLOCK_OBJ_INDEX (object, block);
894 obj = (char*)MS_BLOCK_OBJ (block, idx);
897 return MS_OBJ_ALLOCED (obj, block);
898 } END_FOREACH_BLOCK_NO_LOCK;
905 major_describe_pointer (char *ptr)
909 FOREACH_BLOCK_NO_LOCK (block) {
917 if ((MS_BLOCK_FOR_BLOCK_INFO (block) > ptr) || ((MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) <= ptr))
920 SGEN_LOG (0, "major-ptr (block %p sz %d pin %d ref %d)\n",
921 MS_BLOCK_FOR_BLOCK_INFO (block), block->obj_size, block->pinned, block->has_references);
923 idx = MS_BLOCK_OBJ_INDEX (ptr, block);
924 obj = (char*)MS_BLOCK_OBJ (block, idx);
925 live = MS_OBJ_ALLOCED (obj, block);
926 vtable = live ? (MonoVTable*)SGEN_LOAD_VTABLE (obj) : NULL;
928 MS_CALC_MARK_BIT (w, b, obj);
929 marked = MS_MARK_BIT (block, w, b);
934 SGEN_LOG (0, "object");
936 SGEN_LOG (0, "dead-object");
939 SGEN_LOG (0, "interior-ptr offset %td", ptr - obj);
941 SGEN_LOG (0, "dead-interior-ptr offset %td", ptr - obj);
944 SGEN_LOG (0, " marked %d)\n", marked ? 1 : 0);
947 } END_FOREACH_BLOCK_NO_LOCK;
953 major_check_scan_starts (void)
958 major_dump_heap (FILE *heap_dump_file)
961 int *slots_available = alloca (sizeof (int) * num_block_obj_sizes);
962 int *slots_used = alloca (sizeof (int) * num_block_obj_sizes);
965 for (i = 0; i < num_block_obj_sizes; ++i)
966 slots_available [i] = slots_used [i] = 0;
968 FOREACH_BLOCK_NO_LOCK (block) {
969 int index = ms_find_block_obj_size_index (block->obj_size);
970 int count = MS_BLOCK_FREE / block->obj_size;
972 slots_available [index] += count;
973 for (i = 0; i < count; ++i) {
974 if (MS_OBJ_ALLOCED (MS_BLOCK_OBJ (block, i), block))
975 ++slots_used [index];
977 } END_FOREACH_BLOCK_NO_LOCK;
979 fprintf (heap_dump_file, "<occupancies>\n");
980 for (i = 0; i < num_block_obj_sizes; ++i) {
981 fprintf (heap_dump_file, "<occupancy size=\"%d\" available=\"%d\" used=\"%d\" />\n",
982 block_obj_sizes [i], slots_available [i], slots_used [i]);
984 fprintf (heap_dump_file, "</occupancies>\n");
986 FOREACH_BLOCK_NO_LOCK (block) {
987 int count = MS_BLOCK_FREE / block->obj_size;
991 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", "old", (size_t)MS_BLOCK_FREE);
993 for (i = 0; i <= count; ++i) {
994 if ((i < count) && MS_OBJ_ALLOCED (MS_BLOCK_OBJ (block, i), block)) {
999 sgen_dump_occupied (MS_BLOCK_OBJ (block, start), MS_BLOCK_OBJ (block, i), MS_BLOCK_FOR_BLOCK_INFO (block));
1005 fprintf (heap_dump_file, "</section>\n");
1006 } END_FOREACH_BLOCK_NO_LOCK;
1009 #define LOAD_VTABLE SGEN_LOAD_VTABLE
1011 #define MS_MARK_OBJECT_AND_ENQUEUE_CHECKED(obj,desc,block,queue) do { \
1012 int __word, __bit; \
1013 MS_CALC_MARK_BIT (__word, __bit, (obj)); \
1014 if (!MS_MARK_BIT ((block), __word, __bit) && MS_OBJ_ALLOCED ((obj), (block))) { \
1015 MS_SET_MARK_BIT ((block), __word, __bit); \
1016 if (sgen_gc_descr_has_references (desc)) \
1017 GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
1018 binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
1019 INC_NUM_MAJOR_OBJECTS_MARKED (); \
1022 #define MS_MARK_OBJECT_AND_ENQUEUE(obj,desc,block,queue) do { \
1023 int __word, __bit; \
1024 MS_CALC_MARK_BIT (__word, __bit, (obj)); \
1025 SGEN_ASSERT (9, MS_OBJ_ALLOCED ((obj), (block)), "object %p not allocated", obj); \
1026 if (!MS_MARK_BIT ((block), __word, __bit)) { \
1027 MS_SET_MARK_BIT ((block), __word, __bit); \
1028 if (sgen_gc_descr_has_references (desc)) \
1029 GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
1030 binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
1031 INC_NUM_MAJOR_OBJECTS_MARKED (); \
1036 pin_major_object (char *obj, SgenGrayQueue *queue)
1040 if (concurrent_mark)
1041 g_assert_not_reached ();
1043 block = MS_BLOCK_FOR_OBJ (obj);
1044 block->has_pinned = TRUE;
1045 MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
1048 #include "sgen-major-copy-object.h"
1051 major_copy_or_mark_object_with_evacuation_concurrent (void **ptr, void *obj, SgenGrayQueue *queue)
1053 SGEN_ASSERT (9, sgen_concurrent_collection_in_progress (), "Why are we scanning concurrently when there's no concurrent collection on?");
1054 SGEN_ASSERT (9, !sgen_workers_are_working () || sgen_is_worker_thread (mono_native_thread_id_get ()), "We must not scan from two threads at the same time!");
1056 g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
1058 if (!sgen_ptr_in_nursery (obj)) {
1061 objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj));
1063 if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE) {
1064 MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
1065 MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
1067 if (sgen_los_object_is_pinned (obj))
1070 #ifdef ENABLE_DTRACE
1071 if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
1072 MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
1073 MONO_GC_OBJ_PINNED ((mword)obj, sgen_safe_object_get_size (obj), vt->klass->name_space, vt->klass->name, GENERATION_OLD);
1077 sgen_los_pin_object (obj);
1078 if (SGEN_OBJECT_HAS_REFERENCES (obj))
1079 GRAY_OBJECT_ENQUEUE (queue, obj, sgen_obj_get_descriptor (obj));
1080 INC_NUM_MAJOR_OBJECTS_MARKED ();
1086 major_get_and_reset_num_major_objects_marked (void)
1088 #ifdef SGEN_COUNT_NUMBER_OF_MAJOR_OBJECTS_MARKED
1089 long long num = num_major_objects_marked;
1090 num_major_objects_marked = 0;
1097 #define PREFETCH_CARDS 1 /* BOOL FASTENABLE */
1099 #undef PREFETCH_CARDS
1102 /* gcc 4.2.1 from xcode4 crashes on sgen_card_table_get_card_address () when this is enabled */
1103 #if defined(PLATFORM_MACOSX)
1104 #define GCC_VERSION (__GNUC__ * 10000 \
1105 + __GNUC_MINOR__ * 100 \
1106 + __GNUC_PATCHLEVEL__)
1107 #if GCC_VERSION <= 40300
1108 #undef PREFETCH_CARDS
1112 #ifdef HEAVY_STATISTICS
1113 static guint64 stat_optimized_copy;
1114 static guint64 stat_optimized_copy_nursery;
1115 static guint64 stat_optimized_copy_nursery_forwarded;
1116 static guint64 stat_optimized_copy_nursery_pinned;
1117 static guint64 stat_optimized_copy_major;
1118 static guint64 stat_optimized_copy_major_small_fast;
1119 static guint64 stat_optimized_copy_major_small_slow;
1120 static guint64 stat_optimized_copy_major_large;
1121 static guint64 stat_optimized_copy_major_forwarded;
1122 static guint64 stat_optimized_copy_major_small_evacuate;
1123 static guint64 stat_optimized_major_scan;
1124 static guint64 stat_optimized_major_scan_no_refs;
1126 static guint64 stat_drain_prefetch_fills;
1127 static guint64 stat_drain_prefetch_fill_failures;
1128 static guint64 stat_drain_loops;
1131 static void major_scan_object_with_evacuation (char *start, mword desc, SgenGrayQueue *queue);
1133 #define COPY_OR_MARK_FUNCTION_NAME major_copy_or_mark_object_no_evacuation
1134 #define SCAN_OBJECT_FUNCTION_NAME major_scan_object_no_evacuation
1135 #define DRAIN_GRAY_STACK_FUNCTION_NAME drain_gray_stack_no_evacuation
1136 #include "sgen-marksweep-drain-gray-stack.h"
1138 #define COPY_OR_MARK_WITH_EVACUATION
1139 #define COPY_OR_MARK_FUNCTION_NAME major_copy_or_mark_object_with_evacuation
1140 #define SCAN_OBJECT_FUNCTION_NAME major_scan_object_with_evacuation
1141 #define DRAIN_GRAY_STACK_FUNCTION_NAME drain_gray_stack_with_evacuation
1142 #include "sgen-marksweep-drain-gray-stack.h"
1145 drain_gray_stack (ScanCopyContext ctx)
1147 gboolean evacuation = FALSE;
1149 for (i = 0; i < num_block_obj_sizes; ++i) {
1150 if (evacuate_block_obj_sizes [i]) {
1157 return drain_gray_stack_with_evacuation (ctx);
1159 return drain_gray_stack_no_evacuation (ctx);
1162 #include "sgen-marksweep-scan-object-concurrent.h"
1165 major_copy_or_mark_object_canonical (void **ptr, SgenGrayQueue *queue)
1167 major_copy_or_mark_object_with_evacuation (ptr, *ptr, queue);
1171 major_copy_or_mark_object_concurrent_canonical (void **ptr, SgenGrayQueue *queue)
1173 major_copy_or_mark_object_with_evacuation_concurrent (ptr, *ptr, queue);
1177 mark_pinned_objects_in_block (MSBlockInfo *block, size_t first_entry, size_t last_entry, SgenGrayQueue *queue)
1179 void **entry, **end;
1180 int last_index = -1;
1182 if (first_entry == last_entry)
1185 block->has_pinned = TRUE;
1187 entry = sgen_pinning_get_entry (first_entry);
1188 end = sgen_pinning_get_entry (last_entry);
1190 for (; entry < end; ++entry) {
1191 int index = MS_BLOCK_OBJ_INDEX (*entry, block);
1193 SGEN_ASSERT (9, index >= 0 && index < MS_BLOCK_FREE / block->obj_size, "invalid object %p index %d max-index %d", *entry, index, MS_BLOCK_FREE / block->obj_size);
1194 if (index == last_index)
1196 obj = MS_BLOCK_OBJ (block, index);
1197 MS_MARK_OBJECT_AND_ENQUEUE_CHECKED (obj, sgen_obj_get_descriptor (obj), block, queue);
1203 sweep_block_for_size (MSBlockInfo *block, int count, int obj_size)
1207 for (obj_index = 0; obj_index < count; ++obj_index) {
1209 void *obj = MS_BLOCK_OBJ_FOR_SIZE (block, obj_index, obj_size);
1211 MS_CALC_MARK_BIT (word, bit, obj);
1212 if (MS_MARK_BIT (block, word, bit)) {
1213 SGEN_ASSERT (9, MS_OBJ_ALLOCED (obj, block), "object %p not allocated", obj);
1215 /* an unmarked object */
1216 if (MS_OBJ_ALLOCED (obj, block)) {
1218 * FIXME: Merge consecutive
1219 * slots for lower reporting
1220 * overhead. Maybe memset
1221 * will also benefit?
1223 binary_protocol_empty (obj, obj_size);
1224 MONO_GC_MAJOR_SWEPT ((mword)obj, obj_size);
1225 memset (obj, 0, obj_size);
1227 *(void**)obj = block->free_list;
1228 block->free_list = obj;
1234 try_set_block_state (MSBlockInfo *block, gint32 new_state, gint32 expected_state)
1236 gint32 old_state = SGEN_CAS (&block->state, new_state, expected_state);
1237 gboolean success = old_state == expected_state;
1239 binary_protocol_block_set_state (block, MS_BLOCK_SIZE, old_state, new_state);
1244 * FIXME: This only CASes to catch errors. It's not needed for correctness.
1247 set_block_state (MSBlockInfo *block, gint32 new_state, gint32 expected_state)
1249 gboolean success = try_set_block_state (block, new_state, expected_state);
1250 SGEN_ASSERT (0, success, "Couldn't set block state");
1251 SGEN_ASSERT (0, block->state == new_state, "Block state incorrect after set");
1255 * If `block` needs sweeping, sweep it and return TRUE. Otherwise return FALSE.
1257 * Sweeping means iterating through the block's slots and building the free-list from the
1258 * unmarked ones. They will also be zeroed. The mark bits will be reset.
1261 sweep_block (MSBlockInfo *block)
1264 void *reversed = NULL;
1267 switch (block->state) {
1268 case BLOCK_STATE_SWEPT:
1270 case BLOCK_STATE_MARKING:
1271 case BLOCK_STATE_CHECKING:
1272 SGEN_ASSERT (0, FALSE, "How did we get to sweep a block that's being marked or being checked?");
1274 case BLOCK_STATE_SWEEPING:
1275 /* FIXME: Do this more elegantly */
1278 case BLOCK_STATE_NEED_SWEEPING:
1279 if (!try_set_block_state (block, BLOCK_STATE_SWEEPING, BLOCK_STATE_NEED_SWEEPING))
1283 SGEN_ASSERT (0, FALSE, "Illegal block state");
1286 SGEN_ASSERT (0, block->state == BLOCK_STATE_SWEEPING, "How did we get here without setting state to sweeping?");
1288 count = MS_BLOCK_FREE / block->obj_size;
1290 block->free_list = NULL;
1292 /* Use inline instances specialized to constant sizes, this allows the compiler to replace the memset calls with inline code */
1293 // FIXME: Add more sizes
1294 switch (block->obj_size) {
1296 sweep_block_for_size (block, count, 16);
1299 sweep_block_for_size (block, count, block->obj_size);
1303 /* reset mark bits */
1304 memset (block->mark_words, 0, sizeof (mword) * MS_NUM_MARK_WORDS);
1306 /* Reverse free list so that it's in address order */
1308 while (block->free_list) {
1309 void *next = *(void**)block->free_list;
1310 *(void**)block->free_list = reversed;
1311 reversed = block->free_list;
1312 block->free_list = next;
1314 block->free_list = reversed;
1316 mono_memory_write_barrier ();
1318 set_block_state (block, BLOCK_STATE_SWEPT, BLOCK_STATE_SWEEPING);
1329 if (sizeof (mword) == sizeof (unsigned long))
1330 count += __builtin_popcountl (d);
1332 count += __builtin_popcount (d);
1342 /* statistics for evacuation */
1343 static size_t *sweep_slots_available;
1344 static size_t *sweep_slots_used;
1345 static size_t *sweep_num_blocks;
1347 static volatile size_t num_major_sections_before_sweep;
1348 static volatile size_t num_major_sections_freed_in_sweep;
1355 for (i = 0; i < num_block_obj_sizes; ++i)
1356 sweep_slots_available [i] = sweep_slots_used [i] = sweep_num_blocks [i] = 0;
1358 /* clear all the free lists */
1359 for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i) {
1360 MSBlockInfo * volatile *free_blocks = free_block_lists [i];
1362 for (j = 0; j < num_block_obj_sizes; ++j)
1363 free_blocks [j] = NULL;
1367 static void sweep_finish (void);
1370 * If `wait` is TRUE and the block is currently being checked, this function will wait until
1371 * the checking has finished.
1373 * Returns whether the block is still there. If `wait` is FALSE, the return value will not
1374 * be correct, i.e. must not be used.
1377 ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *have_checked)
1380 gboolean have_live = FALSE;
1381 gboolean have_free = FALSE;
1388 SGEN_ASSERT (0, sweep_in_progress (), "Why do we call this function if there's no sweep in progress?");
1391 *have_checked = FALSE;
1394 tagged_block = *(void * volatile *)&allocated_blocks.data [block_index];
1398 if (BLOCK_IS_TAGGED_CHECKING (tagged_block)) {
1401 /* FIXME: do this more elegantly */
1406 if (SGEN_CAS_PTR (&allocated_blocks.data [block_index], BLOCK_TAG_CHECKING (tagged_block), tagged_block) != tagged_block)
1409 block = BLOCK_UNTAG (tagged_block);
1410 block_state = block->state;
1412 if (!sweep_in_progress ()) {
1413 SGEN_ASSERT (0, block_state != BLOCK_STATE_SWEEPING && block_state != BLOCK_STATE_CHECKING, "Invalid block state.");
1415 SGEN_ASSERT (0, block_state != BLOCK_STATE_NEED_SWEEPING, "Invalid block state.");
1418 switch (block_state) {
1419 case BLOCK_STATE_SWEPT:
1420 case BLOCK_STATE_NEED_SWEEPING:
1421 case BLOCK_STATE_SWEEPING:
1423 case BLOCK_STATE_MARKING:
1425 case BLOCK_STATE_CHECKING:
1426 SGEN_ASSERT (0, FALSE, "We set the CHECKING bit - how can the stage be CHECKING?");
1429 SGEN_ASSERT (0, FALSE, "Illegal block state");
1433 SGEN_ASSERT (0, block->state == BLOCK_STATE_MARKING, "When we sweep all blocks must start out marking.");
1434 set_block_state (block, BLOCK_STATE_CHECKING, BLOCK_STATE_MARKING);
1437 *have_checked = TRUE;
1439 block->has_pinned = block->pinned;
1441 block->is_to_space = FALSE;
1443 count = MS_BLOCK_FREE / block->obj_size;
1445 if (block->cardtable_mod_union) {
1446 sgen_free_internal_dynamic (block->cardtable_mod_union, CARDS_PER_BLOCK, INTERNAL_MEM_CARDTABLE_MOD_UNION);
1447 block->cardtable_mod_union = NULL;
1450 /* Count marked objects in the block */
1451 for (i = 0; i < MS_NUM_MARK_WORDS; ++i)
1452 nused += bitcount (block->mark_words [i]);
1460 int obj_size_index = block->obj_size_index;
1461 gboolean has_pinned = block->has_pinned;
1463 set_block_state (block, BLOCK_STATE_NEED_SWEEPING, BLOCK_STATE_CHECKING);
1466 * FIXME: Go straight to SWEPT if there are no free slots. We need
1467 * to set the free slot list to NULL, though, and maybe update some
1471 sweep_block (block);
1474 ++sweep_num_blocks [obj_size_index];
1475 sweep_slots_used [obj_size_index] += nused;
1476 sweep_slots_available [obj_size_index] += count;
1480 * If there are free slots in the block, add
1481 * the block to the corresponding free list.
1484 MSBlockInfo * volatile *free_blocks = FREE_BLOCKS (block->pinned, block->has_references);
1485 int index = MS_BLOCK_OBJ_SIZE_INDEX (block->obj_size);
1488 SGEN_ASSERT (0, block->free_list, "How do we not have a free list when there are free slots?");
1490 add_free_block (free_blocks, index, block);
1493 /* FIXME: Do we need the heap boundaries while we do nursery collections? */
1494 update_heap_boundaries_for_block (block);
1497 * Blocks without live objects are removed from the
1498 * block list and freed.
1500 SGEN_ASSERT (0, block_index < allocated_blocks.next_slot, "How did the number of blocks shrink?");
1501 SGEN_ASSERT (0, allocated_blocks.data [block_index] == BLOCK_TAG_CHECKING (tagged_block), "How did the block move?");
1503 binary_protocol_empty (MS_BLOCK_OBJ (block, 0), (char*)MS_BLOCK_OBJ (block, count) - (char*)MS_BLOCK_OBJ (block, 0));
1504 ms_free_block (block);
1506 SGEN_ATOMIC_ADD_P (num_major_sections, -1);
1508 tagged_block = NULL;
1512 allocated_blocks.data [block_index] = tagged_block;
1513 return !!tagged_block;
1516 static mono_native_thread_return_t
1517 sweep_loop_thread_func (void *dummy)
1520 int num_blocks = num_major_sections_before_sweep;
1521 int small_id = mono_thread_info_register_small_id ();
1523 SGEN_ASSERT (0, sweep_in_progress (), "Sweep thread called with wrong state");
1524 SGEN_ASSERT (0, num_blocks <= allocated_blocks.next_slot, "How did we lose blocks?");
1527 * We traverse the block array from high to low. Nursery collections will have to
1528 * cooperate with the sweep thread to finish sweeping, and they will traverse from
1529 * low to high, to avoid constantly colliding on the same blocks.
1531 for (block_index = num_blocks - 1; block_index >= 0; --block_index) {
1532 gboolean have_checked;
1535 * The block might have been freed by another thread doing some checking
1538 if (!ensure_block_is_checked_for_sweeping (block_index, TRUE, &have_checked))
1539 ++num_major_sections_freed_in_sweep;
1542 while (!try_set_sweep_state (SWEEP_STATE_COMPACTING, SWEEP_STATE_SWEEPING))
1545 /* FIXME: remove this iteration */
1546 for (block_index = num_blocks; block_index < allocated_blocks.next_slot; ++block_index) {
1547 MSBlockInfo *block = BLOCK_UNTAG (allocated_blocks.data [block_index]);
1548 SGEN_ASSERT (0, block && block->state == BLOCK_STATE_SWEPT, "How did a new block to be swept get added while swept?");
1551 sgen_pointer_queue_remove_nulls (&allocated_blocks);
1555 mono_thread_small_id_free (small_id);
1563 mword total_evacuate_heap = 0;
1564 mword total_evacuate_saved = 0;
1567 for (i = 0; i < num_block_obj_sizes; ++i) {
1568 float usage = (float)sweep_slots_used [i] / (float)sweep_slots_available [i];
1569 if (sweep_num_blocks [i] > 5 && usage < evacuation_threshold) {
1570 evacuate_block_obj_sizes [i] = TRUE;
1572 g_print ("slot size %d - %d of %d used\n",
1573 block_obj_sizes [i], slots_used [i], slots_available [i]);
1576 evacuate_block_obj_sizes [i] = FALSE;
1579 mword total_bytes = block_obj_sizes [i] * sweep_slots_available [i];
1580 total_evacuate_heap += total_bytes;
1581 if (evacuate_block_obj_sizes [i])
1582 total_evacuate_saved += total_bytes - block_obj_sizes [i] * sweep_slots_used [i];
1586 want_evacuation = (float)total_evacuate_saved / (float)total_evacuate_heap > (1 - concurrent_evacuation_threshold);
1588 set_sweep_state (SWEEP_STATE_SWEPT, SWEEP_STATE_COMPACTING);
1591 static MonoNativeThreadId sweep_loop_thread;
1596 set_sweep_state (SWEEP_STATE_SWEEPING, SWEEP_STATE_NEED_SWEEPING);
1600 SGEN_ASSERT (0, num_major_sections == allocated_blocks.next_slot, "We don't know how many blocks we have?");
1602 num_major_sections_before_sweep = num_major_sections;
1603 num_major_sections_freed_in_sweep = 0;
1605 if (TRUE /*concurrent_mark*/) {
1607 * FIXME: We can't create a thread while the world is stopped because it
1608 * might deadlock. `finalizer-wait.exe` exposes this.
1610 mono_native_thread_create (&sweep_loop_thread, sweep_loop_thread_func, NULL);
1612 sweep_loop_thread_func (NULL);
1617 major_have_swept (void)
1619 return sweep_state == SWEEP_STATE_SWEPT;
1622 static int count_pinned_ref;
1623 static int count_pinned_nonref;
1624 static int count_nonpinned_ref;
1625 static int count_nonpinned_nonref;
1628 count_nonpinned_callback (char *obj, size_t size, void *data)
1630 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
1632 if (vtable->klass->has_references)
1633 ++count_nonpinned_ref;
1635 ++count_nonpinned_nonref;
1639 count_pinned_callback (char *obj, size_t size, void *data)
1641 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
1643 if (vtable->klass->has_references)
1646 ++count_pinned_nonref;
1649 static G_GNUC_UNUSED void
1650 count_ref_nonref_objs (void)
1654 count_pinned_ref = 0;
1655 count_pinned_nonref = 0;
1656 count_nonpinned_ref = 0;
1657 count_nonpinned_nonref = 0;
1659 major_iterate_objects (ITERATE_OBJECTS_SWEEP_NON_PINNED, count_nonpinned_callback, NULL);
1660 major_iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, count_pinned_callback, NULL);
1662 total = count_pinned_nonref + count_nonpinned_nonref + count_pinned_ref + count_nonpinned_ref;
1664 g_print ("ref: %d pinned %d non-pinned non-ref: %d pinned %d non-pinned -- %.1f\n",
1665 count_pinned_ref, count_nonpinned_ref,
1666 count_pinned_nonref, count_nonpinned_nonref,
1667 (count_pinned_nonref + count_nonpinned_nonref) * 100.0 / total);
1671 ms_calculate_block_obj_sizes (double factor, int *arr)
1678 * Have every possible slot size starting with the minimal
1679 * object size up to and including four times that size. Then
1680 * proceed by increasing geometrically with the given factor.
1683 for (int size = sizeof (MonoObject); size <= 4 * sizeof (MonoObject); size += SGEN_ALLOC_ALIGN) {
1685 arr [num_sizes] = size;
1689 target_size = (double)last_size;
1692 int target_count = (int)floor (MS_BLOCK_FREE / target_size);
1693 int size = MIN ((MS_BLOCK_FREE / target_count) & ~(SGEN_ALLOC_ALIGN - 1), SGEN_MAX_SMALL_OBJ_SIZE);
1695 if (size != last_size) {
1697 arr [num_sizes] = size;
1702 target_size *= factor;
1703 } while (last_size < SGEN_MAX_SMALL_OBJ_SIZE);
1708 /* only valid during minor collections */
1709 static mword old_num_major_sections;
1712 major_start_nursery_collection (void)
1714 #ifdef MARKSWEEP_CONSISTENCY_CHECK
1715 consistency_check ();
1718 old_num_major_sections = num_major_sections;
1720 if (sweep_in_progress ())
1721 g_print ("sweeping during nursery collection\n");
1725 major_finish_nursery_collection (void)
1727 #ifdef MARKSWEEP_CONSISTENCY_CHECK
1728 consistency_check ();
1733 major_start_major_collection (void)
1738 SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Major collection on unswept heap");
1741 * Clear the free lists for block sizes where we do evacuation. For those block
1742 * sizes we will have to allocate new blocks.
1744 for (i = 0; i < num_block_obj_sizes; ++i) {
1745 if (!evacuate_block_obj_sizes [i])
1748 free_block_lists [0][i] = NULL;
1749 free_block_lists [MS_BLOCK_FLAG_REFS][i] = NULL;
1753 MONO_GC_SWEEP_BEGIN (GENERATION_OLD, TRUE);
1755 /* Sweep all unswept blocks and set them to MARKING */
1756 FOREACH_BLOCK_NO_LOCK (block) {
1758 sweep_block (block);
1759 SGEN_ASSERT (0, block->state == BLOCK_STATE_SWEPT, "All blocks must be swept when we're pinning.");
1760 set_block_state (block, BLOCK_STATE_MARKING, BLOCK_STATE_SWEPT);
1761 } END_FOREACH_BLOCK_NO_LOCK;
1764 MONO_GC_SWEEP_END (GENERATION_OLD, TRUE);
1766 set_sweep_state (SWEEP_STATE_NEED_SWEEPING, SWEEP_STATE_SWEPT);
1770 major_finish_major_collection (ScannedObjectCounts *counts)
1772 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
1773 if (binary_protocol_is_enabled ()) {
1774 counts->num_scanned_objects = scanned_objects_list.next_slot;
1776 sgen_pointer_queue_sort_uniq (&scanned_objects_list);
1777 counts->num_unique_scanned_objects = scanned_objects_list.next_slot;
1779 sgen_pointer_queue_clear (&scanned_objects_list);
1784 #if SIZEOF_VOID_P != 8
1786 compare_pointers (const void *va, const void *vb) {
1787 char *a = *(char**)va, *b = *(char**)vb;
1797 * This is called with sweep completed and the world stopped.
1800 major_free_swept_blocks (void)
1802 /* FIXME: use something sensible here. */
1803 size_t section_reserve = DEFAULT_NURSERY_SIZE * 2 / MS_BLOCK_SIZE;
1805 SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Sweeping must have finished before freeing blocks");
1807 #if SIZEOF_VOID_P != 8
1809 int i, num_empty_blocks_orig, num_blocks, arr_length;
1811 void **empty_block_arr;
1812 void **rebuild_next;
1816 * sgen_free_os_memory () asserts in mono_vfree () because windows doesn't like freeing the middle of
1817 * a VirtualAlloc ()-ed block.
1822 if (num_empty_blocks <= section_reserve)
1824 SGEN_ASSERT (0, num_empty_blocks > 0, "section reserve can't be negative");
1826 num_empty_blocks_orig = num_empty_blocks;
1827 empty_block_arr = (void**)sgen_alloc_internal_dynamic (sizeof (void*) * num_empty_blocks_orig,
1828 INTERNAL_MEM_MS_BLOCK_INFO_SORT, FALSE);
1829 if (!empty_block_arr)
1833 for (block = empty_blocks; block; block = *(void**)block)
1834 empty_block_arr [i++] = block;
1835 SGEN_ASSERT (0, i == num_empty_blocks, "empty block count wrong");
1837 sgen_qsort (empty_block_arr, num_empty_blocks, sizeof (void*), compare_pointers);
1840 * We iterate over the free blocks, trying to find MS_BLOCK_ALLOC_NUM
1841 * contiguous ones. If we do, we free them. If that's not enough to get to
1842 * section_reserve, we halve the number of contiguous blocks we're looking
1843 * for and have another go, until we're done with looking for pairs of
1844 * blocks, at which point we give up and go to the fallback.
1846 arr_length = num_empty_blocks_orig;
1847 num_blocks = MS_BLOCK_ALLOC_NUM;
1848 while (num_empty_blocks > section_reserve && num_blocks > 1) {
1853 for (i = 0; i < arr_length; ++i) {
1855 void *block = empty_block_arr [i];
1856 SGEN_ASSERT (0, block, "we're not shifting correctly");
1858 empty_block_arr [dest] = block;
1860 * This is not strictly necessary, but we're
1863 empty_block_arr [i] = NULL;
1872 SGEN_ASSERT (0, first >= 0 && d > first, "algorithm is wrong");
1874 if ((char*)block != ((char*)empty_block_arr [d-1]) + MS_BLOCK_SIZE) {
1879 if (d + 1 - first == num_blocks) {
1881 * We found num_blocks contiguous blocks. Free them
1882 * and null their array entries. As an optimization
1883 * we could, instead of nulling the entries, shift
1884 * the following entries over to the left, while
1888 sgen_free_os_memory (empty_block_arr [first], MS_BLOCK_SIZE * num_blocks, SGEN_ALLOC_HEAP);
1889 for (j = first; j <= d; ++j)
1890 empty_block_arr [j] = NULL;
1894 num_empty_blocks -= num_blocks;
1896 stat_major_blocks_freed += num_blocks;
1897 if (num_blocks == MS_BLOCK_ALLOC_NUM)
1898 stat_major_blocks_freed_ideal += num_blocks;
1900 stat_major_blocks_freed_less_ideal += num_blocks;
1905 SGEN_ASSERT (0, dest <= i && dest <= arr_length, "array length is off");
1907 SGEN_ASSERT (0, arr_length == num_empty_blocks, "array length is off");
1912 /* rebuild empty_blocks free list */
1913 rebuild_next = (void**)&empty_blocks;
1914 for (i = 0; i < arr_length; ++i) {
1915 void *block = empty_block_arr [i];
1916 SGEN_ASSERT (0, block, "we're missing blocks");
1917 *rebuild_next = block;
1918 rebuild_next = (void**)block;
1920 *rebuild_next = NULL;
1923 sgen_free_internal_dynamic (empty_block_arr, sizeof (void*) * num_empty_blocks_orig, INTERNAL_MEM_MS_BLOCK_INFO_SORT);
1926 SGEN_ASSERT (0, num_empty_blocks >= 0, "we freed more blocks than we had in the first place?");
1930 * This is our threshold. If there's not more empty than used blocks, we won't
1931 * release uncontiguous blocks, in fear of fragmenting the address space.
1933 if (num_empty_blocks <= num_major_sections)
1937 while (num_empty_blocks > section_reserve) {
1938 void *next = *(void**)empty_blocks;
1939 sgen_free_os_memory (empty_blocks, MS_BLOCK_SIZE, SGEN_ALLOC_HEAP);
1940 empty_blocks = next;
1942 * Needs not be atomic because this is running
1947 ++stat_major_blocks_freed;
1948 #if SIZEOF_VOID_P != 8
1949 ++stat_major_blocks_freed_individual;
1955 major_pin_objects (SgenGrayQueue *queue)
1959 FOREACH_BLOCK_NO_LOCK (block) {
1960 size_t first_entry, last_entry;
1961 SGEN_ASSERT (0, block_is_swept_or_marking (block), "All blocks must be swept when we're pinning.");
1962 sgen_find_optimized_pin_queue_area (MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SKIP, MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE,
1963 &first_entry, &last_entry);
1964 mark_pinned_objects_in_block (block, first_entry, last_entry, queue);
1965 } END_FOREACH_BLOCK_NO_LOCK;
1969 major_init_to_space (void)
1974 major_report_pinned_memory_usage (void)
1976 g_assert_not_reached ();
1980 major_get_used_size (void)
1985 FOREACH_BLOCK_NO_LOCK (block) {
1986 int count = MS_BLOCK_FREE / block->obj_size;
1988 size += count * block->obj_size;
1989 for (iter = block->free_list; iter; iter = (void**)*iter)
1990 size -= block->obj_size;
1991 } END_FOREACH_BLOCK_NO_LOCK;
1996 /* FIXME: return number of bytes, not of sections */
1998 get_num_major_sections (void)
2000 return num_major_sections;
2004 * Returns the number of bytes in blocks that were present when the last sweep was
2005 * initiated, and were not freed during the sweep. They are the basis for calculating the
2009 get_bytes_survived_last_sweep (void)
2011 SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Can only query unswept sections after sweep");
2012 return (num_major_sections_before_sweep - num_major_sections_freed_in_sweep) * MS_BLOCK_SIZE;
2016 major_handle_gc_param (const char *opt)
2018 if (g_str_has_prefix (opt, "evacuation-threshold=")) {
2019 const char *arg = strchr (opt, '=') + 1;
2020 int percentage = atoi (arg);
2021 if (percentage < 0 || percentage > 100) {
2022 fprintf (stderr, "evacuation-threshold must be an integer in the range 0-100.\n");
2025 evacuation_threshold = (float)percentage / 100.0f;
2027 } else if (!strcmp (opt, "lazy-sweep")) {
2030 } else if (!strcmp (opt, "no-lazy-sweep")) {
2039 major_print_gc_param_usage (void)
2043 " evacuation-threshold=P (where P is a percentage, an integer in 0-100)\n"
2044 " (no-)lazy-sweep\n"
2049 * This callback is used to clear cards, move cards to the shadow table and do counting.
2052 major_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
2055 gboolean has_references;
2057 major_finish_sweep_checking ();
2058 FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK (block, has_references) {
2060 callback ((mword)MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE);
2061 } END_FOREACH_BLOCK_NO_LOCK;
2064 #ifdef HEAVY_STATISTICS
2065 extern guint64 marked_cards;
2066 extern guint64 scanned_cards;
2067 extern guint64 scanned_objects;
2068 extern guint64 remarked_cards;
2071 #define CARD_WORDS_PER_BLOCK (CARDS_PER_BLOCK / SIZEOF_VOID_P)
2073 * MS blocks are 16K aligned.
2074 * Cardtables are 4K aligned, at least.
2075 * This means that the cardtable of a given block is 32 bytes aligned.
2078 initial_skip_card (guint8 *card_data)
2080 mword *cards = (mword*)card_data;
2083 for (i = 0; i < CARD_WORDS_PER_BLOCK; ++i) {
2089 if (i == CARD_WORDS_PER_BLOCK)
2090 return card_data + CARDS_PER_BLOCK;
2092 #if defined(__i386__) && defined(__GNUC__)
2093 return card_data + i * 4 + (__builtin_ffs (card) - 1) / 8;
2094 #elif defined(__x86_64__) && defined(__GNUC__)
2095 return card_data + i * 8 + (__builtin_ffsll (card) - 1) / 8;
2096 #elif defined(__s390x__) && defined(__GNUC__)
2097 return card_data + i * 8 + (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
2099 for (i = i * SIZEOF_VOID_P; i < CARDS_PER_BLOCK; ++i) {
2101 return &card_data [i];
2107 #define MS_BLOCK_OBJ_INDEX_FAST(o,b,os) (((char*)(o) - ((b) + MS_BLOCK_SKIP)) / (os))
2108 #define MS_BLOCK_OBJ_FAST(b,os,i) ((b) + MS_BLOCK_SKIP + (os) * (i))
2109 #define MS_OBJ_ALLOCED_FAST(o,b) (*(void**)(o) && (*(char**)(o) < (b) || *(char**)(o) >= (b) + MS_BLOCK_SIZE))
2112 card_offset (char *obj, char *base)
2114 return (obj - base) >> CARD_BITS;
2118 scan_card_table_for_block (MSBlockInfo *block, gboolean mod_union, ScanObjectFunc scan_func, SgenGrayQueue *queue)
2120 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
2121 guint8 cards_copy [CARDS_PER_BLOCK];
2123 gboolean small_objects;
2126 guint8 *card_data, *card_base;
2127 guint8 *card_data_end;
2128 char *scan_front = NULL;
2130 block_obj_size = block->obj_size;
2131 small_objects = block_obj_size < CARD_SIZE_IN_BYTES;
2133 block_start = MS_BLOCK_FOR_BLOCK_INFO (block);
2136 * This is safe in face of card aliasing for the following reason:
2138 * Major blocks are 16k aligned, or 32 cards aligned.
2139 * Cards aliasing happens in powers of two, so as long as major blocks are aligned to their
2140 * sizes, they won't overflow the cardtable overlap modulus.
2143 card_data = card_base = block->cardtable_mod_union;
2145 * This happens when the nursery collection that precedes finishing
2146 * the concurrent collection allocates new major blocks.
2151 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2152 card_data = card_base = sgen_card_table_get_card_scan_address ((mword)block_start);
2154 if (!sgen_card_table_get_card_data (cards_copy, (mword)block_start, CARDS_PER_BLOCK))
2156 card_data = card_base = cards_copy;
2159 card_data_end = card_data + CARDS_PER_BLOCK;
2161 card_data += MS_BLOCK_SKIP >> CARD_BITS;
2163 card_data = initial_skip_card (card_data);
2164 while (card_data < card_data_end) {
2165 size_t card_index, first_object_index;
2168 char *first_obj, *obj;
2170 HEAVY_STAT (++scanned_cards);
2177 card_index = card_data - card_base;
2178 start = (char*)(block_start + card_index * CARD_SIZE_IN_BYTES);
2179 end = start + CARD_SIZE_IN_BYTES;
2181 if (!block_is_swept_or_marking (block))
2182 sweep_block (block);
2184 HEAVY_STAT (++marked_cards);
2187 sgen_card_table_prepare_card_for_scanning (card_data);
2190 * If the card we're looking at starts at or in the block header, we
2191 * must start at the first object in the block, without calculating
2192 * the index of the object we're hypothetically starting at, because
2193 * it would be negative.
2195 if (card_index <= (MS_BLOCK_SKIP >> CARD_BITS))
2196 first_object_index = 0;
2198 first_object_index = MS_BLOCK_OBJ_INDEX_FAST (start, block_start, block_obj_size);
2200 obj = first_obj = (char*)MS_BLOCK_OBJ_FAST (block_start, block_obj_size, first_object_index);
2202 binary_protocol_card_scan (first_obj, end - first_obj);
2205 if (obj < scan_front || !MS_OBJ_ALLOCED_FAST (obj, block_start))
2209 /* FIXME: do this more efficiently */
2211 MS_CALC_MARK_BIT (w, b, obj);
2212 if (!MS_MARK_BIT (block, w, b))
2216 if (small_objects) {
2217 HEAVY_STAT (++scanned_objects);
2218 scan_func (obj, sgen_obj_get_descriptor (obj), queue);
2220 size_t offset = card_offset (obj, block_start);
2221 sgen_cardtable_scan_object (obj, block_obj_size, card_base + offset, mod_union, queue);
2224 obj += block_obj_size;
2225 g_assert (scan_front <= obj);
2229 HEAVY_STAT (if (*card_data) ++remarked_cards);
2234 card_data = card_base + card_offset (obj, block_start);
2239 major_scan_card_table (gboolean mod_union, SgenGrayQueue *queue)
2241 ScanObjectFunc scan_func = sgen_get_current_object_ops ()->scan_object;
2243 gboolean has_references;
2245 if (!concurrent_mark)
2246 g_assert (!mod_union);
2248 major_finish_sweep_checking ();
2249 FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK (block, has_references) {
2250 #ifdef PREFETCH_CARDS
2251 int prefetch_index = __index + 6;
2252 if (prefetch_index < allocated_blocks.next_slot) {
2253 MSBlockInfo *prefetch_block = BLOCK_UNTAG (allocated_blocks.data [prefetch_index]);
2254 guint8 *prefetch_cards = sgen_card_table_get_card_scan_address ((mword)MS_BLOCK_FOR_BLOCK_INFO (prefetch_block));
2255 PREFETCH_READ (prefetch_block);
2256 PREFETCH_WRITE (prefetch_cards);
2257 PREFETCH_WRITE (prefetch_cards + 32);
2261 if (!has_references)
2264 scan_card_table_for_block (block, mod_union, scan_func, queue);
2265 } END_FOREACH_BLOCK_NO_LOCK;
2269 major_count_cards (long long *num_total_cards, long long *num_marked_cards)
2272 gboolean has_references;
2273 long long total_cards = 0;
2274 long long marked_cards = 0;
2276 if (sweep_in_progress ()) {
2277 *num_total_cards = -1;
2278 *num_marked_cards = -1;
2282 FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK (block, has_references) {
2283 guint8 *cards = sgen_card_table_get_card_scan_address ((mword) MS_BLOCK_FOR_BLOCK_INFO (block));
2286 if (!has_references)
2289 total_cards += CARDS_PER_BLOCK;
2290 for (i = 0; i < CARDS_PER_BLOCK; ++i) {
2294 } END_FOREACH_BLOCK_NO_LOCK;
2296 *num_total_cards = total_cards;
2297 *num_marked_cards = marked_cards;
2301 update_cardtable_mod_union (void)
2305 FOREACH_BLOCK_NO_LOCK (block) {
2308 block->cardtable_mod_union = sgen_card_table_update_mod_union (block->cardtable_mod_union,
2309 MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE, &num_cards);
2311 SGEN_ASSERT (0, num_cards == CARDS_PER_BLOCK, "Number of cards calculation is wrong");
2312 } END_FOREACH_BLOCK_NO_LOCK;
2316 major_get_cardtable_mod_union_for_object (char *obj)
2318 MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
2319 size_t offset = card_offset (obj, (char*)sgen_card_table_align_pointer (MS_BLOCK_FOR_BLOCK_INFO (block)));
2320 return &block->cardtable_mod_union [offset];
2323 #undef pthread_create
2326 post_param_init (SgenMajorCollector *collector)
2328 collector->sweeps_lazily = lazy_sweep;
2332 sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurrent)
2336 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_MS_BLOCK_INFO, sizeof (MSBlockInfo));
2338 num_block_obj_sizes = ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, NULL);
2339 block_obj_sizes = sgen_alloc_internal_dynamic (sizeof (int) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2340 ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, block_obj_sizes);
2342 evacuate_block_obj_sizes = sgen_alloc_internal_dynamic (sizeof (gboolean) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2343 for (i = 0; i < num_block_obj_sizes; ++i)
2344 evacuate_block_obj_sizes [i] = FALSE;
2346 sweep_slots_available = sgen_alloc_internal_dynamic (sizeof (size_t) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2347 sweep_slots_used = sgen_alloc_internal_dynamic (sizeof (size_t) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2348 sweep_num_blocks = sgen_alloc_internal_dynamic (sizeof (size_t) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2353 g_print ("block object sizes:\n");
2354 for (i = 0; i < num_block_obj_sizes; ++i)
2355 g_print ("%d\n", block_obj_sizes [i]);
2359 for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i)
2360 free_block_lists [i] = sgen_alloc_internal_dynamic (sizeof (MSBlockInfo*) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2362 for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES; ++i)
2363 fast_block_obj_size_indexes [i] = ms_find_block_obj_size_index (i * 8);
2364 for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES * 8; ++i)
2365 g_assert (MS_BLOCK_OBJ_SIZE_INDEX (i) == ms_find_block_obj_size_index (i));
2367 mono_counters_register ("# major blocks allocated", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_alloced);
2368 mono_counters_register ("# major blocks freed", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed);
2369 mono_counters_register ("# major blocks lazy swept", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_lazy_swept);
2370 mono_counters_register ("# major objects evacuated", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_objects_evacuated);
2371 #if SIZEOF_VOID_P != 8
2372 mono_counters_register ("# major blocks freed ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_ideal);
2373 mono_counters_register ("# major blocks freed less ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_less_ideal);
2374 mono_counters_register ("# major blocks freed individually", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_individual);
2375 mono_counters_register ("# major blocks allocated less ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_alloced_less_ideal);
2378 collector->section_size = MAJOR_SECTION_SIZE;
2380 concurrent_mark = is_concurrent;
2381 if (is_concurrent) {
2382 collector->is_concurrent = TRUE;
2383 collector->want_synchronous_collection = &want_evacuation;
2385 collector->is_concurrent = FALSE;
2386 collector->want_synchronous_collection = NULL;
2388 collector->get_and_reset_num_major_objects_marked = major_get_and_reset_num_major_objects_marked;
2389 collector->supports_cardtable = TRUE;
2391 collector->alloc_heap = major_alloc_heap;
2392 collector->is_object_live = major_is_object_live;
2393 collector->alloc_small_pinned_obj = major_alloc_small_pinned_obj;
2394 collector->alloc_degraded = major_alloc_degraded;
2396 collector->alloc_object = major_alloc_object;
2397 collector->free_pinned_object = free_pinned_object;
2398 collector->iterate_objects = major_iterate_objects;
2399 collector->free_non_pinned_object = major_free_non_pinned_object;
2400 collector->pin_objects = major_pin_objects;
2401 collector->pin_major_object = pin_major_object;
2402 collector->scan_card_table = major_scan_card_table;
2403 collector->iterate_live_block_ranges = (void*)(void*) major_iterate_live_block_ranges;
2404 if (is_concurrent) {
2405 collector->update_cardtable_mod_union = update_cardtable_mod_union;
2406 collector->get_cardtable_mod_union_for_object = major_get_cardtable_mod_union_for_object;
2408 collector->init_to_space = major_init_to_space;
2409 collector->sweep = major_sweep;
2410 collector->have_swept = major_have_swept;
2411 collector->finish_sweeping = major_finish_sweep_checking;
2412 collector->free_swept_blocks = major_free_swept_blocks;
2413 collector->check_scan_starts = major_check_scan_starts;
2414 collector->dump_heap = major_dump_heap;
2415 collector->get_used_size = major_get_used_size;
2416 collector->start_nursery_collection = major_start_nursery_collection;
2417 collector->finish_nursery_collection = major_finish_nursery_collection;
2418 collector->start_major_collection = major_start_major_collection;
2419 collector->finish_major_collection = major_finish_major_collection;
2420 collector->ptr_is_in_non_pinned_space = major_ptr_is_in_non_pinned_space;
2421 collector->obj_is_from_pinned_alloc = obj_is_from_pinned_alloc;
2422 collector->report_pinned_memory_usage = major_report_pinned_memory_usage;
2423 collector->get_num_major_sections = get_num_major_sections;
2424 collector->get_bytes_survived_last_sweep = get_bytes_survived_last_sweep;
2425 collector->handle_gc_param = major_handle_gc_param;
2426 collector->print_gc_param_usage = major_print_gc_param_usage;
2427 collector->post_param_init = post_param_init;
2428 collector->is_valid_object = major_is_valid_object;
2429 collector->describe_pointer = major_describe_pointer;
2430 collector->count_cards = major_count_cards;
2432 collector->major_ops.copy_or_mark_object = major_copy_or_mark_object_canonical;
2433 collector->major_ops.scan_object = major_scan_object_with_evacuation;
2434 if (is_concurrent) {
2435 collector->major_concurrent_ops.copy_or_mark_object = major_copy_or_mark_object_concurrent_canonical;
2436 collector->major_concurrent_ops.scan_object = major_scan_object_no_mark_concurrent;
2437 collector->major_concurrent_ops.scan_vtype = major_scan_vtype_concurrent;
2440 #if !defined (FIXED_HEAP) && !defined (SGEN_PARALLEL_MARK)
2442 collector->drain_gray_stack = drain_gray_stack;
2444 #ifdef HEAVY_STATISTICS
2445 mono_counters_register ("Optimized copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy);
2446 mono_counters_register ("Optimized copy nursery", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_nursery);
2447 mono_counters_register ("Optimized copy nursery forwarded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_nursery_forwarded);
2448 mono_counters_register ("Optimized copy nursery pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_nursery_pinned);
2449 mono_counters_register ("Optimized copy major", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major);
2450 mono_counters_register ("Optimized copy major small fast", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_small_fast);
2451 mono_counters_register ("Optimized copy major small slow", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_small_slow);
2452 mono_counters_register ("Optimized copy major large", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_large);
2453 mono_counters_register ("Optimized major scan", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_major_scan);
2454 mono_counters_register ("Optimized major scan no refs", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_major_scan_no_refs);
2456 mono_counters_register ("Gray stack drain loops", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_drain_loops);
2457 mono_counters_register ("Gray stack prefetch fills", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_drain_prefetch_fills);
2458 mono_counters_register ("Gray stack prefetch failures", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_drain_prefetch_fill_failures);
2462 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
2463 mono_mutex_init (&scanned_objects_list_lock);
2466 SGEN_ASSERT (0, SGEN_MAX_SMALL_OBJ_SIZE <= MS_BLOCK_FREE / 2, "MAX_SMALL_OBJ_SIZE must be at most MS_BLOCK_FREE / 2");
2468 /*cardtable requires major pages to be 8 cards aligned*/
2469 g_assert ((MS_BLOCK_SIZE % (8 * CARD_SIZE_IN_BYTES)) == 0);
2473 sgen_marksweep_init (SgenMajorCollector *collector)
2475 sgen_marksweep_init_internal (collector, FALSE);
2479 sgen_marksweep_conc_init (SgenMajorCollector *collector)
2481 sgen_marksweep_init_internal (collector, TRUE);