#include "config.h"
#ifdef HAVE_SGEN_GC
+#ifdef __MACH__
+#undef _XOPEN_SOURCE
+#define _XOPEN_SOURCE
+#define _DARWIN_C_SOURCE
+#endif
+
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <signal.h>
#include <errno.h>
#include <assert.h>
-#ifdef __MACH__
-#undef _XOPEN_SOURCE
-#endif
-#ifdef __MACH__
-#define _XOPEN_SOURCE
-#endif
#include "metadata/sgen-gc.h"
#include "metadata/metadata-internals.h"
#include "metadata/sgen-protocol.h"
#include "metadata/sgen-archdep.h"
#include "metadata/sgen-bridge.h"
+#include "metadata/sgen-memory-governor.h"
#include "metadata/mono-gc.h"
#include "metadata/method-builder.h"
#include "metadata/profiler-private.h"
#include "utils/mono-proclib.h"
#include "utils/mono-memory-model.h"
#include "utils/mono-logger-internal.h"
+#include "utils/dtrace.h"
#include <mono/utils/mono-logger-internal.h>
#include <mono/utils/memcheck.h>
*/
/* 0 means not initialized, 1 is initialized, -1 means in progress */
-static gint32 gc_initialized = 0;
+static int gc_initialized = 0;
+/* If set, check if we need to do something every X allocations */
+gboolean has_per_allocation_action;
+/* If set, do a heap check every X allocation */
+guint32 verify_before_allocs = 0;
/* If set, do a minor collection before every X allocation */
guint32 collect_before_allocs = 0;
+/* If set, do a whole heap check before each collection */
+static gboolean whole_heap_check_before_collection = FALSE;
/* If set, do a heap consistency check before each minor collection */
static gboolean consistency_check_at_minor_collection = FALSE;
/* If set, check that there are no references to the domain left at domain unload */
long long stat_scan_object_called_nursery = 0;
long long stat_scan_object_called_major = 0;
+long long stat_slots_allocated_in_vain;
+
long long stat_nursery_copy_object_failed_from_space = 0;
long long stat_nursery_copy_object_failed_forwarded = 0;
long long stat_nursery_copy_object_failed_pinned = 0;
+long long stat_nursery_copy_object_failed_to_space = 0;
static int stat_wbarrier_set_field = 0;
static int stat_wbarrier_set_arrayref = 0;
int gc_debug_level = 0;
FILE* gc_debug_file;
-static gboolean debug_print_allowance = FALSE;
/*
void
#define pin_object SGEN_PIN_OBJECT
#define unpin_object SGEN_UNPIN_OBJECT
-#define ptr_in_nursery mono_sgen_ptr_in_nursery
+#define ptr_in_nursery sgen_ptr_in_nursery
#define LOAD_VTABLE SGEN_LOAD_VTABLE
return vt->klass->name;
}
-#define safe_object_get_size mono_sgen_safe_object_get_size
+#define safe_object_get_size sgen_safe_object_get_size
const char*
-mono_sgen_safe_name (void* obj)
+sgen_safe_name (void* obj)
{
return safe_name (obj);
}
static gboolean use_cardtable;
-#define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
-
#define SCAN_START_SIZE SGEN_SCAN_START_SIZE
static mword pagesize = 4096;
-static mword nursery_size;
int degraded_mode = 0;
static mword bytes_pinned_from_failed_allocation = 0;
-static mword total_alloc = 0;
-/* use this to tune when to do a major/minor collection */
-static mword memory_pressure = 0;
-static mword minor_collection_allowance;
-static int minor_collection_sections_alloced = 0;
-
-
-/* GC Logging stats */
-static int last_major_num_sections = 0;
-static int last_los_memory_usage = 0;
-static gboolean major_collection_happened = FALSE;
-
GCMemSection *nursery_section = NULL;
static mword lowest_heap_address = ~(mword)0;
static mword highest_heap_address = 0;
MonoNativeThreadId main_gc_thread = NULL;
#endif
-/*
- * ######################################################################
- * ######## Heap size accounting
- * ######################################################################
- */
-/*heap limits*/
-static mword max_heap_size = ((mword)0)- ((mword)1);
-static mword soft_heap_limit = ((mword)0) - ((mword)1);
-static mword allocated_heap;
-
/*Object was pinned during the current collection*/
static mword objects_pinned;
-void
-mono_sgen_release_space (mword size, int space)
-{
- allocated_heap -= size;
-}
-
-static size_t
-available_free_space (void)
-{
- return max_heap_size - MIN (allocated_heap, max_heap_size);
-}
-
-gboolean
-mono_sgen_try_alloc_space (mword size, int space)
-{
- if (available_free_space () < size)
- return FALSE;
-
- allocated_heap += size;
- mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
- return TRUE;
-}
-
-static void
-init_heap_size_limits (glong max_heap, glong soft_limit)
-{
- if (soft_limit)
- soft_heap_limit = soft_limit;
-
- if (max_heap == 0)
- return;
-
- if (max_heap < soft_limit) {
- fprintf (stderr, "max-heap-size must be at least as large as soft-heap-limit.\n");
- exit (1);
- }
-
- if (max_heap < nursery_size * 4) {
- fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
- exit (1);
- }
- max_heap_size = max_heap - nursery_size;
-}
-
/*
* ######################################################################
* ######## Macros and function declarations.
/* forward declarations */
static int stop_world (int generation);
-static int restart_world (int generation);
+static int restart_world (int generation, GGTimingInfo *timing);
static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeReadyEntry *list, GrayQueue *queue);
static void process_fin_stage_entries (void);
static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
static void null_links_for_domain (MonoDomain *domain, int generation);
+static void remove_finalizers_for_domain (MonoDomain *domain, int generation);
static void process_dislink_stage_entries (void);
static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
-static gboolean need_major_collection (mword space_needed);
-static void major_collection (const char *reason);
static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc);
static gboolean mono_gc_is_critical_method (MonoMethod *method);
void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
+
static void init_stats (void);
static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
static void null_ephemerons_for_domain (MonoDomain *domain);
+SgenObjectOperations current_object_ops;
SgenMajorCollector major_collector;
+SgenMinorCollector sgen_minor_collector;
static GrayQueue gray_queue;
static SgenRemeberedSet remset;
-#define WORKERS_DISTRIBUTE_GRAY_QUEUE (mono_sgen_collection_is_parallel () ? mono_sgen_workers_get_distribute_gray_queue () : &gray_queue)
+#define WORKERS_DISTRIBUTE_GRAY_QUEUE (sgen_collection_is_parallel () ? sgen_workers_get_distribute_gray_queue () : &gray_queue)
static SgenGrayQueue*
-mono_sgen_workers_get_job_gray_queue (WorkerData *worker_data)
+sgen_workers_get_job_gray_queue (WorkerData *worker_data)
{
return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
}
}
void
-mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
+sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
{
while (start < end) {
size_t size;
}
return;
case ROOT_DESC_COMPLEX: {
- gsize *bitmap_data = mono_sgen_get_complex_descriptor_bitmap (desc);
+ gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
int bwords = (*bitmap_data) - 1;
void **start_run = start_root;
bitmap_data++;
break;
}
case ROOT_DESC_USER: {
- MonoGCRootMarkFunc marker = mono_sgen_get_user_descriptor_func (desc);
+ MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
marker (start_root, check_root_obj_specific_ref_from_marker);
break;
}
scan_object_for_specific_ref_precise = precise;
- mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+ sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
(IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
- mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
+ sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
}
break;
case ROOT_DESC_COMPLEX: {
- gsize *bitmap_data = mono_sgen_get_complex_descriptor_bitmap (desc);
+ gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
int bwords = (*bitmap_data) - 1;
void **start_run = start_root;
bitmap_data++;
break;
}
case ROOT_DESC_USER: {
- MonoGCRootMarkFunc marker = mono_sgen_get_user_descriptor_func (desc);
+ MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
marker (start_root, check_obj_not_in_domain);
break;
}
{
LOSObject *bigobj;
- mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+ sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
(IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
process_fin_stage_entries ();
process_dislink_stage_entries ();
- mono_sgen_clear_nursery_fragments ();
+ sgen_clear_nursery_fragments ();
if (xdomain_checks && domain != mono_get_root_domain ()) {
scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
check_for_xdomain_refs ();
}
- mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
- (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
-
/*Ephemerons and dislinks must be processed before LOS since they might end up pointing
to memory returned to the OS.*/
null_ephemerons_for_domain (domain);
for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
null_links_for_domain (domain, i);
+ for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
+ remove_finalizers_for_domain (domain, i);
+
+ sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+ (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
+
/* We need two passes over major and large objects because
freeing such objects might give their memory back to the OS
(in the case of large objects) or obliterate its vtable
bigobj = bigobj->next;
DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
bigobj->data));
- mono_sgen_los_free_object (to_free);
+ sgen_los_free_object (to_free);
continue;
}
prev = bigobj;
if (G_UNLIKELY (do_pin_stats)) {
if (domain == mono_get_root_domain ())
- mono_sgen_pin_stats_print_class_stats ();
+ sgen_pin_stats_print_class_stats ();
}
UNLOCK_GC;
}
/*
- * mono_sgen_add_to_global_remset:
+ * sgen_add_to_global_remset:
*
* The global remset contains locations which point into newspace after
* a minor collection. This can happen if the objects they point to are pinned.
* lock must be held. For serial collectors that is not necessary.
*/
void
-mono_sgen_add_to_global_remset (gpointer ptr)
+sgen_add_to_global_remset (gpointer ptr)
{
remset.record_pointer (ptr);
}
/*
- * mono_sgen_drain_gray_stack:
+ * sgen_drain_gray_stack:
*
* Scan objects in the gray stack until the stack is empty. This should be called
* frequently after each object is copied, to achieve better locality and cache
* usage.
*/
gboolean
-mono_sgen_drain_gray_stack (GrayQueue *queue, int max_objs)
+sgen_drain_gray_stack (GrayQueue *queue, int max_objs)
{
char *obj;
+ ScanObjectFunc scan_func = current_object_ops.scan_object;
- if (current_collection_generation == GENERATION_NURSERY) {
- ScanObjectFunc scan_func = mono_sgen_get_minor_scan_object ();
-
+ if (max_objs == -1) {
for (;;) {
GRAY_OBJECT_DEQUEUE (queue, obj);
if (!obj)
} else {
int i;
- if (mono_sgen_collection_is_parallel () && mono_sgen_workers_is_distributed_queue (queue))
- return TRUE;
-
do {
for (i = 0; i != max_objs; ++i) {
GRAY_OBJECT_DEQUEUE (queue, obj);
if (!obj)
return TRUE;
DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
- major_collector.major_scan_object (obj, queue);
+ scan_func (obj, queue);
}
} while (max_objs < 0);
return FALSE;
int idx;
void **definitely_pinned = start;
- mono_sgen_nursery_allocator_prepare_for_pinning ();
+ sgen_nursery_allocator_prepare_for_pinning ();
while (start < end) {
addr = *start;
if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
+ if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
+ int gen = sgen_ptr_in_nursery (search_start) ? GENERATION_NURSERY : GENERATION_OLD;
+ MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (search_start);
+ MONO_GC_OBJ_PINNED ((mword)search_start, sgen_safe_object_get_size (search_start), vt->klass->name_space, vt->klass->name, gen);
+ }
pin_object (search_start);
GRAY_OBJECT_ENQUEUE (queue, search_start);
if (G_UNLIKELY (do_pin_stats))
- mono_sgen_pin_stats_register_object (search_start, last_obj_size);
+ sgen_pin_stats_register_object (search_start, last_obj_size);
definitely_pinned [count] = search_start;
count++;
break;
}
void
-mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
+sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
{
int num_entries = section->pin_queue_num_entries;
if (num_entries) {
void
-mono_sgen_pin_object (void *object, GrayQueue *queue)
+sgen_pin_object (void *object, GrayQueue *queue)
{
- if (mono_sgen_collection_is_parallel ()) {
+ if (sgen_collection_is_parallel ()) {
LOCK_PIN_QUEUE;
/*object arrives pinned*/
- mono_sgen_pin_stage_ptr (object);
+ sgen_pin_stage_ptr (object);
++objects_pinned ;
UNLOCK_PIN_QUEUE;
} else {
SGEN_PIN_OBJECT (object);
- mono_sgen_pin_stage_ptr (object);
+ sgen_pin_stage_ptr (object);
++objects_pinned;
if (G_UNLIKELY (do_pin_stats))
- mono_sgen_pin_stats_register_object (object, safe_object_get_size (object));
+ sgen_pin_stats_register_object (object, safe_object_get_size (object));
}
GRAY_OBJECT_ENQUEUE (queue, object);
binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
+ if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
+ int gen = sgen_ptr_in_nursery (object) ? GENERATION_NURSERY : GENERATION_OLD;
+ MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (object);
+ MONO_GC_OBJ_PINNED ((mword)object, sgen_safe_object_get_size (object), vt->klass->name_space, vt->klass->name, gen);
+ }
+}
+
+void
+sgen_parallel_pin_or_update (void **ptr, void *obj, MonoVTable *vt, SgenGrayQueue *queue)
+{
+ for (;;) {
+ mword vtable_word;
+ gboolean major_pinned = FALSE;
+
+ if (sgen_ptr_in_nursery (obj)) {
+ if (SGEN_CAS_PTR (obj, (void*)((mword)vt | SGEN_PINNED_BIT), vt) == vt) {
+ sgen_pin_object (obj, queue);
+ break;
+ }
+ } else {
+ major_collector.pin_major_object (obj, queue);
+ major_pinned = TRUE;
+ }
+
+ vtable_word = *(mword*)obj;
+ /*someone else forwarded it, update the pointer and bail out*/
+ if (vtable_word & SGEN_FORWARDED_BIT) {
+ *ptr = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
+ break;
+ }
+
+ /*someone pinned it, nothing to do.*/
+ if (vtable_word & SGEN_PINNED_BIT || major_pinned)
+ break;
+ }
}
/* Sort the addresses in array in increasing order.
* Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
*/
void
-mono_sgen_sort_addresses (void **array, int size)
+sgen_sort_addresses (void **array, int size)
{
int i;
void *tmp;
mword addr = (mword)*start;
addr &= ~(ALLOC_ALIGN - 1);
if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
- mono_sgen_pin_stage_ptr ((void*)addr);
+ sgen_pin_stage_ptr ((void*)addr);
if (G_UNLIKELY (do_pin_stats)) {
if (ptr_in_nursery ((void*)addr))
- mono_sgen_pin_stats_register_address ((char*)addr, pin_type);
+ sgen_pin_stats_register_address ((char*)addr, pin_type);
}
DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
count++;
if ((desc & 1) && *start_root) {
copy_func (start_root, queue);
DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
- mono_sgen_drain_gray_stack (queue, -1);
+ sgen_drain_gray_stack (queue, -1);
}
desc >>= 1;
start_root++;
}
return;
case ROOT_DESC_COMPLEX: {
- gsize *bitmap_data = mono_sgen_get_complex_descriptor_bitmap (desc);
+ gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
int bwords = (*bitmap_data) - 1;
void **start_run = start_root;
bitmap_data++;
if ((bmap & 1) && *objptr) {
copy_func (objptr, queue);
DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
- mono_sgen_drain_gray_stack (queue, -1);
+ sgen_drain_gray_stack (queue, -1);
}
bmap >>= 1;
++objptr;
}
case ROOT_DESC_USER: {
UserCopyOrMarkData data = { copy_func, queue };
- MonoGCRootMarkFunc marker = mono_sgen_get_user_descriptor_func (desc);
+ MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
set_user_copy_or_mark_data (&data);
marker (start_root, single_arg_user_copy_or_mark);
set_user_copy_or_mark_data (NULL);
}
void
-mono_sgen_update_heap_boundaries (mword low, mword high)
+sgen_update_heap_boundaries (mword low, mword high)
{
mword old;
} while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
}
-static unsigned long
-prot_flags_for_activate (int activate)
-{
- unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
- return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
-}
-
-/*
- * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
- * This must not require any lock.
- */
-void*
-mono_sgen_alloc_os_memory (size_t size, int activate)
-{
- void *ptr = mono_valloc (0, size, prot_flags_for_activate (activate));
- if (ptr) {
- /* FIXME: CAS */
- total_alloc += size;
- }
- return ptr;
-}
-
-/* size must be a power of 2 */
-void*
-mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
-{
- void *ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (activate));
- if (ptr) {
- /* FIXME: CAS */
- total_alloc += size;
- }
- return ptr;
-}
-
-/*
- * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
- */
-void
-mono_sgen_free_os_memory (void *addr, size_t size)
-{
- mono_vfree (addr, size);
- /* FIXME: CAS */
- total_alloc -= size;
-}
-
/*
* Allocate and setup the data structures needed to be able to allocate objects
* in the nursery. The nursery is stored in nursery_section.
if (nursery_section)
return;
- DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
+ DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)sgen_nursery_size));
/* later we will alloc a larger area for the nursery but only activate
* what we need. The rest will be used as expansion if we have too many pinned
* objects in the existing nursery.
*/
/* FIXME: handle OOM */
- section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
+ section = sgen_alloc_internal (INTERNAL_MEM_SECTION);
+
+ alloc_size = sgen_nursery_size;
+
+ /* If there isn't enough space even for the nursery we should simply abort. */
+ g_assert (sgen_memgov_try_alloc_space (alloc_size, SPACE_NURSERY));
- g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
- alloc_size = nursery_size;
#ifdef SGEN_ALIGN_NURSERY
data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
#else
data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
#endif
- mono_sgen_update_heap_boundaries ((mword)data, (mword)(data + nursery_size));
- DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %lu, total: %lu\n", data, data + alloc_size, (unsigned long)nursery_size, (unsigned long)total_alloc));
+ sgen_update_heap_boundaries ((mword)data, (mword)(data + sgen_nursery_size));
+ DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %lu, total: %lu\n", data, data + alloc_size, (unsigned long)sgen_nursery_size, (unsigned long)mono_gc_get_heap_size ()));
section->data = section->next_data = data;
section->size = alloc_size;
- section->end_data = data + nursery_size;
+ section->end_data = data + sgen_nursery_size;
scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
- section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
+ section->scan_starts = sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS, TRUE);
section->num_scan_start = scan_starts;
section->block.role = MEMORY_ROLE_GEN0;
section->block.next = NULL;
nursery_section = section;
- mono_sgen_nursery_allocator_set_nursery_bounds (data, data + nursery_size);
+ sgen_nursery_allocator_set_nursery_bounds (data, data + sgen_nursery_size);
}
void*
mono_gc_get_nursery (int *shift_bits, size_t *size)
{
- *size = nursery_size;
+ *size = sgen_nursery_size;
#ifdef SGEN_ALIGN_NURSERY
*shift_bits = DEFAULT_NURSERY_BITS;
#else
*shift_bits = -1;
#endif
- return mono_sgen_get_nursery_start ();
+ return sgen_get_nursery_start ();
}
void
FILE *
mono_gc_get_logfile (void)
{
- return mono_sgen_get_logfile ();
+ return sgen_get_logfile ();
}
static void
}
return;
case ROOT_DESC_COMPLEX: {
- gsize *bitmap_data = mono_sgen_get_complex_descriptor_bitmap (desc);
+ gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
int bwords = (*bitmap_data) - 1;
void **start_run = start_root;
bitmap_data++;
break;
}
case ROOT_DESC_USER: {
- MonoGCRootMarkFunc marker = mono_sgen_get_user_descriptor_func (desc);
+ MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
root_report = report;
marker (start_root, single_arg_report_root);
break;
static void
stw_bridge_process (void)
{
- mono_sgen_bridge_processing_stw_step ();
+ sgen_bridge_processing_stw_step ();
}
static void
bridge_process (void)
{
- mono_sgen_bridge_processing_finish ();
-}
-
-CopyOrMarkObjectFunc
-mono_sgen_get_copy_object (void)
-{
- if (current_collection_generation == GENERATION_NURSERY) {
- if (mono_sgen_collection_is_parallel ())
- return major_collector.copy_object;
- else
- return major_collector.nopar_copy_object;
- } else {
- return major_collector.copy_or_mark_object;
- }
+ sgen_bridge_processing_finish ();
}
-ScanObjectFunc
-mono_sgen_get_minor_scan_object (void)
-{
- g_assert (current_collection_generation == GENERATION_NURSERY);
-
- if (mono_sgen_collection_is_parallel ())
- return major_collector.minor_scan_object;
- else
- return major_collector.nopar_minor_scan_object;
+SgenObjectOperations *
+sgen_get_current_object_ops (void){
+ return ¤t_object_ops;
}
-ScanVTypeFunc
-mono_sgen_get_minor_scan_vtype (void)
-{
- g_assert (current_collection_generation == GENERATION_NURSERY);
-
- if (mono_sgen_collection_is_parallel ())
- return major_collector.minor_scan_vtype;
- else
- return major_collector.nopar_minor_scan_vtype;
-}
static void
finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
TV_DECLARE (atv);
TV_DECLARE (btv);
int done_with_ephemerons, ephemeron_rounds = 0;
- CopyOrMarkObjectFunc copy_func = mono_sgen_get_copy_object ();
+ CopyOrMarkObjectFunc copy_func = current_object_ops.copy_or_mark_object;
/*
* We copied all the reachable objects. Now it's the time to copy
* To achieve better cache locality and cache usage, we drain the gray stack
* frequently, after each object is copied, and just finish the work here.
*/
- mono_sgen_drain_gray_stack (queue, -1);
+ sgen_drain_gray_stack (queue, -1);
TV_GETTIME (atv);
DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
We must reset the gathered bridges since their original block might be evacuated due to major
fragmentation in the meanwhile and the bridge code should not have to deal with that.
*/
- mono_sgen_bridge_reset_data ();
+ sgen_bridge_reset_data ();
/*
* Walk the ephemeron tables marking all values with reachable keys. This must be completely done
done_with_ephemerons = 0;
do {
done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
- mono_sgen_drain_gray_stack (queue, -1);
+ sgen_drain_gray_stack (queue, -1);
++ephemeron_rounds;
} while (!done_with_ephemerons);
- mono_sgen_scan_togglerefs (copy_func, start_addr, end_addr, queue);
+ sgen_scan_togglerefs (copy_func, start_addr, end_addr, queue);
if (generation == GENERATION_OLD)
- mono_sgen_scan_togglerefs (copy_func, mono_sgen_get_nursery_start (), mono_sgen_get_nursery_end (), queue);
+ sgen_scan_togglerefs (copy_func, sgen_get_nursery_start (), sgen_get_nursery_end (), queue);
- if (mono_sgen_need_bridge_processing ()) {
+ if (sgen_need_bridge_processing ()) {
collect_bridge_objects (copy_func, start_addr, end_addr, generation, queue);
if (generation == GENERATION_OLD)
- collect_bridge_objects (copy_func, mono_sgen_get_nursery_start (), mono_sgen_get_nursery_end (), GENERATION_NURSERY, queue);
- mono_sgen_drain_gray_stack (queue, -1);
+ collect_bridge_objects (copy_func, sgen_get_nursery_start (), sgen_get_nursery_end (), GENERATION_NURSERY, queue);
}
+ /*
+ Make sure we drain the gray stack before processing disappearing links and finalizers.
+ If we don't make sure it is empty we might wrongly see a live object as dead.
+ */
+ sgen_drain_gray_stack (queue, -1);
+
/*
We must clear weak links that don't track resurrection before processing object ready for
finalization so they can be cleared before that.
*/
finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
if (generation == GENERATION_OLD)
- finalize_in_range (copy_func, mono_sgen_get_nursery_start (), mono_sgen_get_nursery_end (), GENERATION_NURSERY, queue);
+ finalize_in_range (copy_func, sgen_get_nursery_start (), sgen_get_nursery_end (), GENERATION_NURSERY, queue);
/* drain the new stack that might have been created */
DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
- mono_sgen_drain_gray_stack (queue, -1);
+ sgen_drain_gray_stack (queue, -1);
/*
* This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
done_with_ephemerons = 0;
do {
done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
- mono_sgen_drain_gray_stack (queue, -1);
+ sgen_drain_gray_stack (queue, -1);
++ephemeron_rounds;
} while (!done_with_ephemerons);
* GC a finalized object my lose the monitor because it is cleared before the finalizer is
* called.
*/
- g_assert (mono_sgen_gray_object_queue_is_empty (queue));
+ g_assert (sgen_gray_object_queue_is_empty (queue));
for (;;) {
null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
if (generation == GENERATION_OLD)
null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
- if (mono_sgen_gray_object_queue_is_empty (queue))
+ if (sgen_gray_object_queue_is_empty (queue))
break;
- mono_sgen_drain_gray_stack (queue, -1);
+ sgen_drain_gray_stack (queue, -1);
}
- g_assert (mono_sgen_gray_object_queue_is_empty (queue));
+ g_assert (sgen_gray_object_queue_is_empty (queue));
}
void
-mono_sgen_check_section_scan_starts (GCMemSection *section)
+sgen_check_section_scan_starts (GCMemSection *section)
{
int i;
for (i = 0; i < section->num_scan_start; ++i) {
{
if (!do_scan_starts_check)
return;
- mono_sgen_check_section_scan_starts (nursery_section);
+ sgen_check_section_scan_starts (nursery_section);
major_collector.check_scan_starts ();
}
}
void
-mono_sgen_dump_occupied (char *start, char *end, char *section_start)
+sgen_dump_occupied (char *start, char *end, char *section_start)
{
fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
}
void
-mono_sgen_dump_section (GCMemSection *section, const char *type)
+sgen_dump_section (GCMemSection *section, const char *type)
{
char *start = section->data;
char *end = section->data + section->size;
if (!*(void**)start) {
if (occ_start) {
- mono_sgen_dump_occupied (occ_start, start, section->data);
+ sgen_dump_occupied (occ_start, start, section->data);
occ_start = NULL;
}
start += sizeof (void*); /* should be ALLOC_ALIGN, really */
start += size;
}
if (occ_start)
- mono_sgen_dump_occupied (occ_start, start, section->data);
+ sgen_dump_occupied (occ_start, start, section->data);
fprintf (heap_dump_file, "</section>\n");
}
fprintf (heap_dump_file, " reason=\"%s\"", reason);
fprintf (heap_dump_file, ">\n");
fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
- mono_sgen_dump_internal_mem_usage (heap_dump_file);
- fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", mono_sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
+ sgen_dump_internal_mem_usage (heap_dump_file);
+ fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
/* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
- fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", mono_sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
+ fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
fprintf (heap_dump_file, "<pinned-objects>\n");
- for (list = mono_sgen_pin_stats_get_object_list (); list; list = list->next)
+ for (list = sgen_pin_stats_get_object_list (); list; list = list->next)
dump_object (list->obj, TRUE);
fprintf (heap_dump_file, "</pinned-objects>\n");
- mono_sgen_dump_section (nursery_section, "nursery");
+ sgen_dump_section (nursery_section, "nursery");
major_collector.dump_heap (heap_dump_file);
}
void
-mono_sgen_register_moved_object (void *obj, void *destination)
+sgen_register_moved_object (void *obj, void *destination)
{
g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
/* FIXME: handle this for parallel collector */
- g_assert (!mono_sgen_collection_is_parallel ());
+ g_assert (!sgen_collection_is_parallel ());
if (moved_objects_idx == MOVED_OBJECTS_NUM) {
mono_profiler_gc_moves (moved_objects, moved_objects_idx);
mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
+ mono_counters_register ("Slots allocated in vain", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_slots_allocated_in_vain);
+
mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
+ mono_counters_register ("# nursery copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_to_space);
- mono_sgen_nursery_allocator_init_heavy_stats ();
- mono_sgen_alloc_init_heavy_stats ();
+ sgen_nursery_allocator_init_heavy_stats ();
+ sgen_alloc_init_heavy_stats ();
#endif
inited = TRUE;
}
-static gboolean need_calculate_minor_collection_allowance;
-
-static int last_collection_old_num_major_sections;
-static mword last_collection_los_memory_usage = 0;
-static mword last_collection_old_los_memory_usage;
-static mword last_collection_los_memory_alloced;
-
-static void
-reset_minor_collection_allowance (void)
-{
- need_calculate_minor_collection_allowance = TRUE;
-}
-
-static void
-try_calculate_minor_collection_allowance (gboolean overwrite)
-{
- int num_major_sections, num_major_sections_saved, save_target, allowance_target;
- mword los_memory_saved, new_major, new_heap_size;
-
- if (overwrite)
- g_assert (need_calculate_minor_collection_allowance);
-
- if (!need_calculate_minor_collection_allowance)
- return;
-
- if (!*major_collector.have_swept) {
- if (overwrite)
- minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
- return;
- }
-
- num_major_sections = major_collector.get_num_major_sections ();
-
- num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
- los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
-
- new_major = num_major_sections * major_collector.section_size;
- new_heap_size = new_major + last_collection_los_memory_usage;
-
- /*
- * FIXME: Why is save_target half the major memory plus half the
- * LOS memory saved? Shouldn't it be half the major memory
- * saved plus half the LOS memory saved? Or half the whole heap
- * size?
- */
- save_target = (new_major + los_memory_saved) / 2;
-
- /*
- * We aim to allow the allocation of as many sections as is
- * necessary to reclaim save_target sections in the next
- * collection. We assume the collection pattern won't change.
- * In the last cycle, we had num_major_sections_saved for
- * minor_collection_sections_alloced. Assuming things won't
- * change, this must be the same ratio as save_target for
- * allowance_target, i.e.
- *
- * num_major_sections_saved save_target
- * --------------------------------- == ----------------
- * minor_collection_sections_alloced allowance_target
- *
- * hence:
- */
- allowance_target = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major_collector.section_size + last_collection_los_memory_alloced) / (double)(num_major_sections_saved * major_collector.section_size + los_memory_saved));
-
- minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
-
- if (new_heap_size + minor_collection_allowance > soft_heap_limit) {
- if (new_heap_size > soft_heap_limit)
- minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
- else
- minor_collection_allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
- }
-
- if (debug_print_allowance) {
- mword old_major = last_collection_old_num_major_sections * major_collector.section_size;
-
- fprintf (gc_debug_file, "Before collection: %ld bytes (%ld major, %ld LOS)\n",
- old_major + last_collection_old_los_memory_usage, old_major, last_collection_old_los_memory_usage);
- fprintf (gc_debug_file, "After collection: %ld bytes (%ld major, %ld LOS)\n",
- new_heap_size, new_major, last_collection_los_memory_usage);
- fprintf (gc_debug_file, "Allowance: %ld bytes\n", minor_collection_allowance);
- }
-
- if (major_collector.have_computed_minor_collection_allowance)
- major_collector.have_computed_minor_collection_allowance ();
-
- need_calculate_minor_collection_allowance = FALSE;
-}
-
-static gboolean
-need_major_collection (mword space_needed)
-{
- mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
- return (space_needed > available_free_space ()) ||
- minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
-}
-
-gboolean
-mono_sgen_need_major_collection (mword space_needed)
-{
- return need_major_collection (space_needed);
-}
static void
reset_pinned_from_failed_allocation (void)
}
void
-mono_sgen_set_pinned_from_failed_allocation (mword objsize)
+sgen_set_pinned_from_failed_allocation (mword objsize)
{
bytes_pinned_from_failed_allocation += objsize;
}
gboolean
-mono_sgen_collection_is_parallel (void)
+sgen_collection_is_parallel (void)
{
switch (current_collection_generation) {
case GENERATION_NURSERY:
case GENERATION_OLD:
return major_collector.is_parallel;
default:
- g_assert_not_reached ();
+ g_error ("Invalid current generation %d", current_collection_generation);
}
}
-gboolean
-mono_sgen_nursery_collection_is_parallel (void)
-{
- return nursery_collection_is_parallel;
-}
-
typedef struct
{
char *heap_start;
{
FinishRememberedSetScanJobData *job_data = job_data_untyped;
- remset.finish_scan_remsets (job_data->heap_start, job_data->heap_end, mono_sgen_workers_get_job_gray_queue (worker_data));
+ remset.finish_scan_remsets (job_data->heap_start, job_data->heap_end, sgen_workers_get_job_gray_queue (worker_data));
}
typedef struct
scan_from_registered_roots (job_data->func,
job_data->heap_start, job_data->heap_end,
job_data->root_type,
- mono_sgen_workers_get_job_gray_queue (worker_data));
+ sgen_workers_get_job_gray_queue (worker_data));
}
typedef struct
ScanThreadDataJobData *job_data = job_data_untyped;
scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
- mono_sgen_workers_get_job_gray_queue (worker_data));
+ sgen_workers_get_job_gray_queue (worker_data));
}
typedef struct
{
ScanFinalizerEntriesJobData *job_data = job_data_untyped;
- scan_finalizer_entries (mono_sgen_get_copy_object (),
+ scan_finalizer_entries (current_object_ops.copy_or_mark_object,
job_data->list,
- mono_sgen_workers_get_job_gray_queue (worker_data));
+ sgen_workers_get_job_gray_queue (worker_data));
}
static void
return;
/*This cleans up unused fragments */
- mono_sgen_nursery_allocator_prepare_for_pinning ();
+ sgen_nursery_allocator_prepare_for_pinning ();
- hole_start = start = cur = mono_sgen_get_nursery_start ();
- end = mono_sgen_get_nursery_end ();
+ hole_start = start = cur = sgen_get_nursery_start ();
+ end = sgen_get_nursery_end ();
while (cur < end) {
size_t ss, size;
if (do_dump_nursery_content) {
if (cur > hole_start)
fprintf (gc_debug_file, "HOLE [%p %p %d]\n", hole_start, cur, (int)(cur - hole_start));
- fprintf (gc_debug_file, "OBJ [%p %p %d %d %s %d]\n", cur, cur + size, (int)size, (int)ss, mono_sgen_safe_name ((MonoObject*)cur), (gpointer)LOAD_VTABLE (cur) == mono_sgen_get_array_fill_vtable ());
+ fprintf (gc_debug_file, "OBJ [%p %p %d %d %s %d]\n", cur, cur + size, (int)size, (int)ss, sgen_safe_name ((MonoObject*)cur), (gpointer)LOAD_VTABLE (cur) == sgen_get_array_fill_vtable ());
}
cur += size;
hole_start = cur;
* collection.
*/
static gboolean
-collect_nursery (size_t requested_size)
+collect_nursery (void)
{
gboolean needs_major;
size_t max_garbage_amount;
if (disable_minor_collections)
return TRUE;
+ MONO_GC_BEGIN (GENERATION_NURSERY);
+
verify_nursery ();
mono_perfcounters->gc_collections0++;
current_collection_generation = GENERATION_NURSERY;
-
+ if (sgen_collection_is_parallel ())
+ current_object_ops = sgen_minor_collector.parallel_ops;
+ else
+ current_object_ops = sgen_minor_collector.serial_ops;
+
reset_pinned_from_failed_allocation ();
binary_protocol_collection (GENERATION_NURSERY);
check_scan_starts ();
+ sgen_nursery_alloc_prepare_for_minor ();
+
degraded_mode = 0;
objects_pinned = 0;
- nursery_next = mono_sgen_nursery_alloc_get_upper_alloc_bound ();
+ nursery_next = sgen_nursery_alloc_get_upper_alloc_bound ();
/* FIXME: optimize later to use the higher address where an object can be present */
- nursery_next = MAX (nursery_next, mono_sgen_get_nursery_end ());
+ nursery_next = MAX (nursery_next, sgen_get_nursery_end ());
- DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", stat_minor_gcs, mono_sgen_get_nursery_start (), nursery_next, (int)(nursery_next - mono_sgen_get_nursery_start ())));
- max_garbage_amount = nursery_next - mono_sgen_get_nursery_start ();
+ DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", stat_minor_gcs, sgen_get_nursery_start (), nursery_next, (int)(nursery_next - sgen_get_nursery_start ())));
+ max_garbage_amount = nursery_next - sgen_get_nursery_start ();
g_assert (nursery_section->size >= max_garbage_amount);
/* world must be stopped already */
TV_GETTIME (all_atv);
atv = all_atv;
- /* Pinning no longer depends on clearing all nursery fragments */
- mono_sgen_clear_current_nursery_fragment ();
-
TV_GETTIME (btv);
time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
- if (xdomain_checks)
+ if (xdomain_checks) {
+ sgen_clear_nursery_fragments ();
check_for_xdomain_refs ();
+ }
nursery_section->next_data = nursery_next;
major_collector.start_nursery_collection ();
- try_calculate_minor_collection_allowance (FALSE);
+ sgen_memgov_minor_collection_start ();
- mono_sgen_gray_object_queue_init (&gray_queue);
- mono_sgen_workers_init_distribute_gray_queue ();
+ sgen_gray_object_queue_init (&gray_queue);
+ sgen_workers_init_distribute_gray_queue ();
stat_minor_gcs++;
- mono_stats.minor_gc_count ++;
+ gc_stats.minor_gc_count ++;
if (remset.prepare_for_minor_collection)
remset.prepare_for_minor_collection ();
process_dislink_stage_entries ();
/* pin from pinned handles */
- mono_sgen_init_pinning ();
+ sgen_init_pinning ();
mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
- pin_from_roots (mono_sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ pin_from_roots (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
/* identify pinned objects */
- mono_sgen_optimize_pin_queue (0);
- mono_sgen_pinning_setup_section (nursery_section);
- mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- mono_sgen_pinning_trim_queue_to_section (nursery_section);
+ sgen_optimize_pin_queue (0);
+ sgen_pinning_setup_section (nursery_section);
+ sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ sgen_pinning_trim_queue_to_section (nursery_section);
TV_GETTIME (atv);
time_minor_pinning += TV_ELAPSED (btv, atv);
- DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", mono_sgen_get_pinned_count (), TV_ELAPSED (btv, atv)));
- DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", mono_sgen_get_pinned_count ()));
+ DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", sgen_get_pinned_count (), TV_ELAPSED (btv, atv)));
+ DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", sgen_get_pinned_count ()));
+ if (whole_heap_check_before_collection)
+ sgen_check_whole_heap ();
if (consistency_check_at_minor_collection)
- mono_sgen_check_consistency ();
+ sgen_check_consistency ();
- mono_sgen_workers_start_all_workers ();
+ sgen_workers_start_all_workers ();
/*
* Perform the sequential part of remembered set scanning.
* This usually involves scanning global information that might later be produced by evacuation.
*/
if (remset.begin_scan_remsets)
- remset.begin_scan_remsets (mono_sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ remset.begin_scan_remsets (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- mono_sgen_workers_start_marking ();
+ sgen_workers_start_marking ();
- frssjd.heap_start = mono_sgen_get_nursery_start ();
+ frssjd.heap_start = sgen_get_nursery_start ();
frssjd.heap_end = nursery_next;
- mono_sgen_workers_enqueue_job (job_finish_remembered_set_scan, &frssjd);
+ sgen_workers_enqueue_job (job_finish_remembered_set_scan, &frssjd);
/* we don't have complete write barrier yet, so we scan all the old generation sections */
TV_GETTIME (btv);
time_minor_scan_remsets += TV_ELAPSED (atv, btv);
DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
- if (!mono_sgen_collection_is_parallel ())
- mono_sgen_drain_gray_stack (&gray_queue, -1);
+ if (!sgen_collection_is_parallel ())
+ sgen_drain_gray_stack (&gray_queue, -1);
if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
report_registered_roots ();
time_minor_scan_pinned += TV_ELAPSED (btv, atv);
/* registered roots, this includes static fields */
- scrrjd_normal.func = mono_sgen_collection_is_parallel () ? major_collector.copy_object : major_collector.nopar_copy_object;
- scrrjd_normal.heap_start = mono_sgen_get_nursery_start ();
+ scrrjd_normal.func = current_object_ops.copy_or_mark_object;
+ scrrjd_normal.heap_start = sgen_get_nursery_start ();
scrrjd_normal.heap_end = nursery_next;
scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
- mono_sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
+ sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
- scrrjd_wbarrier.func = mono_sgen_collection_is_parallel () ? major_collector.copy_object : major_collector.nopar_copy_object;
- scrrjd_wbarrier.heap_start = mono_sgen_get_nursery_start ();
+ scrrjd_wbarrier.func = current_object_ops.copy_or_mark_object;
+ scrrjd_wbarrier.heap_start = sgen_get_nursery_start ();
scrrjd_wbarrier.heap_end = nursery_next;
scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
- mono_sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
+ sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
TV_GETTIME (btv);
time_minor_scan_registered_roots += TV_ELAPSED (atv, btv);
/* thread data */
- stdjd.heap_start = mono_sgen_get_nursery_start ();
+ stdjd.heap_start = sgen_get_nursery_start ();
stdjd.heap_end = nursery_next;
- mono_sgen_workers_enqueue_job (job_scan_thread_data, &stdjd);
+ sgen_workers_enqueue_job (job_scan_thread_data, &stdjd);
TV_GETTIME (atv);
time_minor_scan_thread_data += TV_ELAPSED (btv, atv);
btv = atv;
- if (mono_sgen_collection_is_parallel ()) {
- while (!mono_sgen_gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
- mono_sgen_workers_distribute_gray_queue_sections ();
+ if (sgen_collection_is_parallel ()) {
+ while (!sgen_gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
+ sgen_workers_distribute_gray_queue_sections ();
g_usleep (1000);
}
}
- mono_sgen_workers_join ();
+ sgen_workers_join ();
- if (mono_sgen_collection_is_parallel ())
- g_assert (mono_sgen_gray_object_queue_is_empty (&gray_queue));
+ if (sgen_collection_is_parallel ())
+ g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
/* Scan the list of objects ready for finalization. If */
sfejd_fin_ready.list = fin_ready_list;
- mono_sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
sfejd_critical_fin.list = critical_fin_list;
- mono_sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
- finish_gray_stack (mono_sgen_get_nursery_start (), nursery_next, GENERATION_NURSERY, &gray_queue);
+ finish_gray_stack (sgen_get_nursery_start (), nursery_next, GENERATION_NURSERY, &gray_queue);
TV_GETTIME (atv);
time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
* worker data here instead of earlier when we joined the
* workers.
*/
- mono_sgen_workers_reset_data ();
+ sgen_workers_reset_data ();
if (objects_pinned) {
- mono_sgen_optimize_pin_queue (0);
- mono_sgen_pinning_setup_section (nursery_section);
+ sgen_optimize_pin_queue (0);
+ sgen_pinning_setup_section (nursery_section);
}
/* walk the pin_queue, build up the fragment list of free memory, unmark
* next allocations.
*/
mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
- fragment_total = mono_sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
+ fragment_total = sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
if (!fragment_total)
degraded_mode = 1;
/* Clear TLABs for all threads */
- mono_sgen_clear_tlabs ();
+ sgen_clear_tlabs ();
mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
TV_GETTIME (btv);
DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
if (consistency_check_at_minor_collection)
- mono_sgen_check_major_refs ();
+ sgen_check_major_refs ();
major_collector.finish_nursery_collection ();
TV_GETTIME (all_btv);
- mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
+ gc_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
if (heap_dump_file)
dump_heap ("minor", stat_minor_gcs - 1, NULL);
/* prepare the pin queue for the next collection */
- mono_sgen_finish_pinning ();
+ sgen_finish_pinning ();
if (fin_ready_list || critical_fin_list) {
DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
mono_gc_finalize_notify ();
}
- mono_sgen_pin_stats_reset ();
+ sgen_pin_stats_reset ();
- g_assert (mono_sgen_gray_object_queue_is_empty (&gray_queue));
+ g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
if (remset.finish_minor_collection)
remset.finish_minor_collection ();
binary_protocol_flush_buffers (FALSE);
+ sgen_memgov_minor_collection_end ();
+
/*objects are late pinned because of lack of memory, so a major is a good call*/
- needs_major = need_major_collection (0) || objects_pinned;
+ needs_major = objects_pinned > 0;
current_collection_generation = -1;
objects_pinned = 0;
- return needs_major;
-}
+ MONO_GC_END (GENERATION_NURSERY);
-void
-mono_sgen_collect_nursery_no_lock (size_t requested_size)
-{
- gint64 gc_start_time;
-
- mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
- gc_start_time = mono_100ns_ticks ();
-
- stop_world (0);
- collect_nursery (requested_size);
- restart_world (0);
-
- mono_trace_message (MONO_TRACE_GC, "minor gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
- mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
+ return needs_major;
}
static gboolean
ScanThreadDataJobData stdjd;
ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
+ MONO_GC_BEGIN (GENERATION_OLD);
+
+ current_collection_generation = GENERATION_OLD;
mono_perfcounters->gc_collections1++;
- reset_pinned_from_failed_allocation ();
+ current_object_ops = major_collector.major_ops;
- last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
+ reset_pinned_from_failed_allocation ();
- /*
- * A domain could have been freed, resulting in
- * los_memory_usage being less than last_collection_los_memory_usage.
- */
- last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
- last_collection_old_los_memory_usage = los_memory_usage;
- objects_pinned = 0;
+ sgen_memgov_major_collection_start ();
//count_ref_nonref_objs ();
//consistency_check ();
binary_protocol_collection (GENERATION_OLD);
check_scan_starts ();
- mono_sgen_gray_object_queue_init (&gray_queue);
- mono_sgen_workers_init_distribute_gray_queue ();
+
+ sgen_gray_object_queue_init (&gray_queue);
+ sgen_workers_init_distribute_gray_queue ();
+ sgen_nursery_alloc_prepare_for_major ();
degraded_mode = 0;
DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", stat_major_gcs));
stat_major_gcs++;
- mono_stats.major_gc_count ++;
+ gc_stats.major_gc_count ++;
/* world must be stopped already */
TV_GETTIME (all_atv);
atv = all_atv;
/* Pinning depends on this */
- mono_sgen_clear_nursery_fragments ();
+ sgen_clear_nursery_fragments ();
+
+ if (whole_heap_check_before_collection)
+ sgen_check_whole_heap ();
TV_GETTIME (btv);
time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
- nursery_section->next_data = mono_sgen_get_nursery_end ();
+ nursery_section->next_data = sgen_get_nursery_end ();
/* we should also coalesce scanning from sections close to each other
* and deal with pointers outside of the sections later.
*/
if (major_collector.start_major_collection)
major_collector.start_major_collection ();
+ objects_pinned = 0;
*major_collector.have_swept = FALSE;
- reset_minor_collection_allowance ();
- if (xdomain_checks)
+ if (xdomain_checks) {
+ sgen_clear_nursery_fragments ();
check_for_xdomain_refs ();
+ }
/* Remsets are not useful for a major collection */
remset.prepare_for_major_collection ();
process_dislink_stage_entries ();
TV_GETTIME (atv);
- mono_sgen_init_pinning ();
+ sgen_init_pinning ();
DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- mono_sgen_optimize_pin_queue (0);
+ sgen_optimize_pin_queue (0);
/*
* pin_queue now contains all candidate pointers, sorted and
*/
DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
/* first pass for the sections */
- mono_sgen_find_section_pin_queue_start_end (nursery_section);
+ sgen_find_section_pin_queue_start_end (nursery_section);
major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
/* identify possible pointers to the insize of large objects */
DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
gboolean profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
GCRootReport report;
report.count = 0;
- if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
+ if (sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (bigobj->data));
+ if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
+ MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (bigobj->data);
+ MONO_GC_OBJ_PINNED ((mword)bigobj->data, sgen_safe_object_get_size ((MonoObject*)bigobj->data), vt->klass->name_space, vt->klass->name, GENERATION_OLD);
+ }
pin_object (bigobj->data);
/* FIXME: only enqueue if object has references */
GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
if (G_UNLIKELY (do_pin_stats))
- mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
+ sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %lu from roots\n", bigobj->data, safe_name (bigobj->data), (unsigned long)bigobj->size));
if (profile_roots)
notify_gc_roots (&report);
}
/* second pass for the sections */
- mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
- old_next_pin_slot = mono_sgen_get_pinned_count ();
+ old_next_pin_slot = sgen_get_pinned_count ();
TV_GETTIME (btv);
time_major_pinning += TV_ELAPSED (atv, btv);
- DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", mono_sgen_get_pinned_count (), TV_ELAPSED (atv, btv)));
- DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", mono_sgen_get_pinned_count ()));
+ DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", sgen_get_pinned_count (), TV_ELAPSED (atv, btv)));
+ DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", sgen_get_pinned_count ()));
major_collector.init_to_space ();
main_gc_thread = mono_native_thread_self ();
#endif
- mono_sgen_workers_start_all_workers ();
- mono_sgen_workers_start_marking ();
+ sgen_workers_start_all_workers ();
+ sgen_workers_start_marking ();
if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
report_registered_roots ();
time_major_scan_pinned += TV_ELAPSED (btv, atv);
/* registered roots, this includes static fields */
- scrrjd_normal.func = major_collector.copy_or_mark_object;
+ scrrjd_normal.func = current_object_ops.copy_or_mark_object;
scrrjd_normal.heap_start = heap_start;
scrrjd_normal.heap_end = heap_end;
scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
- mono_sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
+ sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
- scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
+ scrrjd_wbarrier.func = current_object_ops.copy_or_mark_object;
scrrjd_wbarrier.heap_start = heap_start;
scrrjd_wbarrier.heap_end = heap_end;
scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
- mono_sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
+ sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
TV_GETTIME (btv);
time_major_scan_registered_roots += TV_ELAPSED (atv, btv);
/* Threads */
stdjd.heap_start = heap_start;
stdjd.heap_end = heap_end;
- mono_sgen_workers_enqueue_job (job_scan_thread_data, &stdjd);
+ sgen_workers_enqueue_job (job_scan_thread_data, &stdjd);
TV_GETTIME (atv);
time_major_scan_thread_data += TV_ELAPSED (btv, atv);
/* scan the list of objects ready for finalization */
sfejd_fin_ready.list = fin_ready_list;
- mono_sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
sfejd_critical_fin.list = critical_fin_list;
- mono_sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
TV_GETTIME (atv);
time_major_scan_finalized += TV_ELAPSED (btv, atv);
time_major_scan_big_objects += TV_ELAPSED (atv, btv);
if (major_collector.is_parallel) {
- while (!mono_sgen_gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
- mono_sgen_workers_distribute_gray_queue_sections ();
+ while (!sgen_gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
+ sgen_workers_distribute_gray_queue_sections ();
g_usleep (1000);
}
}
- mono_sgen_workers_join ();
+ sgen_workers_join ();
#ifdef SGEN_DEBUG_INTERNAL_ALLOC
main_gc_thread = NULL;
#endif
if (major_collector.is_parallel)
- g_assert (mono_sgen_gray_object_queue_is_empty (&gray_queue));
+ g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
/* all the objects in the heap */
finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
* worker data here instead of earlier when we joined the
* workers.
*/
- mono_sgen_workers_reset_data ();
+ sgen_workers_reset_data ();
if (objects_pinned) {
/*This is slow, but we just OOM'd*/
- mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
- mono_sgen_optimize_pin_queue (0);
- mono_sgen_find_section_pin_queue_start_end (nursery_section);
+ sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
+ sgen_optimize_pin_queue (0);
+ sgen_find_section_pin_queue_start_end (nursery_section);
objects_pinned = 0;
}
reset_heap_boundaries ();
- mono_sgen_update_heap_boundaries ((mword)mono_sgen_get_nursery_start (), (mword)mono_sgen_get_nursery_end ());
+ sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
/* sweep the big objects list */
prevbo = NULL;
for (bigobj = los_object_list; bigobj;) {
if (object_is_pinned (bigobj->data)) {
unpin_object (bigobj->data);
- mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
+ sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
} else {
LOSObject *to_free;
/* not referenced anywhere, so we can free it */
los_object_list = bigobj->next;
to_free = bigobj;
bigobj = bigobj->next;
- mono_sgen_los_free_object (to_free);
+ sgen_los_free_object (to_free);
continue;
}
prevbo = bigobj;
TV_GETTIME (btv);
time_major_free_bigobjs += TV_ELAPSED (atv, btv);
- mono_sgen_los_sweep ();
+ sgen_los_sweep ();
TV_GETTIME (atv);
time_major_los_sweep += TV_ELAPSED (btv, atv);
* pinned objects as we go, memzero() the empty fragments so they are ready for the
* next allocations.
*/
- if (!mono_sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries))
+ if (!sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries))
degraded_mode = 1;
/* Clear TLABs for all threads */
- mono_sgen_clear_tlabs ();
+ sgen_clear_tlabs ();
TV_GETTIME (atv);
time_major_fragment_creation += TV_ELAPSED (btv, atv);
TV_GETTIME (all_btv);
- mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
+ gc_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
if (heap_dump_file)
dump_heap ("major", stat_major_gcs - 1, reason);
/* prepare the pin queue for the next collection */
- mono_sgen_finish_pinning ();
+ sgen_finish_pinning ();
if (fin_ready_list || critical_fin_list) {
DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
mono_gc_finalize_notify ();
}
- mono_sgen_pin_stats_reset ();
-
- g_assert (mono_sgen_gray_object_queue_is_empty (&gray_queue));
+ sgen_pin_stats_reset ();
- try_calculate_minor_collection_allowance (TRUE);
+ g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
- minor_collection_sections_alloced = 0;
- last_collection_los_memory_usage = los_memory_usage;
+ sgen_memgov_major_collection_end ();
+ current_collection_generation = -1;
major_collector.finish_major_collection ();
//consistency_check ();
+ MONO_GC_END (GENERATION_OLD);
+
return bytes_pinned_from_failed_allocation > 0;
}
-static void
-major_collection (const char *reason)
+static gboolean major_do_collection (const char *reason);
+
+/*
+ * Ensure an allocation request for @size will succeed by freeing enough memory.
+ *
+ * LOCKING: The GC lock MUST be held.
+ */
+void
+sgen_ensure_free_space (size_t size)
{
- gboolean need_minor_collection;
+ int generation_to_collect = -1;
+ const char *reason = NULL;
- if (disable_major_collections) {
- collect_nursery (0);
- return;
- }
- major_collection_happened = TRUE;
- current_collection_generation = GENERATION_OLD;
- need_minor_collection = major_do_collection (reason);
- current_collection_generation = -1;
+ if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
+ if (sgen_need_major_collection (size)) {
+ reason = "LOS overflow";
+ generation_to_collect = GENERATION_OLD;
+ }
+ } else {
+ if (degraded_mode) {
+ if (sgen_need_major_collection (size)) {
+ reason = "Degraded mode overflow";
+ generation_to_collect = GENERATION_OLD;
+ }
+ } else if (sgen_need_major_collection (size)) {
+ reason = "Minor allowance";
+ generation_to_collect = GENERATION_OLD;
+ } else {
+ generation_to_collect = GENERATION_NURSERY;
+ reason = "Nursery full";
+ }
+ }
- if (need_minor_collection)
- collect_nursery (0);
+ if (generation_to_collect == -1)
+ return;
+ sgen_perform_collection (size, generation_to_collect, reason);
}
void
-sgen_collect_major_no_lock (const char *reason)
-{
- gint64 gc_start_time;
+sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason)
+{
+ TV_DECLARE (gc_end);
+ GGTimingInfo infos [2];
+ int overflow_generation_to_collect = -1;
+ const char *overflow_reason = NULL;
+
+ memset (infos, 0, sizeof (infos));
+ mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
+
+ infos [0].generation = generation_to_collect;
+ infos [0].reason = reason;
+ infos [0].is_overflow = FALSE;
+ TV_GETTIME (infos [0].total_time);
+ infos [1].generation = -1;
+
+ stop_world (generation_to_collect);
+ //FIXME extract overflow reason
+ if (generation_to_collect == GENERATION_NURSERY) {
+ if (collect_nursery ()) {
+ overflow_generation_to_collect = GENERATION_OLD;
+ overflow_reason = "Minor overflow";
+ }
+ } else {
+ if (major_do_collection (reason)) {
+ overflow_generation_to_collect = GENERATION_NURSERY;
+ overflow_reason = "Excessive pinning";
+ }
+ }
- mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
- gc_start_time = mono_100ns_ticks ();
- stop_world (1);
- major_collection (reason);
- restart_world (1);
- mono_trace_message (MONO_TRACE_GC, "major gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
- mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
-}
+ TV_GETTIME (gc_end);
+ infos [0].total_time = SGEN_TV_ELAPSED (infos [0].total_time, gc_end);
-/*
- * When deciding if it's better to collect or to expand, keep track
- * of how much garbage was reclaimed with the last collection: if it's too
- * little, expand.
- * This is called when we could not allocate a small object.
- */
-static void __attribute__((noinline))
-minor_collect_or_expand_inner (size_t size)
-{
- int do_minor_collection = 1;
- g_assert (nursery_section);
- if (do_minor_collection) {
- gint64 total_gc_time, major_gc_time = 0;
+ if (overflow_generation_to_collect != -1) {
+ mono_profiler_gc_event (MONO_GC_EVENT_START, overflow_generation_to_collect);
+ infos [1].generation = overflow_generation_to_collect;
+ infos [1].reason = overflow_reason;
+ infos [1].is_overflow = TRUE;
+ infos [1].total_time = gc_end;
- mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
- total_gc_time = mono_100ns_ticks ();
+ if (overflow_generation_to_collect == GENERATION_NURSERY)
+ collect_nursery ();
+ else
+ major_do_collection (overflow_reason);
- stop_world (0);
- if (collect_nursery (size)) {
- mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
- major_gc_time = mono_100ns_ticks ();
+ TV_GETTIME (gc_end);
+ infos [1].total_time = SGEN_TV_ELAPSED (infos [1].total_time, gc_end);
- major_collection ("minor overflow");
+ /* keep events symmetric */
+ mono_profiler_gc_event (MONO_GC_EVENT_END, overflow_generation_to_collect);
+ }
- /* keep events symmetric */
- major_gc_time = mono_100ns_ticks () - major_gc_time;
- mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
- }
- DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
- restart_world (0);
+ DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage));
- total_gc_time = mono_100ns_ticks () - total_gc_time;
- if (major_gc_time)
- mono_trace_message (MONO_TRACE_GC, "overflow major gc took %d usecs minor gc took %d usecs", total_gc_time / 10, (total_gc_time - major_gc_time) / 10);
- else
- mono_trace_message (MONO_TRACE_GC, "minor gc took %d usecs", total_gc_time / 10);
-
- /* this also sets the proper pointers for the next allocation */
- if (!mono_sgen_can_alloc_size (size)) {
- /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
- DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, mono_sgen_get_pinned_count ()));
- mono_sgen_dump_pin_queue ();
- degraded_mode = 1;
- }
- mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
+ /* this also sets the proper pointers for the next allocation */
+ if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
+ /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
+ DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", requested_size, sgen_get_pinned_count ()));
+ sgen_dump_pin_queue ();
+ degraded_mode = 1;
}
- //report_internal_mem_usage ();
-}
-void
-mono_sgen_minor_collect_or_expand_inner (size_t size)
-{
- minor_collect_or_expand_inner (size);
+ restart_world (generation_to_collect, infos);
+
+ mono_profiler_gc_event (MONO_GC_EVENT_END, generation_to_collect);
}
/*
report_internal_mem_usage (void)
{
printf ("Internal memory usage:\n");
- mono_sgen_report_internal_mem_usage ();
+ sgen_report_internal_mem_usage ();
printf ("Pinned memory usage:\n");
major_collector.report_pinned_memory_usage ();
}
* Return TRUE if @obj is ready to be finalized.
*/
static inline gboolean
-mono_sgen_nursery_is_object_alive (void *object)
+sgen_is_object_alive (void *object)
{
+ if (ptr_in_nursery (object))
+ return sgen_nursery_is_object_alive (object);
+ /* Oldgen objects can be pinned and forwarded too */
if (SGEN_OBJECT_IS_PINNED (object) || SGEN_OBJECT_IS_FORWARDED (object))
return TRUE;
return major_collector.is_object_live (object);
}
gboolean
-mono_sgen_gc_is_object_ready_for_finalization (void *object)
+sgen_gc_is_object_ready_for_finalization (void *object)
{
- return !mono_sgen_nursery_is_object_alive (object);
+ return !sgen_is_object_alive (object);
}
static gboolean
static void
queue_finalization_entry (MonoObject *obj) {
- FinalizeReadyEntry *entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
+ FinalizeReadyEntry *entry = sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
entry->object = obj;
if (has_critical_finalizer (obj)) {
entry->next = critical_fin_list;
}
}
-static int
+static inline int
object_is_reachable (char *object, char *start, char *end)
{
/*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
if (object < start || object >= end)
return TRUE;
- return mono_sgen_nursery_is_object_alive (object);
+ return sgen_is_object_alive (object);
}
#include "sgen-fin-weak-hash.c"
gboolean
-mono_sgen_object_is_live (void *obj)
+sgen_object_is_live (void *obj)
{
if (ptr_in_nursery (obj))
return object_is_pinned (obj);
ephemeron_list = current->next;
current = current->next;
- mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
+ sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
} else {
prev = current;
current = current->next;
ephemeron_list = current->next;
current = current->next;
- mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
+ sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
continue;
}
if (was_promoted) {
if (ptr_in_nursery (key)) {/*key was not promoted*/
DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
- mono_sgen_add_to_global_remset (&cur->key);
+ sgen_add_to_global_remset (&cur->key);
}
if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
- mono_sgen_add_to_global_remset (&cur->value);
+ sgen_add_to_global_remset (&cur->value);
}
}
}
e = e->next;
e->next = entry->next;
}
- mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
+ sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
entry = NULL;
}
return fin_ready_list || critical_fin_list;
}
-/* Negative value to remove */
-void
-mono_gc_add_memory_pressure (gint64 value)
-{
- /* FIXME: Use interlocked functions */
- LOCK_GC;
- memory_pressure += value;
- UNLOCK_GC;
-}
-
-void
-mono_sgen_register_major_sections_alloced (int num_sections)
-{
- minor_collection_sections_alloced += num_sections;
-}
-
-mword
-mono_sgen_get_minor_collection_allowance (void)
-{
- return minor_collection_allowance;
-}
-
/*
* ######################################################################
* ######## registered roots support
int i;
LOCK_GC;
for (i = 0; i < ROOT_TYPE_NUM; ++i) {
- RootRecord *root = mono_sgen_hash_table_lookup (&roots_hash [i], start);
+ RootRecord *root = sgen_hash_table_lookup (&roots_hash [i], start);
/* we allow changing the size and the descriptor (for thread statics etc) */
if (root) {
size_t old_size = root->end_root - start;
new_root.end_root = start + size;
new_root.root_desc = (mword)descr;
- mono_sgen_hash_table_replace (&roots_hash [root_type], start, &new_root);
+ sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
roots_size += size;
DEBUG (3, fprintf (gc_debug_file, "Added root for range: %p-%p, descr: %p (%d/%d bytes)\n", start, new_root.end_root, descr, (int)size, (int)roots_size));
LOCK_GC;
for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
- if (mono_sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
+ if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
roots_size -= (root.end_root - addr);
}
UNLOCK_GC;
* ######################################################################
*/
-unsigned int mono_sgen_global_stop_count = 0;
+unsigned int sgen_global_stop_count = 0;
#ifdef USE_MONO_CTX
static MonoContext cur_thread_ctx = {0};
#ifdef USE_MONO_CTX
MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
info->monoctx = &cur_thread_ctx;
+ if (gc_callbacks.thread_suspend_func)
+ gc_callbacks.thread_suspend_func (info->runtime_data, NULL, info->monoctx);
#else
ARCH_STORE_REGS (ptr);
info->stopped_regs = ptr;
-#endif
if (gc_callbacks.thread_suspend_func)
- gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
+ gc_callbacks.thread_suspend_func (info->runtime_data, NULL, NULL);
+#endif
}
void
-mono_sgen_fill_thread_info_for_suspend (SgenThreadInfo *info)
+sgen_fill_thread_info_for_suspend (SgenThreadInfo *info)
{
if (remset.fill_thread_info_for_suspend)
remset.fill_thread_info_for_suspend (info);
allocator */
FOREACH_THREAD_SAFE (info) {
gboolean result;
- if (info->skip || info->gc_disabled)
+ if (info->skip || info->gc_disabled || !info->joined_stw)
continue;
if (!info->thread_is_dying && (!info->stack_start || info->in_critical_region ||
is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
- result = mono_sgen_resume_thread (info);
+ result = sgen_resume_thread (info);
if (result) {
++restart_count;
} else {
break;
/* wait for the threads to signal their restart */
- mono_sgen_wait_for_suspend_ack (restart_count);
+ sgen_wait_for_suspend_ack (restart_count);
if (sleep_duration < 0) {
#ifdef HOST_WIN32
gboolean result;
if (info->skip || info->stopped_ip == NULL)
continue;
- result = mono_sgen_suspend_thread (info);
+ result = sgen_suspend_thread (info);
if (result) {
++restarted_count;
num_threads_died += restart_count - restarted_count;
/* wait for the threads to signal their suspension
again */
- mono_sgen_wait_for_suspend_ack (restart_count);
+ sgen_wait_for_suspend_ack (restarted_count);
}
return num_threads_died;
static int
stop_world (int generation)
{
- int count;
+ int count, dead;
/*XXX this is the right stop, thought might not be the nicest place to put it*/
- mono_sgen_process_togglerefs ();
+ sgen_process_togglerefs ();
mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
acquire_gc_locks ();
update_current_thread_stack (&count);
- mono_sgen_global_stop_count++;
- DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", mono_sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ()));
+ sgen_global_stop_count++;
+ DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ()));
TV_GETTIME (stop_world_time);
- count = mono_sgen_thread_handshake (TRUE);
- count -= restart_threads_until_none_in_managed_allocator ();
- g_assert (count >= 0);
+ count = sgen_thread_handshake (TRUE);
+ dead = restart_threads_until_none_in_managed_allocator ();
+ if (count < dead)
+ g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
+ count -= dead;
+
DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
- last_major_num_sections = major_collector.get_num_major_sections ();
- last_los_memory_usage = los_memory_usage;
- major_collection_happened = FALSE;
+ sgen_memgov_collection_start (generation);
+
return count;
}
/* LOCKING: assumes the GC lock is held */
static int
-restart_world (int generation)
+restart_world (int generation, GGTimingInfo *timing)
{
- int count, num_major_sections;
+ int count;
SgenThreadInfo *info;
TV_DECLARE (end_sw);
TV_DECLARE (end_bridge);
stw_bridge_process ();
release_gc_locks ();
- count = mono_sgen_thread_handshake (FALSE);
+ count = sgen_thread_handshake (FALSE);
TV_GETTIME (end_sw);
usec = TV_ELAPSED (stop_world_time, end_sw);
max_pause_usec = MAX (usec, max_pause_usec);
TV_GETTIME (end_bridge);
bridge_usec = TV_ELAPSED (end_sw, end_bridge);
- num_major_sections = major_collector.get_num_major_sections ();
- if (major_collection_happened)
- mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR: %s pause %.2fms, bridge %.2fms major %dK/%dK los %dK/%dK",
- generation ? "" : "(minor overflow)",
- (int)usec / 1000.0f, (int)bridge_usec / 1000.0f,
- major_collector.section_size * num_major_sections / 1024,
- major_collector.section_size * last_major_num_sections / 1024,
- los_memory_usage / 1024,
- last_los_memory_usage / 1024);
- else
- mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR: pause %.2fms, bridge %.2fms promoted %dK major %dK los %dK",
- (int)usec / 1000.0f, (int)bridge_usec / 1000.0f,
- (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
- major_collector.section_size * num_major_sections / 1024,
- los_memory_usage / 1024);
+ if (timing) {
+ timing [0].stw_time = usec;
+ timing [0].bridge_time = bridge_usec;
+ }
+
+ sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
return count;
}
int
-mono_sgen_get_current_collection_generation (void)
+sgen_get_current_collection_generation (void)
{
return current_collection_generation;
}
mono_gc_scan_object (void *obj)
{
UserCopyOrMarkData *data = mono_native_tls_get_value (user_copy_or_mark_key);
-
- if (current_collection_generation == GENERATION_NURSERY) {
- if (mono_sgen_collection_is_parallel ())
- major_collector.copy_object (&obj, data->queue);
- else
- major_collector.nopar_copy_object (&obj, data->queue);
- } else {
- major_collector.copy_or_mark_object (&obj, data->queue);
- }
+ current_object_ops.copy_or_mark_object (&obj, data->queue);
return obj;
}
DEBUG (3, fprintf (gc_debug_file, "GC disabled for thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
continue;
}
- DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %ld, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, mono_sgen_get_pinned_count ()));
+
+ if (!info->joined_stw) {
+ DEBUG (3, fprintf (gc_debug_file, "Skipping thread not seen in STW %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
+ continue;
+ }
+
+ DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, sgen_get_pinned_count ()));
if (!info->thread_is_dying) {
if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
UserCopyOrMarkData data = { NULL, queue };
info->signal = 0;
#endif
info->skip = 0;
+ info->joined_stw = FALSE;
info->doing_handshake = FALSE;
info->thread_is_dying = FALSE;
info->stack_start = NULL;
info->stopped_regs = NULL;
#endif
- mono_sgen_init_tlab_info (info);
+ sgen_init_tlab_info (info);
binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
}
static void
-mono_sgen_wbarrier_cleanup_thread (SgenThreadInfo *p)
+sgen_wbarrier_cleanup_thread (SgenThreadInfo *p)
{
if (remset.cleanup_thread)
remset.cleanup_thread (p);
LOCK_GC;
#else
while (!TRYLOCK_GC) {
- if (!mono_sgen_park_current_thread_if_doing_handshake (p))
+ if (!sgen_park_current_thread_if_doing_handshake (p))
g_usleep (50);
}
+ MONO_GC_LOCKED ();
#endif
binary_protocol_thread_unregister ((gpointer)mono_thread_info_get_tid (p));
gc_callbacks.thread_detach_func (p->runtime_data);
p->runtime_data = NULL;
}
- mono_sgen_wbarrier_cleanup_thread (p);
+ sgen_wbarrier_cleanup_thread (p);
mono_threads_unregister_current_thread (p);
UNLOCK_GC;
{
if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
found_obj = NULL;
- mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+ sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
find_object_for_ptr_callback, ptr, TRUE);
if (found_obj)
return found_obj;
}
found_obj = NULL;
- mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
+ sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
if (found_obj)
return found_obj;
*(void**)ptr = value;
if (ptr_in_nursery (value))
mono_gc_wbarrier_generic_nostore (ptr);
- mono_sgen_dummy_use (value);
+ sgen_dummy_use (value);
}
void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
remset.wbarrier_object_copy (obj, src);
}
+
/*
* ######################################################################
* ######## Other mono public interface functions.
hwi.callback = callback;
hwi.data = data;
- mono_sgen_clear_nursery_fragments ();
- mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
+ sgen_clear_nursery_fragments ();
+ sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
- mono_sgen_los_iterate_objects (walk_references, &hwi);
+ sgen_los_iterate_objects (walk_references, &hwi);
return 0;
}
LOCK_GC;
if (generation > 1)
generation = 1;
- mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
- stop_world (generation);
- if (generation == 0) {
- collect_nursery (0);
- } else {
- major_collection ("user request");
- }
- restart_world (generation);
- mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
+ sgen_perform_collection (0, generation, "user request");
UNLOCK_GC;
}
return tot;
}
-int64_t
-mono_gc_get_heap_size (void)
-{
- return total_alloc;
-}
-
void
mono_gc_disable (void)
{
return MAX_SMALL_OBJ_SIZE;
}
+gboolean
+mono_gc_user_markers_supported (void)
+{
+ return TRUE;
+}
+
gboolean
mono_object_is_alive (MonoObject* o)
{
MonoObject*
mono_gc_weak_link_get (void **link_addr)
{
- if (!*link_addr)
+ /*
+ * We must only load *link_addr once because it might change
+ * under our feet, and REVEAL_POINTER (NULL) results in an
+ * invalid reference.
+ */
+ void *ptr = *link_addr;
+ if (!ptr)
return NULL;
- return (MonoObject*) REVEAL_POINTER (*link_addr);
+ return (MonoObject*) REVEAL_POINTER (ptr);
}
gboolean
LOCK_GC;
- node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
+ node = sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
if (!node) {
UNLOCK_GC;
return FALSE;
char *env;
char **opts, **ptr;
char *major_collector_opt = NULL;
+ char *minor_collector_opt = NULL;
glong max_heap = 0;
glong soft_limit = 0;
int num_workers;
int result;
int dummy;
+ gboolean debug_print_allowance = FALSE;
+ double allowance_ratio = 0, save_target = 0;
do {
result = InterlockedCompareExchange (&gc_initialized, -1, 0);
if (g_str_has_prefix (opt, "major=")) {
opt = strchr (opt, '=') + 1;
major_collector_opt = g_strdup (opt);
+ } else if (g_str_has_prefix (opt, "minor=")) {
+ opt = strchr (opt, '=') + 1;
+ minor_collector_opt = g_strdup (opt);
}
}
} else {
}
init_stats ();
- mono_sgen_init_internal_allocator ();
- mono_sgen_init_nursery_allocator ();
+ sgen_init_internal_allocator ();
+ sgen_init_nursery_allocator ();
- mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
- mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
- mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
+ sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
+ sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
+ sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
- mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
- mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
+ sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
+ sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
#ifndef HAVE_KW_THREAD
mono_native_tls_alloc (&thread_info_key, NULL);
* it inits the small id which is required for hazard pointer
* operations.
*/
- mono_sgen_os_init ();
+ sgen_os_init ();
mono_thread_info_attach (&dummy);
+ if (!minor_collector_opt) {
+ sgen_simple_nursery_init (&sgen_minor_collector);
+ } else {
+ if (!strcmp (minor_collector_opt, "simple"))
+ sgen_simple_nursery_init (&sgen_minor_collector);
+ else if (!strcmp (minor_collector_opt, "split"))
+ sgen_split_nursery_init (&sgen_minor_collector);
+ else {
+ fprintf (stderr, "Unknown minor collector `%s'.\n", minor_collector_opt);
+ exit (1);
+ }
+ }
+
if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
- mono_sgen_marksweep_init (&major_collector);
+ sgen_marksweep_init (&major_collector);
} else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
- mono_sgen_marksweep_fixed_init (&major_collector);
+ sgen_marksweep_fixed_init (&major_collector);
} else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
- mono_sgen_marksweep_par_init (&major_collector);
+ sgen_marksweep_par_init (&major_collector);
} else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
- mono_sgen_marksweep_fixed_par_init (&major_collector);
+ sgen_marksweep_fixed_par_init (&major_collector);
} else if (!strcmp (major_collector_opt, "copying")) {
- mono_sgen_copying_init (&major_collector);
+ sgen_copying_init (&major_collector);
} else {
fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
exit (1);
num_workers = 16;
///* Keep this the default for now */
-#ifdef __APPLE__
+ /* Precise marking is broken on all supported targets. Disable until fixed. */
conservative_stack_mark = TRUE;
-#endif
+
+ sgen_nursery_size = DEFAULT_NURSERY_SIZE;
if (opts) {
for (ptr = opts; *ptr; ++ptr) {
char *opt = *ptr;
if (g_str_has_prefix (opt, "major="))
continue;
+ if (g_str_has_prefix (opt, "minor="))
+ continue;
if (g_str_has_prefix (opt, "wbarrier=")) {
opt = strchr (opt, '=') + 1;
if (strcmp (opt, "remset") == 0) {
fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
exit (1);
}
+ } else {
+ fprintf (stderr, "wbarrier must either be `remset' or `cardtable'.");
+ exit (1);
}
continue;
}
}
if (g_str_has_prefix (opt, "bridge=")) {
opt = strchr (opt, '=') + 1;
- mono_sgen_register_test_bridge_callbacks (g_strdup (opt));
+ sgen_register_test_bridge_callbacks (g_strdup (opt));
continue;
}
#ifdef USER_CONFIG
long val;
opt = strchr (opt, '=') + 1;
if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
- mono_sgen_nursery_size = val;
+ sgen_nursery_size = val;
#ifdef SGEN_ALIGN_NURSERY
if ((val & (val - 1))) {
fprintf (stderr, "The nursery size must be a power of two.\n");
exit (1);
}
- mono_sgen_nursery_bits = 0;
- while (1 << (++ mono_sgen_nursery_bits) != mono_sgen_nursery_size)
+ sgen_nursery_bits = 0;
+ while (1 << (++ sgen_nursery_bits) != sgen_nursery_size)
;
#endif
} else {
continue;
}
#endif
- if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
- fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
- fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
- fprintf (stderr, " soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
- fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
- fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
- fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
- fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
- if (major_collector.print_gc_param_usage)
- major_collector.print_gc_param_usage ();
- exit (1);
+ if (g_str_has_prefix (opt, "save-target-ratio=")) {
+ char *endptr;
+ opt = strchr (opt, '=') + 1;
+ save_target = strtod (opt, &endptr);
+ if (endptr == opt) {
+ fprintf (stderr, "save-target-ratio must be a number.");
+ exit (1);
+ }
+ if (save_target < SGEN_MIN_SAVE_TARGET_RATIO || save_target > SGEN_MAX_SAVE_TARGET_RATIO) {
+ fprintf (stderr, "save-target-ratio must be between %.2f - %.2f.", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
+ exit (1);
+ }
+ continue;
}
+ if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
+ char *endptr;
+ opt = strchr (opt, '=') + 1;
+
+ allowance_ratio = strtod (opt, &endptr);
+ if (endptr == opt) {
+ fprintf (stderr, "save-target-ratio must be a number.");
+ exit (1);
+ }
+ if (allowance_ratio < SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO || allowance_ratio > SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO) {
+ fprintf (stderr, "default-allowance-ratio must be between %.2f - %.2f.", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO);
+ exit (1);
+ }
+ continue;
+ }
+
+ if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
+ continue;
+
+ if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
+ continue;
+
+ fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
+ fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
+ fprintf (stderr, " soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
+ fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
+ fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
+ fprintf (stderr, " minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
+ fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
+ fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
+ if (major_collector.print_gc_param_usage)
+ major_collector.print_gc_param_usage ();
+ if (sgen_minor_collector.print_gc_param_usage)
+ sgen_minor_collector.print_gc_param_usage ();
+ fprintf (stderr, " Experimental options:\n");
+ fprintf (stderr, " save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
+ 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);
+ exit (1);
}
g_strfreev (opts);
}
if (major_collector.is_parallel)
- mono_sgen_workers_init (num_workers);
+ sgen_workers_init (num_workers);
if (major_collector_opt)
g_free (major_collector_opt);
- nursery_size = DEFAULT_NURSERY_SIZE;
- minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
- init_heap_size_limits (max_heap, soft_limit);
+ if (minor_collector_opt)
+ g_free (minor_collector_opt);
alloc_nursery ();
if (opt [0] == ':')
opt++;
if (opt [0]) {
+#ifdef HOST_WIN32
+ char *rf = g_strdup_printf ("%s.%d", opt, GetCurrentProcessId ());
+#else
char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
+#endif
gc_debug_file = fopen (rf, "wb");
if (!gc_debug_file)
gc_debug_file = stderr;
debug_print_allowance = TRUE;
} else if (!strcmp (opt, "print-pinning")) {
do_pin_stats = TRUE;
+ } else if (!strcmp (opt, "verify-before-allocs")) {
+ verify_before_allocs = 1;
+ has_per_allocation_action = TRUE;
+ } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
+ char *arg = strchr (opt, '=') + 1;
+ verify_before_allocs = atoi (arg);
+ has_per_allocation_action = TRUE;
} else if (!strcmp (opt, "collect-before-allocs")) {
collect_before_allocs = 1;
+ has_per_allocation_action = TRUE;
} else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
char *arg = strchr (opt, '=') + 1;
+ has_per_allocation_action = TRUE;
collect_before_allocs = atoi (arg);
+ } else if (!strcmp (opt, "verify-before-collections")) {
+ whole_heap_check_before_collection = TRUE;
} else if (!strcmp (opt, "check-at-minor-collections")) {
consistency_check_at_minor_collection = TRUE;
nursery_clear_policy = CLEAR_AT_GC;
fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
fprintf (stderr, "Valid options are:\n");
fprintf (stderr, " collect-before-allocs[=<n>]\n");
+ fprintf (stderr, " verify-before-allocs[=<n>]\n");
fprintf (stderr, " check-at-minor-collections\n");
+ fprintf (stderr, " verify-before-collections\n");
fprintf (stderr, " disable-minor\n");
fprintf (stderr, " disable-major\n");
fprintf (stderr, " xdomain-checks\n");
if (major_collector.post_param_init)
major_collector.post_param_init ();
+ sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
+
memset (&remset, 0, sizeof (remset));
#ifdef SGEN_HAVE_CARDTABLE
sgen_card_table_init (&remset);
else
#endif
- mono_sgen_ssb_init (&remset);
+ sgen_ssb_init (&remset);
if (remset.register_thread)
remset.register_thread (mono_thread_info_current ());
static gboolean
mono_gc_is_critical_method (MonoMethod *method)
{
- return (method == write_barrier_method || mono_sgen_is_managed_allocator (method));
+ return (method == write_barrier_method || sgen_is_managed_allocator (method));
+}
+
+static gboolean
+sgen_has_critical_method (void)
+{
+ return write_barrier_method || sgen_has_managed_allocator ();
}
static gboolean
if (!ip || !domain)
return FALSE;
+ if (!sgen_has_critical_method ())
+ return FALSE;
ji = mono_jit_info_table_find (domain, ip);
if (!ji)
return FALSE;
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
mono_mb_emit_byte (mb, CEE_SHR_UN);
- mono_mb_emit_icon (mb, (mword)mono_sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
+ mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
// if (!ptr_in_nursery (*ptr)) return;
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
mono_mb_emit_byte (mb, CEE_SHR_UN);
- mono_mb_emit_icon (mb, (mword)mono_sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
+ mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
#else
int label_continue1, label_continue2;
int dereferenced_var;
- // if (ptr < (mono_sgen_get_nursery_start ())) goto continue;
+ // if (ptr < (sgen_get_nursery_start ())) goto continue;
mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ptr (mb, (gpointer) mono_sgen_get_nursery_start ());
+ mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
- // if (ptr >= mono_sgen_get_nursery_end ())) goto continue;
+ // if (ptr >= sgen_get_nursery_end ())) goto continue;
mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ptr (mb, (gpointer) mono_sgen_get_nursery_end ());
+ mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
// Otherwise return
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_stloc (mb, dereferenced_var);
- // if (*ptr < mono_sgen_get_nursery_start ()) return;
+ // if (*ptr < sgen_get_nursery_start ()) return;
mono_mb_emit_ldloc (mb, dereferenced_var);
- mono_mb_emit_ptr (mb, (gpointer) mono_sgen_get_nursery_start ());
+ mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BLT);
- // if (*ptr >= mono_sgen_get_nursery_end ()) return;
+ // if (*ptr >= sgen_get_nursery_end ()) return;
mono_mb_emit_ldloc (mb, dereferenced_var);
- mono_mb_emit_ptr (mb, (gpointer) mono_sgen_get_nursery_end ());
+ mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
nursery_check_return_labels [2] = mono_mb_emit_branch (mb, CEE_BGE);
#endif
}
}
void
-mono_sgen_debug_printf (int level, const char *format, ...)
+sgen_debug_printf (int level, const char *format, ...)
{
va_list ap;
}
FILE*
-mono_sgen_get_logfile (void)
+sgen_get_logfile (void)
{
return gc_debug_file;
}
#endif
NurseryClearPolicy
-mono_sgen_get_nursery_clear_policy (void)
+sgen_get_nursery_clear_policy (void)
{
return nursery_clear_policy;
}
MonoVTable*
-mono_sgen_get_array_fill_vtable (void)
+sgen_get_array_fill_vtable (void)
{
if (!array_fill_vtable) {
static MonoClass klass;
}
void
-mono_sgen_gc_lock (void)
+sgen_gc_lock (void)
{
LOCK_GC;
}
void
-mono_sgen_gc_unlock (void)
+sgen_gc_unlock (void)
{
UNLOCK_GC;
}
}
SgenMajorCollector*
-mono_sgen_get_major_collector (void)
+sgen_get_major_collector (void)
{
return &major_collector;
}
}
SgenRemeberedSet*
-mono_sgen_get_remset (void)
+sgen_get_remset (void)
{
return &remset;
}
guint
mono_gc_get_vtable_bits (MonoClass *class)
{
- if (mono_sgen_need_bridge_processing () && mono_sgen_is_bridge_class (class))
+ if (sgen_need_bridge_processing () && sgen_is_bridge_class (class))
return SGEN_GC_BIT_BRIDGE_OBJECT;
return 0;
}
+void
+mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
+{
+ // FIXME:
+}
+
+
+void
+sgen_check_whole_heap_stw (void)
+{
+ stop_world (0);
+ sgen_clear_nursery_fragments ();
+ sgen_check_whole_heap ();
+ restart_world (0, NULL);
+}
+
#endif /* HAVE_SGEN_GC */