4648d8d761d444cbb8b6ec411d6fff6d3d2ad4da
[mono.git] / mono / sgen / sgen-memory-governor.c
1 /*
2  * sgen-memory-governor.c: When to schedule collections based on
3  * memory usage.
4  *
5  * Author:
6  *      Rodrigo Kumpera (rkumpera@novell.com)
7  *
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
12  *
13  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14  */
15
16 #include "config.h"
17 #ifdef HAVE_SGEN_GC
18
19 #include <stdlib.h>
20
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"
25
26 #define MIN_MINOR_COLLECTION_ALLOWANCE  ((mword)(DEFAULT_NURSERY_SIZE * default_allowance_nursery_size_ratio))
27
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;
32
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);
36
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;
39
40 /**/
41 static mword allocated_heap;
42 static mword total_alloc = 0;
43 static mword total_alloc_max = 0;
44
45 /* GC triggers. */
46
47 static gboolean debug_print_allowance = FALSE;
48
49
50 /* use this to tune when to do a major/minor collection */
51 static mword major_collection_trigger_size;
52
53 static mword major_pre_sweep_heap_size;
54 static mword major_start_heap_size;
55
56 static gboolean need_calculate_minor_collection_allowance;
57
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;
61
62 static mword sgen_memgov_available_free_space (void);
63
64
65 /* GC trigger heuristics. */
66
67 static void
68 sgen_memgov_calculate_minor_collection_allowance (void)
69 {
70         size_t new_major, new_heap_size, allowance_target, allowance;
71         size_t decrease;
72
73         if (!need_calculate_minor_collection_allowance)
74                 return;
75
76         SGEN_ASSERT (0, major_collector.have_swept (), "Can only calculate allowance if heap is swept");
77
78         new_major = major_collector.get_bytes_survived_last_sweep ();
79         new_heap_size = new_major + last_collection_los_memory_usage;
80
81         /*
82          * We allow the heap to grow by one third its current size before we start the next
83          * major collection.
84          */
85         allowance_target = new_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO;
86
87         allowance = MAX (allowance_target, MIN_MINOR_COLLECTION_ALLOWANCE);
88
89         /*
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
92          * ratio.
93          */
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)
96                 decrease = allowance;
97         allowance -= decrease;
98
99         if (new_heap_size + allowance > soft_heap_limit) {
100                 if (new_heap_size > soft_heap_limit)
101                         allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
102                 else
103                         allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
104         }
105
106         /* FIXME: Why is this here? */
107         if (major_collector.free_swept_blocks)
108                 major_collector.free_swept_blocks (allowance);
109
110         major_collection_trigger_size = new_heap_size + allowance;
111
112         need_calculate_minor_collection_allowance = FALSE;
113
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);
118         }
119 }
120
121 static inline size_t
122 get_heap_size (void)
123 {
124         return major_collector.get_num_major_sections () * major_collector.section_size + los_memory_usage;
125 }
126
127 gboolean
128 sgen_need_major_collection (mword space_needed)
129 {
130         size_t heap_size;
131
132         if (sgen_concurrent_collection_in_progress ()) {
133                 heap_size = get_heap_size ();
134
135                 if (heap_size <= major_collection_trigger_size)
136                         return FALSE; 
137
138                 /*
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.
143                  */
144                 if ((heap_size - major_start_heap_size) > major_start_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO)
145                         return TRUE;
146                 return FALSE;
147         }
148
149         /* FIXME: This is a cop-out.  We should have some way of figuring this out. */
150         if (!major_collector.have_swept ())
151                 return FALSE;
152
153         if (space_needed > sgen_memgov_available_free_space ())
154                 return TRUE;
155
156         sgen_memgov_calculate_minor_collection_allowance ();
157
158         heap_size = get_heap_size ();
159
160         return heap_size > major_collection_trigger_size;
161 }
162
163 void
164 sgen_memgov_minor_collection_start (void)
165 {
166         total_promoted_size_start = total_promoted_size;
167 }
168
169 void
170 sgen_memgov_minor_collection_end (void)
171 {
172 }
173
174 void
175 sgen_memgov_major_pre_sweep (void)
176 {
177         if (sgen_concurrent_collection_in_progress ()) {
178                 major_pre_sweep_heap_size = get_heap_size ();
179         } else {
180                 /* We decrease the allowance only in the concurrent case */
181                 major_pre_sweep_heap_size = major_start_heap_size;
182         }
183 }
184
185 void
186 sgen_memgov_major_post_sweep (mword used_slots_size)
187 {
188         mword num_major_sections = major_collector.get_num_major_sections ();
189
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;
194 }
195
196 void
197 sgen_memgov_major_collection_start (void)
198 {
199         need_calculate_minor_collection_allowance = TRUE;
200         major_start_heap_size = get_heap_size ();
201
202         if (debug_print_allowance) {
203                 SGEN_LOG (0, "Starting collection with heap size %ld bytes", (long)major_start_heap_size);
204         }
205 }
206
207 void
208 sgen_memgov_major_collection_end (gboolean forced)
209 {
210         last_collection_los_memory_usage = los_memory_usage;
211
212         total_allocated_major_end = total_allocated_major;
213         if (forced) {
214                 sgen_get_major_collector ()->finish_sweeping ();
215                 sgen_memgov_calculate_minor_collection_allowance ();
216         }
217 }
218
219 void
220 sgen_memgov_collection_start (int generation)
221 {
222 }
223
224 void
225 sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count)
226 {
227         int i;
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);
231         }
232 }
233
234 /*
235 Global GC memory tracking.
236 This tracks the total usage of memory by the GC. This includes
237 managed and unmanaged memory.
238 */
239
240 static unsigned long
241 prot_flags_for_activate (int activate)
242 {
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;
245 }
246
247 void
248 sgen_assert_memory_alloc (void *ptr, size_t requested_size, const char *assert_description)
249 {
250         if (ptr || !assert_description)
251                 return;
252         fprintf (stderr, "Error: Garbage collector could not allocate %zu bytes of memory for %s.\n", requested_size, assert_description);
253         exit (1);
254 }
255
256 /*
257  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
258  * This must not require any lock.
259  */
260 void*
261 sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description)
262 {
263         void *ptr;
264
265         g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
266
267         ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
268         sgen_assert_memory_alloc (ptr, size, assert_description);
269         if (ptr) {
270                 SGEN_ATOMIC_ADD_P (total_alloc, size);
271                 total_alloc_max = MAX (total_alloc_max, total_alloc);
272         }
273         return ptr;
274 }
275
276 /* size must be a power of 2 */
277 void*
278 sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description)
279 {
280         void *ptr;
281
282         g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
283
284         ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
285         sgen_assert_memory_alloc (ptr, size, assert_description);
286         if (ptr) {
287                 SGEN_ATOMIC_ADD_P (total_alloc, size);
288                 total_alloc_max = MAX (total_alloc_max, total_alloc);
289         }
290         return ptr;
291 }
292
293 /*
294  * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
295  */
296 void
297 sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags)
298 {
299         g_assert (!(flags & ~SGEN_ALLOC_HEAP));
300
301         mono_vfree (addr, size);
302         SGEN_ATOMIC_ADD_P (total_alloc, -(gssize)size);
303         total_alloc_max = MAX (total_alloc_max, total_alloc);
304 }
305
306 size_t
307 sgen_gc_get_total_heap_allocation (void)
308 {
309         return total_alloc;
310 }
311
312
313 /*
314 Heap Sizing limits.
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.
318  */
319 static mword
320 sgen_memgov_available_free_space (void)
321 {
322         return max_heap_size - MIN (allocated_heap, max_heap_size);
323 }
324
325 void
326 sgen_memgov_release_space (mword size, int space)
327 {
328         SGEN_ATOMIC_ADD_P (allocated_heap, -(gssize)size);
329 }
330
331 gboolean
332 sgen_memgov_try_alloc_space (mword size, int space)
333 {
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");
336                 return FALSE;
337         }
338
339         SGEN_ATOMIC_ADD_P (allocated_heap, size);
340         sgen_client_total_allocated_heap_changed (allocated_heap);
341         return TRUE;
342 }
343
344 void
345 sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance, double allowance_ratio, double save_target)
346 {
347         if (soft_limit)
348                 soft_heap_limit = soft_limit;
349
350         debug_print_allowance = debug_allowance;
351         major_collection_trigger_size = MIN_MINOR_COLLECTION_ALLOWANCE;
352
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);
355
356         if (max_heap == 0)
357                 return;
358
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;
362         }
363
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;
367         }
368         max_heap_size = max_heap - sgen_nursery_size;
369
370         if (allowance_ratio)
371                 default_allowance_nursery_size_ratio = allowance_ratio;
372
373         if (save_target)
374                 save_target_ratio = save_target;
375 }
376
377 #endif