#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 */
int gc_debug_level = 0;
FILE* gc_debug_file;
-static gboolean debug_print_allowance = FALSE;
/*
void
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 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
-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
-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 < sgen_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 - sgen_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);
check_for_xdomain_refs ();
}
- 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
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;
collect_bridge_objects (copy_func, start_addr, end_addr, generation, queue);
if (generation == GENERATION_OLD)
collect_bridge_objects (copy_func, sgen_get_nursery_start (), sgen_get_nursery_end (), GENERATION_NURSERY, queue);
- sgen_drain_gray_stack (queue, -1);
}
+ /*
+ 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.
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: %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 > 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)
case GENERATION_OLD:
return major_collector.is_parallel;
default:
- g_assert_not_reached ();
+ g_error ("Invalid current generation %d", current_collection_generation);
}
}
* 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++;
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 ();
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 ();
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)
sgen_check_consistency ();
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);
/* Pinning depends on this */
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);
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 ();
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
new_root.end_root = start + size;
new_root.root_desc = (mword)descr;
- 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));
#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
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))) {
num_threads_died += restart_count - restarted_count;
/* wait for the threads to signal their suspension
again */
- 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*/
sgen_process_togglerefs ();
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 = sgen_thread_handshake (TRUE);
- count -= restart_threads_until_none_in_managed_allocator ();
- g_assert (count >= 0);
+ 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);
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;
}
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;
}
+
+ 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) {
info->signal = 0;
#endif
info->skip = 0;
+ info->joined_stw = FALSE;
info->doing_handshake = FALSE;
info->thread_is_dying = FALSE;
info->stack_start = NULL;
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));
remset.wbarrier_object_copy (obj, src);
}
+
/*
* ######################################################################
* ######## Other mono public interface functions.
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
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);
else {
fprintf (stderr, "Unknown minor collector `%s'.\n", minor_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;
- minor_collection_allowance = MIN_MINOR_COLLECTION_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 (major_collector_opt)
g_free (major_collector_opt);
- 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
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;
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 */