#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>
int gc_debug_level = 0;
FILE* gc_debug_file;
-static gboolean debug_print_allowance = FALSE;
/*
void
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;
/* 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 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);
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))
}
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
} 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*
-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*
-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 sgen_alloc_os_memory (), returning it to the OS.
- */
-void
-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.
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));
+
#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
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)total_alloc));
+ 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 + sgen_nursery_size;
scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
- section->scan_starts = 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;
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 = sgen_memgov_min_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), sgen_memgov_min_allowance ());
-
- minor_collection_allowance = sgen_memgov_adjust_allowance (minor_collection_allowance, new_heap_size);
-
- if (debug_print_allowance) {
- mword old_major = last_collection_old_num_major_sections * major_collector.section_size;
-
- fprintf (gc_debug_file, "Before collection: %td bytes (%td major, %td LOS)\n",
- old_major + last_collection_old_los_memory_usage, old_major, last_collection_old_los_memory_usage);
- fprintf (gc_debug_file, "After collection: %td bytes (%td major, %td LOS)\n",
- new_heap_size, new_major, last_collection_los_memory_usage);
- fprintf (gc_debug_file, "Allowance: %td 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 > sgen_memgov_available_free_space ()) ||
- minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
-}
-
-gboolean
-sgen_need_major_collection (mword space_needed)
-{
- return need_major_collection (space_needed);
-}
static void
reset_pinned_from_failed_allocation (void)
* 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++;
major_collector.start_nursery_collection ();
- try_calculate_minor_collection_allowance (FALSE);
+ sgen_memgov_minor_collection_start ();
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 ();
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);
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;
-}
-
-void
-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_GC_END (GENERATION_NURSERY);
- 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++;
current_object_ops = major_collector.major_ops;
reset_pinned_from_failed_allocation ();
- last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
-
- /*
- * 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 ();
sgen_gray_object_queue_init (&gray_queue);
sgen_workers_init_distribute_gray_queue ();
- sgen_nursery_alloc_prepare_for_major (reason);
+ 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);
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) {
sgen_clear_nursery_fragments ();
report.count = 0;
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);
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);
g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
- try_calculate_minor_collection_allowance (TRUE);
-
- 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 (!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, sgen_get_pinned_count ()));
- 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
-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);
}
/*
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
-sgen_register_major_sections_alloced (int num_sections)
-{
- minor_collection_sections_alloced += num_sections;
-}
-
-mword
-sgen_get_minor_collection_allowance (void)
-{
- return minor_collection_allowance;
-}
-
/*
* ######################################################################
* ######## registered roots support
#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
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);
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;
}
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));
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)
{
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
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);
conservative_stack_mark = TRUE;
sgen_nursery_size = DEFAULT_NURSERY_SIZE;
- minor_collection_allowance = sgen_memgov_min_allowance ();
if (opts) {
for (ptr = opts; *ptr; ++ptr) {
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;
}
continue;
}
#endif
+ 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;
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 (minor_collector_opt)
g_free (minor_collector_opt);
- sgen_memgov_init (max_heap, soft_limit);
-
alloc_nursery ();
if ((env = getenv ("MONO_GC_DEBUG"))) {
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
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
is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
{
if (!ip || !domain)
return FALSE;
+ if (!sgen_has_critical_method ())
+ return FALSE;
ji = mono_jit_info_table_find (domain, ip);
if (!ji)
return FALSE;
stop_world (0);
sgen_clear_nursery_fragments ();
sgen_check_whole_heap ();
- restart_world (0);
+ restart_world (0, NULL);
}
#endif /* HAVE_SGEN_GC */