update readme (#21797)
[mono-project.git] / mono / sgen / sgen-memory-governor.c
blobefd2a35b445f47d4a463c5ad74b9eac837f58e45
1 /**
2 * \file
3 * When to schedule collections based on memory usage.
5 * Author:
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.
16 #include "config.h"
17 #ifdef HAVE_SGEN_GC
19 #include <stdlib.h>
21 #include "mono/sgen/sgen-gc.h"
22 #include "mono/sgen/sgen-memory-governor.h"
23 #include "mono/sgen/sgen-workers.h"
24 #include "mono/sgen/sgen-client.h"
25 #include "mono/utils/memfuncs.h"
28 * The allowance we are targeting is a third of the current heap size. Still, we
29 * allow the heap to grow at least 4 times the nursery size before triggering a
30 * major, to reduce unnecessary collections. We make sure we don't set the minimum
31 * allowance too high when using a soft heap limit.
33 #define MIN_MINOR_COLLECTION_ALLOWANCE (MIN(((mword)(sgen_nursery_size * default_allowance_nursery_size_ratio)), (soft_heap_limit * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO)))
35 static SgenPointerQueue log_entries = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_TEMPORARY);
36 static mono_mutex_t log_entries_mutex;
38 mword sgen_total_promoted_size = 0;
39 mword sgen_total_allocated_major = 0;
40 static mword total_promoted_size_start;
41 static mword total_allocated_major_end;
43 /*Heap limits and allocation knobs*/
44 static mword max_heap_size = ((mword)0)- ((mword)1);
45 static mword soft_heap_limit = ((mword)0) - ((mword)1);
47 static double default_allowance_nursery_size_ratio = SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO;
48 static double save_target_ratio = SGEN_DEFAULT_SAVE_TARGET_RATIO;
50 /**/
51 static mword allocated_heap;
52 static mword total_alloc = 0;
53 static mword total_alloc_max = 0;
55 static SGEN_TV_DECLARE(last_minor_start);
56 static SGEN_TV_DECLARE(last_major_start);
58 /* GC triggers. */
60 static gboolean debug_print_allowance = FALSE;
63 /* use this to tune when to do a major/minor collection */
64 static mword major_collection_trigger_size;
66 static mword major_pre_sweep_heap_size;
67 static mword major_start_heap_size;
69 static gboolean need_calculate_minor_collection_allowance;
71 /* The size of the LOS after the last major collection, after sweeping. */
72 static mword last_collection_los_memory_usage = 0;
73 static mword last_used_slots_size = 0;
75 static mword sgen_memgov_available_free_space (void);
77 /* GC trigger heuristics. */
79 static void
80 sgen_memgov_calculate_minor_collection_allowance (void)
82 size_t new_major, new_heap_size, allowance_target, allowance;
83 size_t decrease;
85 if (!need_calculate_minor_collection_allowance)
86 return;
88 SGEN_ASSERT (0, sgen_major_collector.have_swept (), "Can only calculate allowance if heap is swept");
90 new_major = sgen_major_collector.get_bytes_survived_last_sweep ();
91 new_heap_size = new_major + last_collection_los_memory_usage;
94 * We allow the heap to grow by one third its current size before we start the next
95 * major collection.
97 allowance_target = new_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO;
99 allowance = MAX (allowance_target, MIN_MINOR_COLLECTION_ALLOWANCE);
102 * For the concurrent collector, we decrease the allowance relative to the memory
103 * growth during the M&S phase, survival rate of the collection and the allowance
104 * ratio.
106 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);
107 if (decrease > allowance)
108 decrease = allowance;
109 allowance -= decrease;
111 if (new_heap_size + allowance > soft_heap_limit) {
112 if (new_heap_size > soft_heap_limit)
113 allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
114 else
115 allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
118 /* FIXME: Why is this here? */
119 if (sgen_major_collector.free_swept_blocks)
120 sgen_major_collector.free_swept_blocks (sgen_major_collector.get_num_major_sections () * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO);
122 major_collection_trigger_size = new_heap_size + allowance;
124 need_calculate_minor_collection_allowance = FALSE;
126 if (debug_print_allowance) {
127 SGEN_LOG (0, "Surviving sweep: %ld bytes (%ld major, %ld LOS)", (long)new_heap_size, (long)new_major, (long)last_collection_los_memory_usage);
128 SGEN_LOG (0, "Allowance: %ld bytes", (long)allowance);
129 SGEN_LOG (0, "Trigger size: %ld bytes", (long)major_collection_trigger_size);
133 static size_t
134 get_heap_size (void)
136 return sgen_major_collector.get_num_major_sections () * sgen_major_collector.section_size + sgen_los_memory_usage;
139 gboolean
140 sgen_need_major_collection (mword space_needed, gboolean *forced)
142 size_t heap_size;
144 *forced = FALSE;
146 if (sgen_get_concurrent_collection_in_progress ()) {
147 heap_size = get_heap_size ();
149 if (heap_size <= major_collection_trigger_size)
150 return FALSE;
153 * The more the heap grows, the more we need to decrease the allowance above,
154 * in order to have similar trigger sizes as the synchronous collector.
155 * If the heap grows so much that we would need to have a negative allowance,
156 * we force the finishing of the collection, to avoid increased memory usage.
158 if ((heap_size - major_start_heap_size) > major_start_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO)
159 return TRUE;
160 return FALSE;
163 /* FIXME: This is a cop-out. We should have some way of figuring this out. */
164 if (!sgen_major_collector.have_swept ())
165 return FALSE;
167 if (space_needed > sgen_memgov_available_free_space ())
168 return TRUE;
170 sgen_memgov_calculate_minor_collection_allowance ();
172 heap_size = get_heap_size ();
174 *forced = heap_size > soft_heap_limit;
175 return heap_size > major_collection_trigger_size;
178 void
179 sgen_memgov_minor_collection_start (void)
181 total_promoted_size_start = sgen_total_promoted_size;
182 SGEN_TV_GETTIME (last_minor_start);
185 static void
186 sgen_add_log_entry (SgenLogEntry *log_entry)
188 mono_os_mutex_lock (&log_entries_mutex);
189 sgen_pointer_queue_add (&log_entries, log_entry);
190 mono_os_mutex_unlock (&log_entries_mutex);
193 void
194 sgen_memgov_minor_collection_end (const char *reason, gboolean is_overflow)
196 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
197 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
198 SGEN_TV_DECLARE (current_time);
199 SGEN_TV_GETTIME (current_time);
201 log_entry->type = SGEN_LOG_NURSERY;
202 log_entry->reason = reason;
203 log_entry->is_overflow = is_overflow;
204 log_entry->time = SGEN_TV_ELAPSED (last_minor_start, current_time);
205 log_entry->promoted_size = sgen_total_promoted_size - total_promoted_size_start;
206 log_entry->major_size = sgen_major_collector.get_num_major_sections () * sgen_major_collector.section_size;
207 log_entry->major_size_in_use = last_used_slots_size + sgen_total_allocated_major - total_allocated_major_end;
208 log_entry->los_size = sgen_los_memory_usage_total;
209 log_entry->los_size_in_use = sgen_los_memory_usage;
211 sgen_add_log_entry (log_entry);
215 void
216 sgen_memgov_major_pre_sweep (void)
218 if (sgen_get_concurrent_collection_in_progress ()) {
219 major_pre_sweep_heap_size = get_heap_size ();
220 } else {
221 /* We decrease the allowance only in the concurrent case */
222 major_pre_sweep_heap_size = major_start_heap_size;
226 void
227 sgen_memgov_major_post_sweep (mword used_slots_size)
229 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
230 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
232 log_entry->type = SGEN_LOG_MAJOR_SWEEP_FINISH;
233 log_entry->major_size = sgen_major_collector.get_num_major_sections () * sgen_major_collector.section_size;
234 log_entry->major_size_in_use = used_slots_size + sgen_total_allocated_major - total_allocated_major_end;
236 sgen_add_log_entry (log_entry);
239 sgen_gc_info.heap_size_bytes = sgen_major_collector.get_num_major_sections () * sgen_major_collector.section_size + sgen_los_memory_usage_total;
240 sgen_gc_info.fragmented_bytes = sgen_gc_info.heap_size_bytes - sgen_los_memory_usage - (used_slots_size + sgen_total_allocated_major - total_allocated_major_end);
241 sgen_gc_info.memory_load_bytes = mono_determine_physical_ram_available_size ();
243 last_used_slots_size = used_slots_size;
246 void
247 sgen_memgov_major_collection_start (gboolean concurrent, const char *reason)
249 need_calculate_minor_collection_allowance = TRUE;
250 major_start_heap_size = get_heap_size ();
252 if (debug_print_allowance) {
253 SGEN_LOG (0, "Starting collection with heap size %ld bytes", (long)major_start_heap_size);
255 if (concurrent && mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
256 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
258 log_entry->type = SGEN_LOG_MAJOR_CONC_START;
259 log_entry->reason = reason;
261 sgen_add_log_entry (log_entry);
263 SGEN_TV_GETTIME (last_major_start);
266 void
267 sgen_memgov_major_collection_end (gboolean forced, gboolean concurrent, const char *reason, gboolean is_overflow)
269 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
270 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
271 SGEN_TV_DECLARE (current_time);
272 SGEN_TV_GETTIME (current_time);
274 if (concurrent) {
275 log_entry->type = SGEN_LOG_MAJOR_CONC_FINISH;
276 } else {
277 log_entry->type = SGEN_LOG_MAJOR_SERIAL;
279 log_entry->time = SGEN_TV_ELAPSED (last_major_start, current_time);
280 log_entry->reason = reason;
281 log_entry->is_overflow = is_overflow;
282 log_entry->los_size = sgen_los_memory_usage_total;
283 log_entry->los_size_in_use = sgen_los_memory_usage;
285 sgen_add_log_entry (log_entry);
288 last_collection_los_memory_usage = sgen_los_memory_usage;
289 total_allocated_major_end = sgen_total_allocated_major;
290 if (forced) {
291 sgen_get_major_collector ()->finish_sweeping ();
292 sgen_memgov_calculate_minor_collection_allowance ();
296 void
297 sgen_memgov_collection_start (int generation)
301 static void
302 sgen_output_log_entry (SgenLogEntry *entry, gint64 stw_time, int generation)
304 char full_timing_buff [1024];
305 full_timing_buff [0] = '\0';
307 if (!entry->is_overflow)
308 sprintf (full_timing_buff, "stw %.2fms", stw_time / 10000.0f);
310 switch (entry->type) {
311 case SGEN_LOG_NURSERY:
312 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_GC, "GC_MINOR%s: (%s) time %.2fms, %s promoted %luK major size: %luK in use: %luK los size: %luK in use: %luK",
313 entry->is_overflow ? "_OVERFLOW" : "",
314 entry->reason ? entry->reason : "",
315 entry->time / 10000.0f,
316 (generation == GENERATION_NURSERY) ? full_timing_buff : "",
317 (unsigned long)entry->promoted_size / 1024,
318 (unsigned long)entry->major_size / 1024,
319 (unsigned long)entry->major_size_in_use / 1024,
320 (unsigned long)entry->los_size / 1024,
321 (unsigned long)entry->los_size_in_use / 1024);
322 break;
323 case SGEN_LOG_MAJOR_SERIAL:
324 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_GC, "GC_MAJOR%s: (%s) time %.2fms, %s los size: %luK in use: %luK",
325 entry->is_overflow ? "_OVERFLOW" : "",
326 entry->reason ? entry->reason : "",
327 (int)entry->time / 10000.0f,
328 full_timing_buff,
329 (unsigned long)entry->los_size / 1024,
330 (unsigned long)entry->los_size_in_use / 1024);
331 break;
332 case SGEN_LOG_MAJOR_CONC_START:
333 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_START: (%s)", entry->reason ? entry->reason : "");
334 break;
335 case SGEN_LOG_MAJOR_CONC_FINISH:
336 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_FINISH: (%s) time %.2fms, %s los size: %luK in use: %luK",
337 entry->reason ? entry->reason : "",
338 entry->time / 10000.0f,
339 full_timing_buff,
340 (unsigned long)entry->los_size / 1024,
341 (unsigned long)entry->los_size_in_use / 1024);
342 break;
343 case SGEN_LOG_MAJOR_SWEEP_FINISH:
344 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_GC, "GC_MAJOR_SWEEP: major size: %luK in use: %luK",
345 (unsigned long)entry->major_size / 1024,
346 (unsigned long)entry->major_size_in_use / 1024);
347 break;
348 default:
349 SGEN_ASSERT (0, FALSE, "Invalid log entry type");
350 break;
354 void
355 sgen_memgov_collection_end (int generation, gint64 stw_time)
358 * At this moment the world has been restarted which means we can log all pending entries
359 * without risking deadlocks.
361 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
362 size_t i;
363 SGEN_ASSERT (0, !sgen_is_world_stopped (), "We can't log if the world is stopped");
364 mono_os_mutex_lock (&log_entries_mutex);
365 for (i = 0; i < log_entries.next_slot; i++) {
366 sgen_output_log_entry ((SgenLogEntry*)log_entries.data [i], stw_time, generation);
367 sgen_free_internal (log_entries.data [i], INTERNAL_MEM_LOG_ENTRY);
369 sgen_pointer_queue_clear (&log_entries);
370 mono_os_mutex_unlock (&log_entries_mutex);
375 Global GC memory tracking.
376 This tracks the total usage of memory by the GC. This includes
377 managed and unmanaged memory.
380 static unsigned long
381 prot_flags_for_activate (int activate)
383 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
384 return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
387 void
388 sgen_assert_memory_alloc (void *ptr, size_t requested_size, const char *assert_description)
390 if (ptr || !assert_description)
391 return;
392 fprintf (stderr, "Error: Garbage collector could not allocate %" G_GSIZE_FORMAT "u bytes of memory for %s.\n", requested_size, assert_description);
393 exit (1);
397 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
398 * This must not require any lock.
400 void*
401 sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
403 void *ptr;
405 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
407 ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
408 sgen_assert_memory_alloc (ptr, size, assert_description);
409 if (ptr) {
410 SGEN_ATOMIC_ADD_P (total_alloc, size);
411 total_alloc_max = MAX (total_alloc_max, total_alloc);
413 return ptr;
416 /* size must be a power of 2 */
417 // FIXME: remove assert_description
418 void*
419 sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
421 void *ptr;
423 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
425 ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
426 sgen_assert_memory_alloc (ptr, size, assert_description);
427 if (ptr) {
428 SGEN_ATOMIC_ADD_P (total_alloc, size);
429 total_alloc_max = MAX (total_alloc_max, total_alloc);
431 return ptr;
435 * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
437 void
438 sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags, MonoMemAccountType type)
440 g_assert (!(flags & ~SGEN_ALLOC_HEAP));
442 mono_vfree (addr, size, type);
443 SGEN_ATOMIC_ADD_P (total_alloc, -(gssize)size);
444 total_alloc_max = MAX (total_alloc_max, total_alloc);
447 size_t
448 sgen_gc_get_total_heap_allocation (void)
450 return total_alloc;
455 Heap Sizing limits.
456 This limit the max size of the heap. It takes into account
457 only memory actively in use to hold heap objects and not
458 for other parts of the GC.
460 static mword
461 sgen_memgov_available_free_space (void)
463 return max_heap_size - MIN (allocated_heap, max_heap_size);
466 void
467 sgen_memgov_release_space (mword size, int space)
469 SGEN_ATOMIC_ADD_P (allocated_heap, -(gssize)size);
472 gboolean
473 sgen_memgov_try_alloc_space (mword size, int space)
475 if (sgen_memgov_available_free_space () < size) {
476 SGEN_ASSERT (4, !sgen_workers_is_worker_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
477 return FALSE;
480 SGEN_ATOMIC_ADD_P (allocated_heap, size);
481 sgen_client_total_allocated_heap_changed (allocated_heap);
482 return TRUE;
485 void
486 sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance, double allowance_ratio, double save_target)
488 if (soft_limit)
489 soft_heap_limit = soft_limit;
491 debug_print_allowance = debug_allowance;
492 major_collection_trigger_size = MIN_MINOR_COLLECTION_ALLOWANCE;
494 mono_counters_register ("Memgov alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, (void*)&total_alloc);
495 mono_counters_register ("Memgov max alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_MONOTONIC, (void*)&total_alloc_max);
497 mono_os_mutex_init (&log_entries_mutex);
499 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_LOG_ENTRY, sizeof (SgenLogEntry));
501 if (max_heap == 0) {
502 sgen_gc_info.total_available_memory_bytes = mono_determine_physical_ram_size ();
504 // This threshold is commonly used by software caches to detect when they are approaching the limit of available memory.
505 // In sgen it is not adjusted dynamically, since sgen does not adjust compaction strategies based on a threshold.
506 sgen_gc_info.high_memory_load_threshold_bytes = .9 * sgen_gc_info.total_available_memory_bytes;
508 if (!sgen_gc_info.total_available_memory_bytes) {
509 SGEN_LOG(9, "Warning: Unable to determine physical ram size for GCMemoryInfo");
512 return;
515 if (max_heap < soft_limit) {
516 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Setting to minimum.", "`max-heap-size` must be at least as large as `soft-heap-limit`.");
517 max_heap = soft_limit;
520 if (max_heap < SGEN_DEFAULT_NURSERY_SIZE * 4) {
521 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`.");
522 max_heap = SGEN_DEFAULT_NURSERY_SIZE * 4;
524 max_heap_size = max_heap;
526 sgen_gc_info.total_available_memory_bytes = max_heap;
527 sgen_gc_info.high_memory_load_threshold_bytes = .9 * sgen_gc_info.total_available_memory_bytes;
529 if (allowance_ratio)
530 default_allowance_nursery_size_ratio = allowance_ratio;
532 if (save_target)
533 save_target_ratio = save_target;
536 #endif