/* 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 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);
sgen_memgov_minor_collection_end ();
/*objects are late pinned because of lack of memory, so a major is a good call*/
- needs_major = sgen_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_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);
-}
-
static gboolean
major_do_collection (const char *reason)
{
ScanThreadDataJobData stdjd;
ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
+ current_collection_generation = GENERATION_OLD;
mono_perfcounters->gc_collections1++;
current_object_ops = major_collector.major_ops;
g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
sgen_memgov_major_collection_end ();
+ current_collection_generation = -1;
major_collector.finish_major_collection ();
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;
- }
- 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 (requested_size)) {
+ 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 (0);
+ 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)mono_gc_get_heap_size (), (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);
}
/*
/* LOCKING: assumes the GC lock is held */
static int
-restart_world (int generation)
+restart_world (int generation, GGTimingInfo *timing)
{
int count;
SgenThreadInfo *info;
TV_GETTIME (end_bridge);
bridge_usec = TV_ELAPSED (end_sw, end_bridge);
+
+ if (timing) {
+ timing [0].stw_time = usec;
+ timing [0].bridge_time = bridge_usec;
+ }
- sgen_memgov_collection_end (generation, usec, bridge_usec);
+ sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
return count;
}
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;
}
stop_world (0);
sgen_clear_nursery_fragments ();
sgen_check_whole_heap ();
- restart_world (0);
+ restart_world (0, NULL);
}
#endif /* HAVE_SGEN_GC */
static int last_major_num_sections = 0;
static int last_los_memory_usage = 0;
-static gboolean major_collection_happened = FALSE;
static gboolean need_calculate_minor_collection_allowance;
last_collection_old_los_memory_usage = los_memory_usage;
need_calculate_minor_collection_allowance = TRUE;
- major_collection_happened = TRUE;
}
void
{
last_major_num_sections = major_collector.get_num_major_sections ();
last_los_memory_usage = los_memory_usage;
- major_collection_happened = FALSE;
}
-void
-sgen_memgov_collection_end (int generation, unsigned long pause_time, unsigned long bridge_pause_time)
+static void
+log_timming (GGTimingInfo *info)
{
+ //unsigned long stw_time, unsigned long bridge_time, gboolean is_overflow
int 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)pause_time / 1000.0f, (int)bridge_pause_time / 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);
+ char full_timing_buff [1024];
+ full_timing_buff [0] = '\0';
+
+ if (!info->is_overflow)
+ sprintf (full_timing_buff, "total %.2fms, bridge %.2f", info->stw_time / 1000.0f, (int)info->bridge_time / 1000.0f);
+ if (info->generation == GENERATION_OLD)
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) pause %.2fms, %s major %dK/%dK los %dK/%dK",
+ info->is_overflow ? "_OVERFLOW" : "",
+ info->reason,
+ (int)info->total_time / 1000.0f,
+ full_timing_buff,
+ 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)pause_time / 1000.0f, (int)bridge_pause_time / 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);
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) pause %.2fms, %s promoted %dK major %dK los %dK",
+ info->is_overflow ? "_OVERFLOW" : "",
+ info->reason,
+ (int)info->total_time / 1000.0f,
+ full_timing_buff,
+ (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
+ major_collector.section_size * num_major_sections / 1024,
+ los_memory_usage / 1024);
}
+
+void
+sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count)
+{
+ int i;
+ for (i = 0; i < info_count; ++i) {
+ if (info->generation != -1)
+ log_timming (&info [i]);
+ }
+}
+
void
sgen_register_major_sections_alloced (int num_sections)
{