Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mono / metadata / 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  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License 2.0 as published by the Free Software Foundation;
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License 2.0 along with this library; if not, write to the Free
24  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  */
26
27 #include "config.h"
28 #ifdef HAVE_SGEN_GC
29
30 #include "metadata/sgen-gc.h"
31 #include "metadata/sgen-memory-governor.h"
32 #include "metadata/mono-gc.h"
33
34 #include "utils/mono-counters.h"
35 #include "utils/mono-mmap.h"
36 #include "utils/mono-logger-internal.h"
37 #include "utils/dtrace.h"
38
39 #define MIN_MINOR_COLLECTION_ALLOWANCE  ((mword)(DEFAULT_NURSERY_SIZE * default_allowance_nursery_size_ratio))
40
41 /*Heap limits and allocation knobs*/
42 static mword max_heap_size = ((mword)0)- ((mword)1);
43 static mword soft_heap_limit = ((mword)0) - ((mword)1);
44
45 static double default_allowance_nursery_size_ratio = SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO;
46 static double save_target_ratio = SGEN_DEFAULT_SAVE_TARGET_RATIO;
47
48 /**/
49 static mword allocated_heap;
50 static mword total_alloc = 0;
51
52 /* GC triggers. */
53
54 static gboolean debug_print_allowance = FALSE;
55
56
57 /* use this to tune when to do a major/minor collection */
58 static mword memory_pressure = 0;
59 static mword minor_collection_allowance;
60 static int minor_collection_sections_alloced = 0;
61
62 static int last_major_num_sections = 0;
63 static int last_los_memory_usage = 0;
64
65 static gboolean need_calculate_minor_collection_allowance;
66
67 static int last_collection_old_num_major_sections;
68 static mword last_collection_los_memory_usage = 0;
69 static mword last_collection_old_los_memory_usage;
70 static mword last_collection_los_memory_alloced;
71
72 static mword sgen_memgov_available_free_space (void);
73
74
75 static mword
76 double_to_mword_with_saturation (double value)
77 {
78         if (value >= (double)MWORD_MAX_VALUE)
79                 return MWORD_MAX_VALUE;
80         return (mword)value;
81 }
82
83 /* GC trigger heuristics. */
84
85 static void
86 sgen_memgov_try_calculate_minor_collection_allowance (gboolean overwrite)
87 {
88         int num_major_sections, num_major_sections_saved;
89         mword los_memory_saved, new_major, new_heap_size, save_target, allowance_target;
90
91         if (overwrite)
92                 g_assert (need_calculate_minor_collection_allowance);
93
94         if (!need_calculate_minor_collection_allowance)
95                 return;
96
97         if (!*major_collector.have_swept) {
98                 if (overwrite)
99                         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
100                 return;
101         }
102
103         num_major_sections = major_collector.get_num_major_sections ();
104
105         num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
106         los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
107
108         new_major = num_major_sections * major_collector.section_size;
109         new_heap_size = new_major + last_collection_los_memory_usage;
110
111         save_target = (mword)((new_major + last_collection_los_memory_usage) * SGEN_DEFAULT_SAVE_TARGET_RATIO);
112
113         /*
114          * We aim to allow the allocation of as many sections as is
115          * necessary to reclaim save_target sections in the next
116          * collection.  We assume the collection pattern won't change.
117          * In the last cycle, we had num_major_sections_saved for
118          * minor_collection_sections_alloced.  Assuming things won't
119          * change, this must be the same ratio as save_target for
120          * allowance_target, i.e.
121          *
122          *    num_major_sections_saved            save_target
123          * --------------------------------- == ----------------
124          * minor_collection_sections_alloced    allowance_target
125          *
126          * hence:
127          */
128         allowance_target = double_to_mword_with_saturation ((double)save_target * (double)(minor_collection_sections_alloced * major_collector.section_size + last_collection_los_memory_alloced) / (double)(num_major_sections_saved * major_collector.section_size + los_memory_saved));
129
130         minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
131
132         if (new_heap_size + minor_collection_allowance > soft_heap_limit) {
133                 if (new_heap_size > soft_heap_limit)
134                         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
135                 else
136                         minor_collection_allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
137         }
138
139         if (debug_print_allowance) {
140                 mword old_major = last_collection_old_num_major_sections * major_collector.section_size;
141
142                 SGEN_LOG (1, "Before collection: %td bytes (%td major, %td LOS)",
143                                 old_major + last_collection_old_los_memory_usage, old_major, last_collection_old_los_memory_usage);
144                 SGEN_LOG (1, "After collection: %td bytes (%td major, %td LOS)",
145                                 new_heap_size, new_major, last_collection_los_memory_usage);
146                 SGEN_LOG (1, "Allowance: %td bytes", minor_collection_allowance);
147         }
148
149         if (major_collector.have_computed_minor_collection_allowance)
150                 major_collector.have_computed_minor_collection_allowance ();
151
152         need_calculate_minor_collection_allowance = FALSE;
153 }
154
155
156 gboolean
157 sgen_need_major_collection (mword space_needed)
158 {
159         mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
160         return (space_needed > sgen_memgov_available_free_space ()) ||
161                 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
162 }
163
164 void
165 sgen_memgov_minor_collection_start (void)
166 {
167         sgen_memgov_try_calculate_minor_collection_allowance (FALSE);
168 }
169
170 void
171 sgen_memgov_minor_collection_end (void)
172 {
173 }
174
175 void
176 sgen_memgov_major_collection_start (void)
177 {
178         last_collection_old_num_major_sections = sgen_get_major_collector ()->get_num_major_sections ();
179
180         /*
181          * A domain could have been freed, resulting in
182          * los_memory_usage being less than last_collection_los_memory_usage.
183          */
184         last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
185         last_collection_old_los_memory_usage = los_memory_usage;
186
187         need_calculate_minor_collection_allowance = TRUE;
188 }
189
190 void
191 sgen_memgov_major_collection_end (void)
192 {
193         sgen_memgov_try_calculate_minor_collection_allowance (TRUE);
194
195         minor_collection_sections_alloced = 0;
196         last_collection_los_memory_usage = los_memory_usage;
197 }
198
199 void
200 sgen_memgov_collection_start (int generation)
201 {
202         last_major_num_sections = major_collector.get_num_major_sections ();
203         last_los_memory_usage = los_memory_usage;
204 }
205
206 static void
207 log_timming (GGTimingInfo *info)
208 {
209         //unsigned long stw_time, unsigned long bridge_time, gboolean is_overflow
210         int num_major_sections = major_collector.get_num_major_sections ();
211         char full_timing_buff [1024];
212         full_timing_buff [0] = '\0';
213
214         if (!info->is_overflow)
215                 sprintf (full_timing_buff, "total %.2fms, bridge %.2f", info->stw_time / 1000.0f, (int)info->bridge_time / 1000.0f);
216         if (info->generation == GENERATION_OLD)
217                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) pause %.2fms, %s major %dK/%dK los %dK/%dK",
218                         info->is_overflow ? "_OVERFLOW" : "",
219                         info->reason ? info->reason : "",
220                         (int)info->total_time / 1000.0f,
221                         full_timing_buff,
222                         major_collector.section_size * num_major_sections / 1024,
223                         major_collector.section_size * last_major_num_sections / 1024,
224                         los_memory_usage / 1024,
225                         last_los_memory_usage / 1024);
226         else
227                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) pause %.2fms, %s promoted %dK major %dK los %dK",
228                                 info->is_overflow ? "_OVERFLOW" : "",
229                         info->reason ? info->reason : "",
230                         (int)info->total_time / 1000.0f,
231                         full_timing_buff,
232                         (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
233                         major_collector.section_size * num_major_sections / 1024,
234                         los_memory_usage / 1024);       
235 }
236
237 void
238 sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count)
239 {
240         int i;
241         for (i = 0; i < info_count; ++i) {
242                 if (info[i].generation != -1)
243                         log_timming (&info [i]);
244         }
245 }
246
247 void
248 sgen_register_major_sections_alloced (int num_sections)
249 {
250         minor_collection_sections_alloced += num_sections;
251 }
252
253 mword
254 sgen_get_minor_collection_allowance (void)
255 {
256         return minor_collection_allowance;
257 }
258
259 /* Memory pressure API */
260
261 /* Negative value to remove */
262 void
263 mono_gc_add_memory_pressure (gint64 value)
264 {
265         /* FIXME: Use interlocked functions */
266         LOCK_GC;
267         memory_pressure += value;
268         UNLOCK_GC;
269 }
270
271
272 /*
273 Global GC memory tracking.
274 This tracks the total usage of memory by the GC. This includes
275 managed and unmanaged memory.
276 */
277
278 static unsigned long
279 prot_flags_for_activate (int activate)
280 {
281         unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
282         return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
283 }
284
285 void
286 sgen_assert_memory_alloc (void *ptr, const char *assert_description)
287 {
288         if (ptr || !assert_description)
289                 return;
290         fprintf (stderr, "Error: Garbage collector could not allocate memory for %s.\n", assert_description);
291         exit (1);
292 }
293
294 /*
295  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
296  * This must not require any lock.
297  */
298 void*
299 sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description)
300 {
301         void *ptr;
302
303         g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
304
305         ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
306         sgen_assert_memory_alloc (ptr, assert_description);
307         if (ptr) {
308                 SGEN_ATOMIC_ADD_P (total_alloc, size);
309                 if (flags & SGEN_ALLOC_HEAP)
310                         MONO_GC_HEAP_ALLOC ((mword)ptr, size);
311         }
312         return ptr;
313 }
314
315 /* size must be a power of 2 */
316 void*
317 sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description)
318 {
319         void *ptr;
320
321         g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
322
323         ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
324         sgen_assert_memory_alloc (ptr, assert_description);
325         if (ptr) {
326                 SGEN_ATOMIC_ADD_P (total_alloc, size);
327                 if (flags & SGEN_ALLOC_HEAP)
328                         MONO_GC_HEAP_ALLOC ((mword)ptr, size);
329         }
330         return ptr;
331 }
332
333 /*
334  * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
335  */
336 void
337 sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags)
338 {
339         g_assert (!(flags & ~SGEN_ALLOC_HEAP));
340
341         mono_vfree (addr, size);
342         SGEN_ATOMIC_ADD_P (total_alloc, -size);
343         if (flags & SGEN_ALLOC_HEAP)
344                 MONO_GC_HEAP_FREE ((mword)addr, size);
345 }
346
347 int64_t
348 mono_gc_get_heap_size (void)
349 {
350         return total_alloc;
351 }
352
353
354 /*
355 Heap Sizing limits.
356 This limit the max size of the heap. It takes into account
357 only memory actively in use to hold heap objects and not
358 for other parts of the GC.
359  */
360 static mword
361 sgen_memgov_available_free_space (void)
362 {
363         return max_heap_size - MIN (allocated_heap, max_heap_size);
364 }
365
366 void
367 sgen_memgov_release_space (mword size, int space)
368 {
369         SGEN_ATOMIC_ADD_P (allocated_heap, -size);
370 }
371
372 gboolean
373 sgen_memgov_try_alloc_space (mword size, int space)
374 {
375         if (sgen_memgov_available_free_space () < size)
376                 return FALSE;
377
378         SGEN_ATOMIC_ADD_P (allocated_heap, size);
379         mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
380         return TRUE;
381 }
382
383 void
384 sgen_memgov_init (glong max_heap, glong soft_limit, gboolean debug_allowance, double allowance_ratio, double save_target)
385 {
386         if (soft_limit)
387                 soft_heap_limit = soft_limit;
388
389         debug_print_allowance = debug_allowance;
390
391         if (max_heap == 0)
392                 return;
393
394         if (max_heap < soft_limit) {
395                 fprintf (stderr, "max-heap-size must be at least as large as soft-heap-limit.\n");
396                 exit (1);
397         }
398
399         if (max_heap < sgen_nursery_size * 4) {
400                 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
401                 exit (1);
402         }
403         max_heap_size = max_heap - sgen_nursery_size;
404
405         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
406
407         if (allowance_ratio)
408                 default_allowance_nursery_size_ratio = allowance_ratio;
409
410         if (save_target)
411                 save_target_ratio = save_target;
412 }
413
414 #endif