2 * sgen-memory-governor.c: When to schedule collections based on
6 * Rodrigo Kumpera (rkumpera@novell.com)
8 * Copyright 2001-2003 Ximian, Inc
9 * Copyright 2003-2010 Novell, Inc.
10 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
11 * Copyright (C) 2012 Xamarin Inc
13 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
21 #include "mono/sgen/sgen-gc.h"
22 #include "mono/sgen/sgen-memory-governor.h"
23 #include "mono/sgen/sgen-thread-pool.h"
24 #include "mono/sgen/sgen-client.h"
26 #define MIN_MINOR_COLLECTION_ALLOWANCE ((mword)(DEFAULT_NURSERY_SIZE * default_allowance_nursery_size_ratio))
28 static SgenPointerQueue log_entries = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_TEMPORARY);
29 static MonoCoopMutex log_entries_mutex;
31 mword total_promoted_size = 0;
32 mword total_allocated_major = 0;
33 static mword total_promoted_size_start;
34 static mword total_allocated_major_end;
36 /*Heap limits and allocation knobs*/
37 static mword max_heap_size = ((mword)0)- ((mword)1);
38 static mword soft_heap_limit = ((mword)0) - ((mword)1);
40 static double default_allowance_nursery_size_ratio = SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO;
41 static double save_target_ratio = SGEN_DEFAULT_SAVE_TARGET_RATIO;
44 static mword allocated_heap;
45 static mword total_alloc = 0;
46 static mword total_alloc_max = 0;
48 static SGEN_TV_DECLARE(last_minor_start);
49 static SGEN_TV_DECLARE(last_major_start);
53 static gboolean debug_print_allowance = FALSE;
56 /* use this to tune when to do a major/minor collection */
57 static mword major_collection_trigger_size;
59 static mword major_pre_sweep_heap_size;
60 static mword major_start_heap_size;
62 static gboolean need_calculate_minor_collection_allowance;
64 /* The size of the LOS after the last major collection, after sweeping. */
65 static mword last_collection_los_memory_usage = 0;
66 static mword last_used_slots_size = 0;
68 static mword sgen_memgov_available_free_space (void);
71 /* GC trigger heuristics. */
74 sgen_memgov_calculate_minor_collection_allowance (void)
76 size_t new_major, new_heap_size, allowance_target, allowance;
79 if (!need_calculate_minor_collection_allowance)
82 SGEN_ASSERT (0, major_collector.have_swept (), "Can only calculate allowance if heap is swept");
84 new_major = major_collector.get_bytes_survived_last_sweep ();
85 new_heap_size = new_major + last_collection_los_memory_usage;
88 * We allow the heap to grow by one third its current size before we start the next
91 allowance_target = new_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO;
93 allowance = MAX (allowance_target, MIN_MINOR_COLLECTION_ALLOWANCE);
96 * For the concurrent collector, we decrease the allowance relative to the memory
97 * growth during the M&S phase, survival rate of the collection and the allowance
100 decrease = (major_pre_sweep_heap_size - major_start_heap_size) * ((float)new_heap_size / major_pre_sweep_heap_size) * (SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO + 1);
101 if (decrease > allowance)
102 decrease = allowance;
103 allowance -= decrease;
105 if (new_heap_size + allowance > soft_heap_limit) {
106 if (new_heap_size > soft_heap_limit)
107 allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
109 allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
112 /* FIXME: Why is this here? */
113 if (major_collector.free_swept_blocks)
114 major_collector.free_swept_blocks (major_collector.get_num_major_sections () * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO);
116 major_collection_trigger_size = new_heap_size + allowance;
118 need_calculate_minor_collection_allowance = FALSE;
120 if (debug_print_allowance) {
121 SGEN_LOG (0, "Surviving sweep: %ld bytes (%ld major, %ld LOS)", (long)new_heap_size, (long)new_major, (long)last_collection_los_memory_usage);
122 SGEN_LOG (0, "Allowance: %ld bytes", (long)allowance);
123 SGEN_LOG (0, "Trigger size: %ld bytes", (long)major_collection_trigger_size);
130 return major_collector.get_num_major_sections () * major_collector.section_size + los_memory_usage;
134 sgen_need_major_collection (mword space_needed)
138 if (sgen_concurrent_collection_in_progress ()) {
139 heap_size = get_heap_size ();
141 if (heap_size <= major_collection_trigger_size)
145 * The more the heap grows, the more we need to decrease the allowance above,
146 * in order to have similar trigger sizes as the synchronous collector.
147 * If the heap grows so much that we would need to have a negative allowance,
148 * we force the finishing of the collection, to avoid increased memory usage.
150 if ((heap_size - major_start_heap_size) > major_start_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO)
155 /* FIXME: This is a cop-out. We should have some way of figuring this out. */
156 if (!major_collector.have_swept ())
159 if (space_needed > sgen_memgov_available_free_space ())
162 sgen_memgov_calculate_minor_collection_allowance ();
164 heap_size = get_heap_size ();
166 return heap_size > major_collection_trigger_size;
170 sgen_memgov_minor_collection_start (void)
172 total_promoted_size_start = total_promoted_size;
173 SGEN_TV_GETTIME (last_minor_start);
177 sgen_add_log_entry (SgenLogEntry *log_entry)
179 mono_coop_mutex_lock (&log_entries_mutex);
180 sgen_pointer_queue_add (&log_entries, log_entry);
181 mono_coop_mutex_unlock (&log_entries_mutex);
185 sgen_memgov_minor_collection_end (const char *reason, gboolean is_overflow)
187 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
188 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
189 SGEN_TV_DECLARE (current_time);
190 SGEN_TV_GETTIME (current_time);
192 log_entry->type = SGEN_LOG_NURSERY;
193 log_entry->reason = reason;
194 log_entry->is_overflow = is_overflow;
195 log_entry->time = SGEN_TV_ELAPSED (last_minor_start, current_time);
196 log_entry->promoted_size = total_promoted_size - total_promoted_size_start;
197 log_entry->major_size = major_collector.get_num_major_sections () * major_collector.section_size;
198 log_entry->major_size_in_use = last_used_slots_size + total_allocated_major - total_allocated_major_end;
199 log_entry->los_size = los_memory_usage_total;
200 log_entry->los_size_in_use = los_memory_usage;
202 sgen_add_log_entry (log_entry);
207 sgen_memgov_major_pre_sweep (void)
209 if (sgen_concurrent_collection_in_progress ()) {
210 major_pre_sweep_heap_size = get_heap_size ();
212 /* We decrease the allowance only in the concurrent case */
213 major_pre_sweep_heap_size = major_start_heap_size;
218 sgen_memgov_major_post_sweep (mword used_slots_size)
220 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
221 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
223 log_entry->type = SGEN_LOG_MAJOR_SWEEP_FINISH;
224 log_entry->major_size = major_collector.get_num_major_sections () * major_collector.section_size;
225 log_entry->major_size_in_use = used_slots_size + total_allocated_major - total_allocated_major_end;
227 sgen_add_log_entry (log_entry);
229 last_used_slots_size = used_slots_size;
233 sgen_memgov_major_collection_start (gboolean concurrent, const char *reason)
235 need_calculate_minor_collection_allowance = TRUE;
236 major_start_heap_size = get_heap_size ();
238 if (debug_print_allowance) {
239 SGEN_LOG (0, "Starting collection with heap size %ld bytes", (long)major_start_heap_size);
241 if (concurrent && mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
242 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
244 log_entry->type = SGEN_LOG_MAJOR_CONC_START;
245 log_entry->reason = reason;
247 sgen_add_log_entry (log_entry);
249 SGEN_TV_GETTIME (last_major_start);
253 sgen_memgov_major_collection_end (gboolean forced, gboolean concurrent, const char *reason, gboolean is_overflow)
255 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
256 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
257 SGEN_TV_DECLARE (current_time);
258 SGEN_TV_GETTIME (current_time);
261 log_entry->type = SGEN_LOG_MAJOR_CONC_FINISH;
263 log_entry->type = SGEN_LOG_MAJOR_SERIAL;
265 log_entry->time = SGEN_TV_ELAPSED (last_major_start, current_time);
266 log_entry->reason = reason;
267 log_entry->is_overflow = is_overflow;
268 log_entry->los_size = los_memory_usage_total;
269 log_entry->los_size_in_use = los_memory_usage;
271 sgen_add_log_entry (log_entry);
274 last_collection_los_memory_usage = los_memory_usage;
275 total_allocated_major_end = total_allocated_major;
277 sgen_get_major_collector ()->finish_sweeping ();
278 sgen_memgov_calculate_minor_collection_allowance ();
283 sgen_memgov_collection_start (int generation)
288 sgen_output_log_entry (SgenLogEntry *entry, gint64 stw_time, int generation)
290 char full_timing_buff [1024];
291 full_timing_buff [0] = '\0';
293 if (!entry->is_overflow)
294 sprintf (full_timing_buff, "stw %.2fms", stw_time / 10000.0f);
296 switch (entry->type) {
297 case SGEN_LOG_NURSERY:
298 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) time %.2fms, %s promoted %zdK major size: %zdK in use: %zdK los size: %zdK in use: %zdK",
299 entry->is_overflow ? "_OVERFLOW" : "",
300 entry->reason ? entry->reason : "",
301 entry->time / 10000.0f,
302 (generation == GENERATION_NURSERY) ? full_timing_buff : "",
303 entry->promoted_size / 1024,
304 entry->major_size / 1024,
305 entry->major_size_in_use / 1024,
306 entry->los_size / 1024,
307 entry->los_size_in_use / 1024);
309 case SGEN_LOG_MAJOR_SERIAL:
310 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) time %.2fms, %s los size: %zdK in use: %zdK",
311 entry->is_overflow ? "_OVERFLOW" : "",
312 entry->reason ? entry->reason : "",
313 (int)entry->time / 10000.0f,
315 entry->los_size / 1024,
316 entry->los_size_in_use / 1024);
318 case SGEN_LOG_MAJOR_CONC_START:
319 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_START: (%s)", entry->reason ? entry->reason : "");
321 case SGEN_LOG_MAJOR_CONC_FINISH:
322 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_FINISH: (%s) time %.2fms, %s los size: %zdK in use: %zdK",
323 entry->reason ? entry->reason : "",
324 entry->time / 10000.0f,
326 entry->los_size / 1024,
327 entry->los_size_in_use / 1024);
329 case SGEN_LOG_MAJOR_SWEEP_FINISH:
330 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_SWEEP: major size: %zdK in use: %zdK",
331 entry->major_size / 1024,
332 entry->major_size_in_use / 1024);
335 SGEN_ASSERT (0, FALSE, "Invalid log entry type");
341 sgen_memgov_collection_end (int generation, gint64 stw_time)
344 * At this moment the world has been restarted which means we can log all pending entries
345 * without risking deadlocks.
347 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
349 SGEN_ASSERT (0, !sgen_is_world_stopped (), "We can't log if the world is stopped");
350 mono_coop_mutex_lock (&log_entries_mutex);
351 for (i = 0; i < log_entries.next_slot; i++) {
352 sgen_output_log_entry (log_entries.data [i], stw_time, generation);
353 sgen_free_internal (log_entries.data [i], INTERNAL_MEM_LOG_ENTRY);
355 sgen_pointer_queue_clear (&log_entries);
356 mono_coop_mutex_unlock (&log_entries_mutex);
361 Global GC memory tracking.
362 This tracks the total usage of memory by the GC. This includes
363 managed and unmanaged memory.
367 prot_flags_for_activate (int activate)
369 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
370 return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
374 sgen_assert_memory_alloc (void *ptr, size_t requested_size, const char *assert_description)
376 if (ptr || !assert_description)
378 fprintf (stderr, "Error: Garbage collector could not allocate %zu bytes of memory for %s.\n", requested_size, assert_description);
383 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
384 * This must not require any lock.
387 sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
391 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
393 ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
394 sgen_assert_memory_alloc (ptr, size, assert_description);
396 SGEN_ATOMIC_ADD_P (total_alloc, size);
397 total_alloc_max = MAX (total_alloc_max, total_alloc);
402 /* size must be a power of 2 */
403 // FIXME: remove assert_description
405 sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
409 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
411 ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
412 sgen_assert_memory_alloc (ptr, size, assert_description);
414 SGEN_ATOMIC_ADD_P (total_alloc, size);
415 total_alloc_max = MAX (total_alloc_max, total_alloc);
421 * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
424 sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags, MonoMemAccountType type)
426 g_assert (!(flags & ~SGEN_ALLOC_HEAP));
428 mono_vfree (addr, size, type);
429 SGEN_ATOMIC_ADD_P (total_alloc, -(gssize)size);
430 total_alloc_max = MAX (total_alloc_max, total_alloc);
434 sgen_gc_get_total_heap_allocation (void)
442 This limit the max size of the heap. It takes into account
443 only memory actively in use to hold heap objects and not
444 for other parts of the GC.
447 sgen_memgov_available_free_space (void)
449 return max_heap_size - MIN (allocated_heap, max_heap_size);
453 sgen_memgov_release_space (mword size, int space)
455 SGEN_ATOMIC_ADD_P (allocated_heap, -(gssize)size);
459 sgen_memgov_try_alloc_space (mword size, int space)
461 if (sgen_memgov_available_free_space () < size) {
462 SGEN_ASSERT (4, !sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
466 SGEN_ATOMIC_ADD_P (allocated_heap, size);
467 sgen_client_total_allocated_heap_changed (allocated_heap);
472 sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance, double allowance_ratio, double save_target)
475 soft_heap_limit = soft_limit;
477 debug_print_allowance = debug_allowance;
478 major_collection_trigger_size = MIN_MINOR_COLLECTION_ALLOWANCE;
480 mono_counters_register ("Memgov alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, (void*)&total_alloc);
481 mono_counters_register ("Memgov max alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_MONOTONIC, (void*)&total_alloc_max);
483 mono_coop_mutex_init (&log_entries_mutex);
485 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_LOG_ENTRY, sizeof (SgenLogEntry));
490 if (max_heap < soft_limit) {
491 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Setting to minimum.", "`max-heap-size` must be at least as large as `soft-heap-limit`.");
492 max_heap = soft_limit;
495 if (max_heap < sgen_nursery_size * 4) {
496 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Setting to minimum.", "`max-heap-size` must be at least 4 times as large as `nursery size`.");
497 max_heap = sgen_nursery_size * 4;
499 max_heap_size = max_heap - sgen_nursery_size;
502 default_allowance_nursery_size_ratio = allowance_ratio;
505 save_target_ratio = save_target;