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 mword total_promoted_size = 0;
29 mword total_allocated_major = 0;
30 static mword total_promoted_size_start;
31 static mword total_allocated_major_end;
33 /*Heap limits and allocation knobs*/
34 static mword max_heap_size = ((mword)0)- ((mword)1);
35 static mword soft_heap_limit = ((mword)0) - ((mword)1);
37 static double default_allowance_nursery_size_ratio = SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO;
38 static double save_target_ratio = SGEN_DEFAULT_SAVE_TARGET_RATIO;
41 static mword allocated_heap;
42 static mword total_alloc = 0;
43 static mword total_alloc_max = 0;
47 static gboolean debug_print_allowance = FALSE;
50 /* use this to tune when to do a major/minor collection */
51 static mword major_collection_trigger_size;
53 static mword major_pre_sweep_heap_size;
54 static mword major_start_heap_size;
56 static gboolean need_calculate_minor_collection_allowance;
58 /* The size of the LOS after the last major collection, after sweeping. */
59 static mword last_collection_los_memory_usage = 0;
60 static mword last_used_slots_size = 0;
62 static mword sgen_memgov_available_free_space (void);
65 /* GC trigger heuristics. */
68 sgen_memgov_calculate_minor_collection_allowance (void)
70 size_t new_major, new_heap_size, allowance_target, allowance;
73 if (!need_calculate_minor_collection_allowance)
76 SGEN_ASSERT (0, major_collector.have_swept (), "Can only calculate allowance if heap is swept");
78 new_major = major_collector.get_bytes_survived_last_sweep ();
79 new_heap_size = new_major + last_collection_los_memory_usage;
82 * We allow the heap to grow by one third its current size before we start the next
85 allowance_target = new_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO;
87 allowance = MAX (allowance_target, MIN_MINOR_COLLECTION_ALLOWANCE);
90 * For the concurrent collector, we decrease the allowance relative to the memory
91 * growth during the M&S phase, survival rate of the collection and the allowance
94 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);
95 if (decrease > allowance)
97 allowance -= decrease;
99 if (new_heap_size + allowance > soft_heap_limit) {
100 if (new_heap_size > soft_heap_limit)
101 allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
103 allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
106 /* FIXME: Why is this here? */
107 if (major_collector.free_swept_blocks)
108 major_collector.free_swept_blocks (allowance);
110 major_collection_trigger_size = new_heap_size + allowance;
112 need_calculate_minor_collection_allowance = FALSE;
114 if (debug_print_allowance) {
115 SGEN_LOG (0, "Surviving sweep: %ld bytes (%ld major, %ld LOS)", (long)new_heap_size, (long)new_major, (long)last_collection_los_memory_usage);
116 SGEN_LOG (0, "Allowance: %ld bytes", (long)allowance);
117 SGEN_LOG (0, "Trigger size: %ld bytes", (long)major_collection_trigger_size);
124 return major_collector.get_num_major_sections () * major_collector.section_size + los_memory_usage;
128 sgen_need_major_collection (mword space_needed)
132 if (sgen_concurrent_collection_in_progress ()) {
133 heap_size = get_heap_size ();
135 if (heap_size <= major_collection_trigger_size)
139 * The more the heap grows, the more we need to decrease the allowance above,
140 * in order to have similar trigger sizes as the synchronous collector.
141 * If the heap grows so much that we would need to have a negative allowance,
142 * we force the finishing of the collection, to avoid increased memory usage.
144 if ((heap_size - major_start_heap_size) > major_start_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO)
149 /* FIXME: This is a cop-out. We should have some way of figuring this out. */
150 if (!major_collector.have_swept ())
153 if (space_needed > sgen_memgov_available_free_space ())
156 sgen_memgov_calculate_minor_collection_allowance ();
158 heap_size = get_heap_size ();
160 return heap_size > major_collection_trigger_size;
164 sgen_memgov_minor_collection_start (void)
166 total_promoted_size_start = total_promoted_size;
170 sgen_memgov_minor_collection_end (void)
175 sgen_memgov_major_pre_sweep (void)
177 if (sgen_concurrent_collection_in_progress ()) {
178 major_pre_sweep_heap_size = get_heap_size ();
180 /* We decrease the allowance only in the concurrent case */
181 major_pre_sweep_heap_size = major_start_heap_size;
186 sgen_memgov_major_post_sweep (mword used_slots_size)
188 mword num_major_sections = major_collector.get_num_major_sections ();
190 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_SWEEP: major size: %dK in use: %dK",
191 num_major_sections * major_collector.section_size / 1024,
192 (used_slots_size + total_allocated_major - total_allocated_major_end) / 1024);
193 last_used_slots_size = used_slots_size;
197 sgen_memgov_major_collection_start (void)
199 need_calculate_minor_collection_allowance = TRUE;
200 major_start_heap_size = get_heap_size ();
202 if (debug_print_allowance) {
203 SGEN_LOG (0, "Starting collection with heap size %ld bytes", (long)major_start_heap_size);
208 sgen_memgov_major_collection_end (gboolean forced)
210 last_collection_los_memory_usage = los_memory_usage;
212 total_allocated_major_end = total_allocated_major;
214 sgen_get_major_collector ()->finish_sweeping ();
215 sgen_memgov_calculate_minor_collection_allowance ();
220 sgen_memgov_collection_start (int generation)
225 sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count)
228 for (i = 0; i < info_count; ++i) {
229 if (info[i].generation != -1)
230 sgen_client_log_timing (&info [i], total_promoted_size - total_promoted_size_start, last_used_slots_size + total_allocated_major - total_allocated_major_end);
235 Global GC memory tracking.
236 This tracks the total usage of memory by the GC. This includes
237 managed and unmanaged memory.
241 prot_flags_for_activate (int activate)
243 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
244 return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
248 sgen_assert_memory_alloc (void *ptr, size_t requested_size, const char *assert_description)
250 if (ptr || !assert_description)
252 fprintf (stderr, "Error: Garbage collector could not allocate %zu bytes of memory for %s.\n", requested_size, assert_description);
257 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
258 * This must not require any lock.
261 sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description)
265 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
267 ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
268 sgen_assert_memory_alloc (ptr, size, assert_description);
270 SGEN_ATOMIC_ADD_P (total_alloc, size);
271 total_alloc_max = MAX (total_alloc_max, total_alloc);
276 /* size must be a power of 2 */
278 sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description)
282 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
284 ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
285 sgen_assert_memory_alloc (ptr, size, assert_description);
287 SGEN_ATOMIC_ADD_P (total_alloc, size);
288 total_alloc_max = MAX (total_alloc_max, total_alloc);
294 * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
297 sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags)
299 g_assert (!(flags & ~SGEN_ALLOC_HEAP));
301 mono_vfree (addr, size);
302 SGEN_ATOMIC_ADD_P (total_alloc, -(gssize)size);
303 total_alloc_max = MAX (total_alloc_max, total_alloc);
307 sgen_gc_get_total_heap_allocation (void)
315 This limit the max size of the heap. It takes into account
316 only memory actively in use to hold heap objects and not
317 for other parts of the GC.
320 sgen_memgov_available_free_space (void)
322 return max_heap_size - MIN (allocated_heap, max_heap_size);
326 sgen_memgov_release_space (mword size, int space)
328 SGEN_ATOMIC_ADD_P (allocated_heap, -(gssize)size);
332 sgen_memgov_try_alloc_space (mword size, int space)
334 if (sgen_memgov_available_free_space () < size) {
335 SGEN_ASSERT (4, !sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
339 SGEN_ATOMIC_ADD_P (allocated_heap, size);
340 sgen_client_total_allocated_heap_changed (allocated_heap);
345 sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance, double allowance_ratio, double save_target)
348 soft_heap_limit = soft_limit;
350 debug_print_allowance = debug_allowance;
351 major_collection_trigger_size = MIN_MINOR_COLLECTION_ALLOWANCE;
353 mono_counters_register ("Memgov alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, &total_alloc);
354 mono_counters_register ("Memgov max alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_MONOTONIC, &total_alloc_max);
359 if (max_heap < soft_limit) {
360 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Setting to minimum.", "`max-heap-size` must be at least as large as `soft-heap-limit`.");
361 max_heap = soft_limit;
364 if (max_heap < sgen_nursery_size * 4) {
365 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`.");
366 max_heap = sgen_nursery_size * 4;
368 max_heap_size = max_heap - sgen_nursery_size;
371 default_allowance_nursery_size_ratio = allowance_ratio;
374 save_target_ratio = save_target;