[corlib] Fixed StringBuilder construction bugs in marshalling caused by changes to...
[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 static mword total_alloc_max = 0;
52
53 /* GC triggers. */
54
55 static gboolean debug_print_allowance = FALSE;
56
57
58 /* use this to tune when to do a major/minor collection */
59 static mword memory_pressure = 0;
60 static mword minor_collection_allowance;
61 static mword minor_collection_sections_alloced = 0;
62
63 static mword last_major_num_sections = 0;
64 static mword last_los_memory_usage = 0;
65
66 static gboolean need_calculate_minor_collection_allowance;
67
68 static mword last_collection_old_num_major_sections;
69 static mword last_collection_los_memory_usage = 0;
70 static mword last_collection_old_los_memory_usage;
71 static mword last_collection_los_memory_alloced;
72
73 static mword sgen_memgov_available_free_space (void);
74
75
76 /* GC trigger heuristics. */
77
78 static void
79 sgen_memgov_try_calculate_minor_collection_allowance (gboolean overwrite)
80 {
81         size_t num_major_sections;
82         mword new_major, new_heap_size, allowance_target;
83
84         if (overwrite)
85                 g_assert (need_calculate_minor_collection_allowance);
86
87         if (!need_calculate_minor_collection_allowance)
88                 return;
89
90         if (!*major_collector.have_swept) {
91                 if (overwrite)
92                         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
93                 return;
94         }
95
96         num_major_sections = major_collector.get_num_major_sections ();
97
98         new_major = num_major_sections * major_collector.section_size;
99         new_heap_size = new_major + last_collection_los_memory_usage;
100
101         /*
102          * We allow the heap to grow by one third its current size before we start the next
103          * major collection.
104          */
105         allowance_target = new_heap_size / 3;
106
107         minor_collection_allowance = MAX (allowance_target, MIN_MINOR_COLLECTION_ALLOWANCE);
108
109         if (new_heap_size + minor_collection_allowance > soft_heap_limit) {
110                 if (new_heap_size > soft_heap_limit)
111                         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
112                 else
113                         minor_collection_allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
114         }
115
116         if (debug_print_allowance) {
117                 mword old_major = last_collection_old_num_major_sections * major_collector.section_size;
118
119                 SGEN_LOG (1, "Before collection: %ld bytes (%ld major, %ld LOS)",
120                                   (long)(old_major + last_collection_old_los_memory_usage), (long)old_major, (long)last_collection_old_los_memory_usage);
121                 SGEN_LOG (1, "After collection: %ld bytes (%ld major, %ld LOS)",
122                                   (long)new_heap_size, (long)new_major, (long)last_collection_los_memory_usage);
123                 SGEN_LOG (1, "Allowance: %ld bytes", (long)minor_collection_allowance);
124         }
125
126         if (major_collector.have_computed_minor_collection_allowance)
127                 major_collector.have_computed_minor_collection_allowance ();
128
129         need_calculate_minor_collection_allowance = FALSE;
130 }
131
132
133 gboolean
134 sgen_need_major_collection (mword space_needed)
135 {
136         mword los_alloced;
137         if (sgen_concurrent_collection_in_progress ())
138                 return FALSE;
139         los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
140         return (space_needed > sgen_memgov_available_free_space ()) ||
141                 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
142 }
143
144 void
145 sgen_memgov_minor_collection_start (void)
146 {
147         sgen_memgov_try_calculate_minor_collection_allowance (FALSE);
148 }
149
150 void
151 sgen_memgov_minor_collection_end (void)
152 {
153 }
154
155 void
156 sgen_memgov_major_collection_start (void)
157 {
158         last_collection_old_num_major_sections = sgen_get_major_collector ()->get_num_major_sections ();
159
160         /*
161          * A domain could have been freed, resulting in
162          * los_memory_usage being less than last_collection_los_memory_usage.
163          */
164         last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
165         last_collection_old_los_memory_usage = los_memory_usage;
166
167         need_calculate_minor_collection_allowance = TRUE;
168 }
169
170 void
171 sgen_memgov_major_collection_end (void)
172 {
173         sgen_memgov_try_calculate_minor_collection_allowance (TRUE);
174
175         minor_collection_sections_alloced = 0;
176         last_collection_los_memory_usage = los_memory_usage;
177 }
178
179 void
180 sgen_memgov_collection_start (int generation)
181 {
182         last_major_num_sections = major_collector.get_num_major_sections ();
183         last_los_memory_usage = los_memory_usage;
184 }
185
186 static void
187 log_timming (GGTimingInfo *info)
188 {
189         //unsigned long stw_time, unsigned long bridge_time, gboolean is_overflow
190         mword num_major_sections = major_collector.get_num_major_sections ();
191         char full_timing_buff [1024];
192         full_timing_buff [0] = '\0';
193
194         if (!info->is_overflow)
195                 sprintf (full_timing_buff, "total %.2fms, bridge %.2fms", info->stw_time / 10000.0f, (int)info->bridge_time / 10000.0f);
196         if (info->generation == GENERATION_OLD)
197                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) pause %.2fms, %s major %dK/%dK los %dK/%dK",
198                         info->is_overflow ? "_OVERFLOW" : "",
199                         info->reason ? info->reason : "",
200                         (int)info->total_time / 10000.0f,
201                         full_timing_buff,
202                         major_collector.section_size * num_major_sections / 1024,
203                         major_collector.section_size * last_major_num_sections / 1024,
204                         los_memory_usage / 1024,
205                         last_los_memory_usage / 1024);
206         else
207                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) pause %.2fms, %s promoted %dK major %dK los %dK",
208                                 info->is_overflow ? "_OVERFLOW" : "",
209                         info->reason ? info->reason : "",
210                         (int)info->total_time / 10000.0f,
211                         full_timing_buff,
212                         (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
213                         major_collector.section_size * num_major_sections / 1024,
214                         los_memory_usage / 1024);       
215 }
216
217 void
218 sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count)
219 {
220         int i;
221         for (i = 0; i < info_count; ++i) {
222                 if (info[i].generation != -1)
223                         log_timming (&info [i]);
224         }
225 }
226
227 void
228 sgen_register_major_sections_alloced (size_t num_sections)
229 {
230         minor_collection_sections_alloced += num_sections;
231 }
232
233 mword
234 sgen_get_minor_collection_allowance (void)
235 {
236         return minor_collection_allowance;
237 }
238
239 /* Memory pressure API */
240
241 /* Negative value to remove */
242 void
243 mono_gc_add_memory_pressure (gint64 value)
244 {
245         /* FIXME: Use interlocked functions */
246         LOCK_GC;
247         memory_pressure += value;
248         UNLOCK_GC;
249 }
250
251
252 /*
253 Global GC memory tracking.
254 This tracks the total usage of memory by the GC. This includes
255 managed and unmanaged memory.
256 */
257
258 static unsigned long
259 prot_flags_for_activate (int activate)
260 {
261         unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
262         return prot_flags | MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
263 }
264
265 void
266 sgen_assert_memory_alloc (void *ptr, size_t requested_size, const char *assert_description)
267 {
268         if (ptr || !assert_description)
269                 return;
270         fprintf (stderr, "Error: Garbage collector could not allocate %zu bytes of memory for %s.\n", requested_size, assert_description);
271         exit (1);
272 }
273
274 /*
275  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
276  * This must not require any lock.
277  */
278 void*
279 sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description)
280 {
281         void *ptr;
282
283         g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
284
285         ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
286         sgen_assert_memory_alloc (ptr, size, assert_description);
287         if (ptr) {
288                 SGEN_ATOMIC_ADD_P (total_alloc, size);
289                 if (flags & SGEN_ALLOC_HEAP)
290                         MONO_GC_HEAP_ALLOC ((mword)ptr, size);
291                 total_alloc_max = MAX (total_alloc_max, total_alloc);
292         }
293         return ptr;
294 }
295
296 /* size must be a power of 2 */
297 void*
298 sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description)
299 {
300         void *ptr;
301
302         g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
303
304         ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
305         sgen_assert_memory_alloc (ptr, size, assert_description);
306         if (ptr) {
307                 SGEN_ATOMIC_ADD_P (total_alloc, size);
308                 if (flags & SGEN_ALLOC_HEAP)
309                         MONO_GC_HEAP_ALLOC ((mword)ptr, size);
310                 total_alloc_max = MAX (total_alloc_max, total_alloc);
311         }
312         return ptr;
313 }
314
315 /*
316  * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
317  */
318 void
319 sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags)
320 {
321         g_assert (!(flags & ~SGEN_ALLOC_HEAP));
322
323         mono_vfree (addr, size);
324         SGEN_ATOMIC_ADD_P (total_alloc, -(gssize)size);
325         if (flags & SGEN_ALLOC_HEAP)
326                 MONO_GC_HEAP_FREE ((mword)addr, size);
327         total_alloc_max = MAX (total_alloc_max, total_alloc);
328 }
329
330 int64_t
331 mono_gc_get_heap_size (void)
332 {
333         return total_alloc;
334 }
335
336
337 /*
338 Heap Sizing limits.
339 This limit the max size of the heap. It takes into account
340 only memory actively in use to hold heap objects and not
341 for other parts of the GC.
342  */
343 static mword
344 sgen_memgov_available_free_space (void)
345 {
346         return max_heap_size - MIN (allocated_heap, max_heap_size);
347 }
348
349 void
350 sgen_memgov_release_space (mword size, int space)
351 {
352         SGEN_ATOMIC_ADD_P (allocated_heap, -(gssize)size);
353 }
354
355 gboolean
356 sgen_memgov_try_alloc_space (mword size, int space)
357 {
358         if (sgen_memgov_available_free_space () < size) {
359                 SGEN_ASSERT (4, !sgen_is_worker_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
360                 return FALSE;
361         }
362
363         SGEN_ATOMIC_ADD_P (allocated_heap, size);
364         mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
365         return TRUE;
366 }
367
368 void
369 sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance, double allowance_ratio, double save_target)
370 {
371         if (soft_limit)
372                 soft_heap_limit = soft_limit;
373
374         debug_print_allowance = debug_allowance;
375         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
376
377         mono_counters_register ("Memgov alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, &total_alloc);
378         mono_counters_register ("Memgov max alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_MONOTONIC, &total_alloc_max);
379
380         if (max_heap == 0)
381                 return;
382
383         if (max_heap < soft_limit) {
384                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Setting to minimum.", "`max-heap-size` must be at least as large as `soft-heap-limit`.");
385                 max_heap = soft_limit;
386         }
387
388         if (max_heap < sgen_nursery_size * 4) {
389                 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`.");
390                 max_heap = sgen_nursery_size * 4;
391         }
392         max_heap_size = max_heap - sgen_nursery_size;
393
394         if (allowance_ratio)
395                 default_allowance_nursery_size_ratio = allowance_ratio;
396
397         if (save_target)
398                 save_target_ratio = save_target;
399         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
400 }
401
402 #endif