[netcore] Implement/fix enum/nullable comparers.
[mono-project.git] / mono / sgen / sgen-memory-governor.c
blobeb6dde977489171b5042de9960ef87d32273c939
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"
27 * The allowance we are targeting is a third of the current heap size. Still, we
28 * allow the heap to grow at least 4 times the nursery size before triggering a
29 * major, to reduce unnecessary collections. We make sure we don't set the minimum
30 * allowance too high when using a soft heap limit.
32 #define MIN_MINOR_COLLECTION_ALLOWANCE (MIN(((mword)(sgen_nursery_size * default_allowance_nursery_size_ratio)), (soft_heap_limit * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO)))
34 static SgenPointerQueue log_entries = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_TEMPORARY);
35 static mono_mutex_t log_entries_mutex;
37 mword sgen_total_promoted_size = 0;
38 mword sgen_total_allocated_major = 0;
39 static mword total_promoted_size_start;
40 static mword total_allocated_major_end;
42 /*Heap limits and allocation knobs*/
43 static mword max_heap_size = ((mword)0)- ((mword)1);
44 static mword soft_heap_limit = ((mword)0) - ((mword)1);
46 static double default_allowance_nursery_size_ratio = SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO;
47 static double save_target_ratio = SGEN_DEFAULT_SAVE_TARGET_RATIO;
49 /**/
50 static mword allocated_heap;
51 static mword total_alloc = 0;
52 static mword total_alloc_max = 0;
54 static SGEN_TV_DECLARE(last_minor_start);
55 static SGEN_TV_DECLARE(last_major_start);
57 /* GC triggers. */
59 static gboolean debug_print_allowance = FALSE;
62 /* use this to tune when to do a major/minor collection */
63 static mword major_collection_trigger_size;
65 static mword major_pre_sweep_heap_size;
66 static mword major_start_heap_size;
68 static gboolean need_calculate_minor_collection_allowance;
70 /* The size of the LOS after the last major collection, after sweeping. */
71 static mword last_collection_los_memory_usage = 0;
72 static mword last_used_slots_size = 0;
74 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 inline 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);
238 last_used_slots_size = used_slots_size;
241 void
242 sgen_memgov_major_collection_start (gboolean concurrent, const char *reason)
244 need_calculate_minor_collection_allowance = TRUE;
245 major_start_heap_size = get_heap_size ();
247 if (debug_print_allowance) {
248 SGEN_LOG (0, "Starting collection with heap size %ld bytes", (long)major_start_heap_size);
250 if (concurrent && mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
251 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
253 log_entry->type = SGEN_LOG_MAJOR_CONC_START;
254 log_entry->reason = reason;
256 sgen_add_log_entry (log_entry);
258 SGEN_TV_GETTIME (last_major_start);
261 void
262 sgen_memgov_major_collection_end (gboolean forced, gboolean concurrent, const char *reason, gboolean is_overflow)
264 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
265 SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
266 SGEN_TV_DECLARE (current_time);
267 SGEN_TV_GETTIME (current_time);
269 if (concurrent) {
270 log_entry->type = SGEN_LOG_MAJOR_CONC_FINISH;
271 } else {
272 log_entry->type = SGEN_LOG_MAJOR_SERIAL;
274 log_entry->time = SGEN_TV_ELAPSED (last_major_start, current_time);
275 log_entry->reason = reason;
276 log_entry->is_overflow = is_overflow;
277 log_entry->los_size = sgen_los_memory_usage_total;
278 log_entry->los_size_in_use = sgen_los_memory_usage;
280 sgen_add_log_entry (log_entry);
283 last_collection_los_memory_usage = sgen_los_memory_usage;
284 total_allocated_major_end = sgen_total_allocated_major;
285 if (forced) {
286 sgen_get_major_collector ()->finish_sweeping ();
287 sgen_memgov_calculate_minor_collection_allowance ();
291 void
292 sgen_memgov_collection_start (int generation)
296 static void
297 sgen_output_log_entry (SgenLogEntry *entry, gint64 stw_time, int generation)
299 char full_timing_buff [1024];
300 full_timing_buff [0] = '\0';
302 if (!entry->is_overflow)
303 sprintf (full_timing_buff, "stw %.2fms", stw_time / 10000.0f);
305 switch (entry->type) {
306 case SGEN_LOG_NURSERY:
307 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) time %.2fms, %s promoted %luK major size: %luK in use: %luK los size: %luK in use: %luK",
308 entry->is_overflow ? "_OVERFLOW" : "",
309 entry->reason ? entry->reason : "",
310 entry->time / 10000.0f,
311 (generation == GENERATION_NURSERY) ? full_timing_buff : "",
312 (unsigned long)entry->promoted_size / 1024,
313 (unsigned long)entry->major_size / 1024,
314 (unsigned long)entry->major_size_in_use / 1024,
315 (unsigned long)entry->los_size / 1024,
316 (unsigned long)entry->los_size_in_use / 1024);
317 break;
318 case SGEN_LOG_MAJOR_SERIAL:
319 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) time %.2fms, %s los size: %luK in use: %luK",
320 entry->is_overflow ? "_OVERFLOW" : "",
321 entry->reason ? entry->reason : "",
322 (int)entry->time / 10000.0f,
323 full_timing_buff,
324 (unsigned long)entry->los_size / 1024,
325 (unsigned long)entry->los_size_in_use / 1024);
326 break;
327 case SGEN_LOG_MAJOR_CONC_START:
328 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_START: (%s)", entry->reason ? entry->reason : "");
329 break;
330 case SGEN_LOG_MAJOR_CONC_FINISH:
331 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_FINISH: (%s) time %.2fms, %s los size: %luK in use: %luK",
332 entry->reason ? entry->reason : "",
333 entry->time / 10000.0f,
334 full_timing_buff,
335 (unsigned long)entry->los_size / 1024,
336 (unsigned long)entry->los_size_in_use / 1024);
337 break;
338 case SGEN_LOG_MAJOR_SWEEP_FINISH:
339 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_SWEEP: major size: %luK in use: %luK",
340 (unsigned long)entry->major_size / 1024,
341 (unsigned long)entry->major_size_in_use / 1024);
342 break;
343 default:
344 SGEN_ASSERT (0, FALSE, "Invalid log entry type");
345 break;
349 void
350 sgen_memgov_collection_end (int generation, gint64 stw_time)
353 * At this moment the world has been restarted which means we can log all pending entries
354 * without risking deadlocks.
356 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
357 size_t i;
358 SGEN_ASSERT (0, !sgen_is_world_stopped (), "We can't log if the world is stopped");
359 mono_os_mutex_lock (&log_entries_mutex);
360 for (i = 0; i < log_entries.next_slot; i++) {
361 sgen_output_log_entry ((SgenLogEntry*)log_entries.data [i], stw_time, generation);
362 sgen_free_internal (log_entries.data [i], INTERNAL_MEM_LOG_ENTRY);
364 sgen_pointer_queue_clear (&log_entries);
365 mono_os_mutex_unlock (&log_entries_mutex);
370 Global GC memory tracking.
371 This tracks the total usage of memory by the GC. This includes
372 managed and unmanaged memory.
375 static unsigned long
376 prot_flags_for_activate (int activate)
378 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
379 return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
382 void
383 sgen_assert_memory_alloc (void *ptr, size_t requested_size, const char *assert_description)
385 if (ptr || !assert_description)
386 return;
387 fprintf (stderr, "Error: Garbage collector could not allocate %zu bytes of memory for %s.\n", requested_size, assert_description);
388 exit (1);
392 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
393 * This must not require any lock.
395 void*
396 sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
398 void *ptr;
400 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
402 ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
403 sgen_assert_memory_alloc (ptr, size, assert_description);
404 if (ptr) {
405 SGEN_ATOMIC_ADD_P (total_alloc, size);
406 total_alloc_max = MAX (total_alloc_max, total_alloc);
408 return ptr;
411 /* size must be a power of 2 */
412 // FIXME: remove assert_description
413 void*
414 sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
416 void *ptr;
418 g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
420 ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
421 sgen_assert_memory_alloc (ptr, size, assert_description);
422 if (ptr) {
423 SGEN_ATOMIC_ADD_P (total_alloc, size);
424 total_alloc_max = MAX (total_alloc_max, total_alloc);
426 return ptr;
430 * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
432 void
433 sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags, MonoMemAccountType type)
435 g_assert (!(flags & ~SGEN_ALLOC_HEAP));
437 mono_vfree (addr, size, type);
438 SGEN_ATOMIC_ADD_P (total_alloc, -(gssize)size);
439 total_alloc_max = MAX (total_alloc_max, total_alloc);
442 size_t
443 sgen_gc_get_total_heap_allocation (void)
445 return total_alloc;
450 Heap Sizing limits.
451 This limit the max size of the heap. It takes into account
452 only memory actively in use to hold heap objects and not
453 for other parts of the GC.
455 static mword
456 sgen_memgov_available_free_space (void)
458 return max_heap_size - MIN (allocated_heap, max_heap_size);
461 void
462 sgen_memgov_release_space (mword size, int space)
464 SGEN_ATOMIC_ADD_P (allocated_heap, -(gssize)size);
467 gboolean
468 sgen_memgov_try_alloc_space (mword size, int space)
470 if (sgen_memgov_available_free_space () < size) {
471 SGEN_ASSERT (4, !sgen_workers_is_worker_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
472 return FALSE;
475 SGEN_ATOMIC_ADD_P (allocated_heap, size);
476 sgen_client_total_allocated_heap_changed (allocated_heap);
477 return TRUE;
480 void
481 sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance, double allowance_ratio, double save_target)
483 if (soft_limit)
484 soft_heap_limit = soft_limit;
486 debug_print_allowance = debug_allowance;
487 major_collection_trigger_size = MIN_MINOR_COLLECTION_ALLOWANCE;
489 mono_counters_register ("Memgov alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, (void*)&total_alloc);
490 mono_counters_register ("Memgov max alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_MONOTONIC, (void*)&total_alloc_max);
492 mono_os_mutex_init (&log_entries_mutex);
494 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_LOG_ENTRY, sizeof (SgenLogEntry));
496 if (max_heap == 0)
497 return;
499 if (max_heap < soft_limit) {
500 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Setting to minimum.", "`max-heap-size` must be at least as large as `soft-heap-limit`.");
501 max_heap = soft_limit;
504 if (max_heap < SGEN_DEFAULT_NURSERY_SIZE * 4) {
505 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`.");
506 max_heap = SGEN_DEFAULT_NURSERY_SIZE * 4;
508 max_heap_size = max_heap - SGEN_DEFAULT_NURSERY_SIZE;
510 if (allowance_ratio)
511 default_allowance_nursery_size_ratio = allowance_ratio;
513 if (save_target)
514 save_target_ratio = save_target;
517 #endif