2 * sgen-major-copying.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
15 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
18 * Permission is hereby granted to use or copy this program
19 * for any purpose, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
25 * Copyright 2001-2003 Ximian, Inc
26 * Copyright 2003-2010 Novell, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining
29 * a copy of this software and associated documentation files (the
30 * "Software"), to deal in the Software without restriction, including
31 * without limitation the rights to use, copy, modify, merge, publish,
32 * distribute, sublicense, and/or sell copies of the Software, and to
33 * permit persons to whom the Software is furnished to do so, subject to
34 * the following conditions:
36 * The above copyright notice and this permission notice shall be
37 * included in all copies or substantial portions of the Software.
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 #include "utils/mono-counters.h"
54 #include "metadata/gc-internal.h"
55 #include "metadata/sgen-gc.h"
56 #include "metadata/sgen-protocol.h"
57 #include "metadata/mono-gc.h"
58 #include "metadata/object-internals.h"
59 #include "metadata/profiler-private.h"
61 #define MAJOR_SECTION_SIZE SGEN_PINNED_CHUNK_SIZE
62 #define BLOCK_FOR_OBJECT(o) SGEN_PINNED_CHUNK_FOR_PTR ((o))
63 #define MAJOR_SECTION_FOR_OBJECT(o) ((GCMemSection*)BLOCK_FOR_OBJECT ((o)))
65 #define MAJOR_OBJ_IS_IN_TO_SPACE(o) (MAJOR_SECTION_FOR_OBJECT ((o))->is_to_space)
67 static int num_major_sections = 0;
69 static GCMemSection *section_list = NULL;
71 static SgenPinnedAllocator pinned_allocator;
73 static gboolean have_swept;
76 * used when moving the objects
78 static char *to_space_bumper = NULL;
79 static char *to_space_top = NULL;
80 static GCMemSection *to_space_section = NULL;
82 /* we get this at init */
83 static int nursery_bits;
84 static char *nursery_start;
85 static char *nursery_end;
87 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), nursery_bits, nursery_start, nursery_end))
89 #ifdef HEAVY_STATISTICS
90 static long stat_major_copy_object_failed_forwarded = 0;
91 static long stat_major_copy_object_failed_pinned = 0;
92 static long stat_major_copy_object_failed_large_pinned = 0;
93 static long stat_major_copy_object_failed_to_space = 0;
97 major_alloc_heap (mword nursery_size, mword nursery_align, int the_nursery_bits)
100 nursery_start = sgen_alloc_os_memory_aligned (nursery_size, nursery_align, TRUE);
102 nursery_start = sgen_alloc_os_memory (nursery_size, TRUE);
104 nursery_end = nursery_start + nursery_size;
105 nursery_bits = the_nursery_bits;
107 return nursery_start;
111 obj_is_from_pinned_alloc (char *p)
113 return BLOCK_FOR_OBJECT (p)->role == MEMORY_ROLE_PINNED;
117 free_pinned_object (char *obj, size_t size)
119 sgen_free_pinned (&pinned_allocator, obj, size);
123 * Allocate a new section of memory to be used as old generation.
126 alloc_major_section (void)
128 GCMemSection *section;
131 section = sgen_alloc_os_memory_aligned (MAJOR_SECTION_SIZE, MAJOR_SECTION_SIZE, TRUE);
132 section->next_data = section->data = (char*)section + SGEN_SIZEOF_GC_MEM_SECTION;
133 g_assert (!((mword)section->data & 7));
134 section->size = MAJOR_SECTION_SIZE - SGEN_SIZEOF_GC_MEM_SECTION;
135 section->end_data = section->data + section->size;
136 sgen_update_heap_boundaries ((mword)section->data, (mword)section->end_data);
137 DEBUG (3, fprintf (gc_debug_file, "New major heap section: (%p-%p), total: %ld\n", section->data, section->end_data, mono_gc_get_heap_size ()));
138 scan_starts = (section->size + SGEN_SCAN_START_SIZE - 1) / SGEN_SCAN_START_SIZE;
139 section->scan_starts = sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
140 section->num_scan_start = scan_starts;
141 section->block.role = MEMORY_ROLE_GEN1;
142 section->is_to_space = TRUE;
144 /* add to the section list */
145 section->block.next = section_list;
146 section_list = section;
148 ++num_major_sections;
154 free_major_section (GCMemSection *section)
156 DEBUG (3, fprintf (gc_debug_file, "Freed major section %p (%p-%p)\n", section, section->data, section->end_data));
157 sgen_free_internal_dynamic (section->scan_starts,
158 (section->size + SGEN_SCAN_START_SIZE - 1) / SGEN_SCAN_START_SIZE * sizeof (char*), INTERNAL_MEM_SCAN_STARTS);
159 sgen_free_os_memory (section, MAJOR_SECTION_SIZE);
161 --num_major_sections;
165 new_to_space_section (void)
167 /* FIXME: if the current to_space_section is empty, we don't
168 have to allocate a new one */
170 to_space_section = alloc_major_section ();
171 to_space_bumper = to_space_section->next_data;
172 to_space_top = to_space_section->end_data;
176 to_space_set_next_data (void)
178 g_assert (to_space_bumper >= to_space_section->next_data && to_space_bumper <= to_space_section->end_data);
179 to_space_section->next_data = to_space_bumper;
183 to_space_expand (void)
185 if (to_space_section) {
186 g_assert (to_space_top == to_space_section->end_data);
187 to_space_set_next_data ();
190 new_to_space_section ();
194 major_alloc_object (int size, gboolean has_references)
196 char *dest = to_space_bumper;
197 /* Make sure we have enough space available */
198 if (dest + size > to_space_top) {
200 (dest) = to_space_bumper;
201 DEBUG (8, g_assert (dest + size <= to_space_top));
203 to_space_bumper += size;
204 DEBUG (8, g_assert (to_space_bumper <= to_space_top));
205 to_space_section->scan_starts [(dest - (char*)to_space_section->data)/SGEN_SCAN_START_SIZE] = dest;
210 unset_to_space (void)
212 /* between collections the to_space_bumper is invalidated
213 because degraded allocations might occur, so we set it to
214 NULL, just to make it explicit */
215 to_space_bumper = NULL;
217 /* don't unset to_space_section if we implement the FIXME in
218 new_to_space_section */
219 to_space_section = NULL;
223 major_is_object_live (char *obj)
228 if (ptr_in_nursery (obj))
231 objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj));
234 if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
238 if (obj_is_from_pinned_alloc (obj))
241 /* now we know it's in a major heap section */
242 return MAJOR_SECTION_FOR_OBJECT (obj)->is_to_space;
245 /* size is a multiple of ALLOC_ALIGN */
247 major_alloc_small_pinned_obj (size_t size, gboolean has_references)
249 return sgen_alloc_pinned (&pinned_allocator, size);
253 * size is already rounded up and we hold the GC lock.
256 major_alloc_degraded (MonoVTable *vtable, size_t size)
258 GCMemSection *section;
260 g_assert (size <= SGEN_MAX_SMALL_OBJ_SIZE);
261 HEAVY_STAT (++stat_objects_alloced_degraded);
262 HEAVY_STAT (stat_bytes_alloced_degraded += size);
263 for (section = section_list; section; section = section->block.next) {
264 if ((section->end_data - section->next_data) >= size) {
265 p = (void**)section->next_data;
270 section = alloc_major_section ();
271 section->is_to_space = FALSE;
272 /* FIXME: handle OOM */
273 p = (void**)section->next_data;
274 sgen_register_major_sections_alloced (1);
276 section->next_data += size;
277 DEBUG (3, fprintf (gc_debug_file, "Allocated (degraded) object %p, vtable: %p (%s), size: %zd in section %p\n", p, vtable, vtable->klass->name, size, section));
283 pin_major_object (char *obj, SgenGrayQueue *queue)
285 sgen_pin_object (obj, queue);
288 #include "sgen-major-copy-object.h"
291 major_copy_or_mark_object (void **obj_slot, SgenGrayQueue *queue)
294 char *obj = *obj_slot;
297 DEBUG (9, g_assert (current_collection_generation == GENERATION_OLD));
299 HEAVY_STAT (++stat_copy_object_called_major);
301 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
304 * obj must belong to one of:
309 * 4. a non-to-space section of the major heap
310 * 5. a to-space section of the major heap
312 * In addition, objects in 1, 2 and 4 might also be pinned.
313 * Objects in 1 and 4 might be forwarded.
315 * Before we can copy the object we must make sure that we are
316 * allowed to, i.e. that the object not pinned, not already
317 * forwarded, not in the nursery To Space and doesn't belong
318 * to the LOS, a pinned chunk, or a to-space section.
320 * We are usually called for to-space objects (5) when we have
321 * two remset entries for the same reference. The first entry
322 * copies the object and updates the reference and the second
323 * calls us with the updated reference that points into
324 * to-space. There might also be other circumstances where we
325 * get to-space objects.
328 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj))) {
329 DEBUG (9, g_assert (((MonoVTable*)SGEN_LOAD_VTABLE(obj))->gc_descr));
330 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
331 HEAVY_STAT (++stat_major_copy_object_failed_forwarded);
332 *obj_slot = forwarded;
335 if (SGEN_OBJECT_IS_PINNED (obj)) {
336 DEBUG (9, g_assert (((MonoVTable*)SGEN_LOAD_VTABLE(obj))->gc_descr));
337 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
338 HEAVY_STAT (++stat_major_copy_object_failed_pinned);
342 if (ptr_in_nursery (obj)) {
343 /* A To Space object is already on its final destination for the current collection. */
344 if (sgen_nursery_is_to_space (obj))
350 * At this point we know obj is not pinned, not forwarded and
351 * belongs to 2, 3, 4, or 5.
353 * LOS object (2) are simple, at least until we always follow
354 * the rule: if objsize > SGEN_MAX_SMALL_OBJ_SIZE, pin the
355 * object and return it. At the end of major collections, we
356 * walk the los list and if the object is pinned, it is
357 * marked, otherwise it can be freed.
359 * Pinned chunks (3) and major heap sections (4, 5) both
360 * reside in blocks, which are always aligned, so once we've
361 * eliminated LOS objects, we can just access the block and
362 * see whether it's a pinned chunk or a major heap section.
365 objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj));
367 if (G_UNLIKELY (objsize > SGEN_MAX_SMALL_OBJ_SIZE || obj_is_from_pinned_alloc (obj))) {
368 if (SGEN_OBJECT_IS_PINNED (obj))
370 DEBUG (9, fprintf (gc_debug_file, " (marked LOS/Pinned %p (%s), size: %zd)\n", obj, sgen_safe_name (obj), objsize));
371 binary_protocol_pin (obj, (gpointer)SGEN_LOAD_VTABLE (obj), sgen_safe_object_get_size ((MonoObject*)obj));
372 SGEN_PIN_OBJECT (obj);
373 GRAY_OBJECT_ENQUEUE (queue, obj);
374 HEAVY_STAT (++stat_major_copy_object_failed_large_pinned);
379 * Now we know the object is in a major heap section. All we
380 * need to do is check whether it's already in to-space (5) or
383 if (MAJOR_OBJ_IS_IN_TO_SPACE (obj)) {
384 DEBUG (9, g_assert (objsize <= SGEN_MAX_SMALL_OBJ_SIZE));
385 DEBUG (9, fprintf (gc_debug_file, " (already copied)\n"));
386 HEAVY_STAT (++stat_major_copy_object_failed_to_space);
391 HEAVY_STAT (++stat_objects_copied_major);
393 *obj_slot = copy_object_no_checks (obj, queue);
396 #include "sgen-major-scan-object.h"
398 /* FIXME: later reduce code duplication here with build_nursery_fragments().
399 * We don't keep track of section fragments for non-nursery sections yet, so
403 build_section_fragments (GCMemSection *section)
406 char *frag_start, *frag_end;
409 /* clear scan starts */
410 memset (section->scan_starts, 0, section->num_scan_start * sizeof (gpointer));
411 frag_start = section->data;
412 section->next_data = section->data;
413 for (i = 0; i < section->pin_queue_num_entries; ++i) {
414 frag_end = section->pin_queue_start [i];
415 /* remove the pin bit from pinned objects */
416 SGEN_UNPIN_OBJECT (frag_end);
417 if (frag_end >= section->data + section->size) {
418 frag_end = section->data + section->size;
420 section->scan_starts [((char*)frag_end - (char*)section->data)/SGEN_SCAN_START_SIZE] = frag_end;
422 frag_size = frag_end - frag_start;
424 binary_protocol_empty (frag_start, frag_size);
425 memset (frag_start, 0, frag_size);
427 frag_size = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)section->pin_queue_start [i]));
428 frag_start = (char*)section->pin_queue_start [i] + frag_size;
429 section->next_data = MAX (section->next_data, frag_start);
431 frag_end = section->end_data;
432 frag_size = frag_end - frag_start;
434 binary_protocol_empty (frag_start, frag_size);
435 memset (frag_start, 0, frag_size);
440 sweep_pinned_objects_callback (char *ptr, size_t size, void *data)
442 if (SGEN_OBJECT_IS_PINNED (ptr)) {
443 SGEN_UNPIN_OBJECT (ptr);
444 DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, sgen_safe_name (ptr)));
446 DEBUG (6, fprintf (gc_debug_file, "Freeing unmarked pinned object %p (%s)\n", ptr, sgen_safe_name (ptr)));
447 free_pinned_object (ptr, size);
452 sweep_pinned_objects (void)
454 sgen_pinned_scan_objects (&pinned_allocator, sweep_pinned_objects_callback, NULL);
458 major_iterate_objects (gboolean non_pinned, gboolean pinned, IterateObjectCallbackFunc callback, void *data)
461 GCMemSection *section;
462 for (section = section_list; section; section = section->block.next)
463 sgen_scan_area_with_callback (section->data, section->end_data, callback, data, FALSE);
466 sgen_pinned_scan_objects (&pinned_allocator, callback, data);
470 major_free_non_pinned_object (char *obj, size_t size)
472 memset (obj, 0, size);
476 pin_pinned_object_callback (void *addr, size_t slot_size, SgenGrayQueue *queue)
478 binary_protocol_pin (addr, (gpointer)SGEN_LOAD_VTABLE (addr), sgen_safe_object_get_size ((MonoObject*)addr));
479 if (!SGEN_OBJECT_IS_PINNED (addr))
480 sgen_pin_stats_register_object ((char*) addr, sgen_safe_object_get_size ((MonoObject*) addr));
481 SGEN_PIN_OBJECT (addr);
482 GRAY_OBJECT_ENQUEUE (queue, addr);
483 DEBUG (6, fprintf (gc_debug_file, "Marked pinned object %p (%s) from roots\n", addr, sgen_safe_name (addr)));
487 major_find_pin_queue_start_ends (SgenGrayQueue *queue)
489 GCMemSection *section;
491 for (section = section_list; section; section = section->block.next)
492 sgen_find_section_pin_queue_start_end (section);
493 sgen_pinned_scan_pinned_objects (&pinned_allocator, (IterateObjectCallbackFunc)pin_pinned_object_callback, queue);
497 major_pin_objects (SgenGrayQueue *queue)
499 GCMemSection *section;
501 for (section = section_list; section; section = section->block.next)
502 sgen_pin_objects_in_section (section, queue);
506 major_init_to_space (void)
508 new_to_space_section ();
514 GCMemSection *section, *prev_section;
516 to_space_set_next_data ();
519 /* unpin objects from the pinned chunks and free the unmarked ones */
520 sweep_pinned_objects ();
522 sgen_pinned_update_heap_boundaries (&pinned_allocator);
524 /* free the unused sections */
526 for (section = section_list; section;) {
527 GCMemSection *this_section = section;
529 /* to_space doesn't need handling here */
530 if (section->is_to_space) {
531 section->is_to_space = FALSE;
532 prev_section = section;
533 section = section->block.next;
536 /* no pinning object, so the section is free */
537 if (!section->pin_queue_num_entries) {
538 GCMemSection *to_free;
539 g_assert (!section->pin_queue_start);
541 prev_section->block.next = section->block.next;
543 section_list = section->block.next;
545 section = section->block.next;
546 free_major_section (to_free);
549 DEBUG (6, fprintf (gc_debug_file, "Section %p has still pinned objects (%d)\n", section, section->pin_queue_num_entries));
550 build_section_fragments (section);
552 prev_section = section;
553 section = section->block.next;
556 sgen_update_heap_boundaries ((mword)this_section->data, (mword)this_section->data + this_section->size);
563 major_check_scan_starts (void)
565 GCMemSection *section;
566 for (section = section_list; section; section = section->block.next)
567 sgen_check_section_scan_starts (section);
571 major_dump_heap (FILE *heap_dump_file)
573 GCMemSection *section;
574 for (section = section_list; section; section = section->block.next)
575 sgen_dump_section (section, "old");
576 /* FIXME: dump pinned sections, too */
580 major_get_used_size (void)
583 GCMemSection *section;
584 for (section = section_list; section; section = section->block.next) {
585 /* this is approximate... */
586 tot += section->next_data - section->data;
591 /* only valid during minor collections */
592 static int old_num_major_sections;
595 major_start_nursery_collection (void)
597 old_num_major_sections = num_major_sections;
599 if (!to_space_section) {
600 new_to_space_section ();
602 /* we might have done degraded allocation since the
604 g_assert (to_space_bumper <= to_space_section->next_data);
605 to_space_bumper = to_space_section->next_data;
607 to_space_section->is_to_space = TRUE;
612 major_finish_nursery_collection (void)
614 GCMemSection *section;
615 int sections_alloced;
617 to_space_set_next_data ();
619 for (section = section_list; section; section = section->block.next)
620 section->is_to_space = FALSE;
622 sections_alloced = num_major_sections - old_num_major_sections;
623 sgen_register_major_sections_alloced (sections_alloced);
627 major_finish_major_collection (void)
632 major_ptr_is_in_non_pinned_space (char *ptr)
634 GCMemSection *section;
635 for (section = section_list; section;) {
636 if (ptr >= section->data && ptr < section->data + section->size)
638 section = section->block.next;
644 major_report_pinned_memory_usage (void)
646 sgen_report_pinned_mem_usage (&pinned_allocator);
650 get_num_major_sections (void)
652 return num_major_sections;
656 sgen_copying_init (SgenMajorCollector *collector)
658 #ifdef HEAVY_STATISTICS
659 mono_counters_register ("# major copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_forwarded);
660 mono_counters_register ("# major copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_pinned);
661 mono_counters_register ("# major copy_object() failed large or pinned chunk", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_large_pinned);
662 mono_counters_register ("# major copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_to_space);
665 collector->section_size = MAJOR_SECTION_SIZE;
666 collector->supports_cardtable = FALSE;
667 collector->is_parallel = FALSE;
669 collector->have_swept = &have_swept;
671 collector->alloc_heap = major_alloc_heap;
672 collector->is_object_live = major_is_object_live;
673 collector->alloc_small_pinned_obj = major_alloc_small_pinned_obj;
674 collector->alloc_degraded = major_alloc_degraded;
675 collector->alloc_object = major_alloc_object;
676 collector->free_pinned_object = free_pinned_object;
677 collector->iterate_objects = major_iterate_objects;
678 collector->free_non_pinned_object = major_free_non_pinned_object;
679 collector->find_pin_queue_start_ends = major_find_pin_queue_start_ends;
680 collector->pin_objects = major_pin_objects;
681 collector->pin_major_object = pin_major_object;
682 collector->init_to_space = major_init_to_space;
683 collector->sweep = major_sweep;
684 collector->check_scan_starts = major_check_scan_starts;
685 collector->dump_heap = major_dump_heap;
686 collector->get_used_size = major_get_used_size;
687 collector->start_nursery_collection = major_start_nursery_collection;
688 collector->finish_nursery_collection = major_finish_nursery_collection;
689 collector->finish_major_collection = major_finish_major_collection;
690 collector->ptr_is_in_non_pinned_space = major_ptr_is_in_non_pinned_space;
691 collector->obj_is_from_pinned_alloc = obj_is_from_pinned_alloc;
692 collector->report_pinned_memory_usage = major_report_pinned_memory_usage;
693 collector->get_num_major_sections = get_num_major_sections;
694 collector->handle_gc_param = NULL;
695 collector->print_gc_param_usage = NULL;
697 collector->major_ops.copy_or_mark_object = major_copy_or_mark_object;
698 collector->major_ops.scan_object = major_scan_object;