2 * mempool.c: efficient memory allocation
4 * MonoMemPool is for fast allocation of memory. We free
5 * all memory when the pool is destroyed.
8 * Dietmar Maurer (dietmar@ximian.com)
10 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
11 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
12 * Copyright 2011 Xamarin Inc. (http://www.xamarin.com)
20 #include "mempool-internals.h"
23 * MonoMemPool is for fast allocation of memory. We free
24 * all memory when the pool is destroyed.
28 #define ALIGN_SIZE(s) (((s) + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1))
30 // Size of memory at start of mempool reserved for header
31 #define SIZEOF_MEM_POOL (ALIGN_SIZE (sizeof (MonoMemPool)))
34 #define MONO_MEMPOOL_PAGESIZE 4096
35 #define MONO_MEMPOOL_MINSIZE 256
37 #define MONO_MEMPOOL_PAGESIZE 8192
38 #define MONO_MEMPOOL_MINSIZE 512
41 // The --with-malloc-mempools debug-build flag causes mempools to be allocated in single-element blocks, so tools like Valgrind can run better.
42 #if USE_MALLOC_FOR_MEMPOOLS
43 #define INDIVIDUAL_ALLOCATIONS
44 #define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE 0
46 #define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE MONO_MEMPOOL_PAGESIZE
50 #define G_LIKELY(a) (a)
51 #define G_UNLIKELY(a) (a)
54 // A mempool is a linked list of memory blocks, each of which begins with this header structure.
55 // The initial block in the linked list is special, and tracks additional information.
57 // Next block after this one in linked list
60 // Size of this memory block only
63 // Used in "initial block" only: Beginning of current free space in mempool (may be in some block other than the first one)
66 // Used in "initial block" only: End of current free space in mempool (ie, the first byte following the end of usable space)
70 // Unused: Imposing floating point memory rules on _MonoMemPool's final field ensures proper alignment of whole header struct
73 // Used in "initial block" only: Number of bytes so far allocated (whether used or not) in the whole mempool
78 static long total_bytes_allocated = 0;
83 * Returns: a new memory pool.
86 mono_mempool_new (void)
88 return mono_mempool_new_size (MONO_MEMPOOL_PAGESIZE);
92 * mono_mempool_new_size:
93 * @initial_size: the amount of memory to initially reserve for the memory pool.
95 * Returns: a new memory pool with a specific initial memory reservation.
98 mono_mempool_new_size (int initial_size)
102 #ifdef INDIVIDUAL_ALLOCATIONS
103 // In individual allocation mode, create initial block with zero storage space.
104 initial_size = SIZEOF_MEM_POOL;
106 if (initial_size < MONO_MEMPOOL_MINSIZE)
107 initial_size = MONO_MEMPOOL_MINSIZE;
110 pool = (MonoMemPool *)g_malloc (initial_size);
113 pool->pos = (guint8*)pool + SIZEOF_MEM_POOL; // Start after header
114 pool->end = (guint8*)pool + initial_size; // End at end of allocated space
115 pool->d.allocated = pool->size = initial_size;
116 total_bytes_allocated += initial_size;
121 * mono_mempool_destroy:
122 * @pool: the memory pool to destroy
124 * Free all memory associated with this pool.
127 mono_mempool_destroy (MonoMemPool *pool)
131 total_bytes_allocated -= pool->d.allocated;
142 * mono_mempool_invalidate:
143 * @pool: the memory pool to invalidate
145 * Fill the memory associated with this pool to 0x2a (42). Useful for debugging.
148 mono_mempool_invalidate (MonoMemPool *pool)
155 memset (p, 42, p->size);
161 * mono_mempool_stats:
162 * @pool: the momory pool we need stats for
164 * Print a few stats about the mempool:
165 * - Total memory allocated (malloced) by mem pool
166 * - Number of chunks/blocks memory is allocated in
167 * - How much memory is available to dispense before a new malloc must occur?
170 mono_mempool_stats (MonoMemPool *pool)
174 guint32 still_free = pool->end - pool->pos;
182 g_print ("Mempool %p stats:\n", pool);
183 g_print ("Total mem allocated: %d\n", pool->d.allocated);
184 g_print ("Num chunks: %d\n", count);
185 g_print ("Free memory: %d\n", still_free);
189 #ifdef TRACE_ALLOCATIONS
190 #include <execinfo.h>
191 #include "metadata/appdomain.h"
192 #include "metadata/metadata-internals.h"
194 static mono_mutex_t mempool_tracing_lock;
195 #define BACKTRACE_DEPTH 7
197 mono_backtrace (int size)
199 void *array[BACKTRACE_DEPTH];
202 static gboolean inited;
205 mono_os_mutex_init_recursive (&mempool_tracing_lock);
209 mono_os_mutex_lock (&mempool_tracing_lock);
210 g_print ("Allocating %d bytes\n", size);
211 symbols = backtrace (array, BACKTRACE_DEPTH);
212 names = backtrace_symbols (array, symbols);
213 for (i = 1; i < symbols; ++i) {
214 g_print ("\t%s\n", names [i]);
217 mono_os_mutex_unlock (&mempool_tracing_lock);
223 * mono_mempool_alloc:
224 * @pool: the memory pool to use
225 * @size: size of the memory entity we are trying to allocate
227 * A mempool is growing; give a recommended size for the next block.
228 * Each block in a mempool should be about 150% bigger than the previous one,
229 * or bigger if it is necessary to include the new entity.
231 * Returns: the recommended size.
234 get_next_size (MonoMemPool *pool, int size)
236 int target = pool->next? pool->next->size: pool->size;
237 size += SIZEOF_MEM_POOL;
238 /* increase the size */
239 target += target / 2;
240 while (target < size) {
241 target += target / 2;
243 if (target > MONO_MEMPOOL_PAGESIZE && size <= MONO_MEMPOOL_PAGESIZE)
244 target = MONO_MEMPOOL_PAGESIZE;
249 * mono_mempool_alloc:
250 * @pool: the memory pool to use
251 * @size: size of the memory block
253 * Allocates a new block of memory in @pool.
255 * Returns: the address of a newly allocated memory block.
258 mono_mempool_alloc (MonoMemPool *pool, guint size)
260 gpointer rval = pool->pos; // Return value
262 // Normal case: Just bump up pos pointer and we are done
263 size = ALIGN_SIZE (size);
264 pool->pos = (guint8*)rval + size;
266 #ifdef TRACE_ALLOCATIONS
267 if (pool == mono_get_corlib ()->mempool) {
268 mono_backtrace (size);
272 // If we have just overflowed the current block, we need to back up and try again.
273 if (G_UNLIKELY (pool->pos >= pool->end)) {
274 pool->pos -= size; // Back out
276 // For large objects, allocate the object into its own block.
277 // (In individual allocation mode, the constant will be 0 and this path will always be taken)
278 if (size >= MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE) {
279 guint new_size = SIZEOF_MEM_POOL + size;
280 MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
282 np->next = pool->next;
285 pool->d.allocated += new_size;
286 total_bytes_allocated += new_size;
288 rval = (guint8*)np + SIZEOF_MEM_POOL;
290 // Notice: any unused memory at the end of the old head becomes simply abandoned in this case until the mempool is freed (see Bugzilla #35136)
291 guint new_size = get_next_size (pool, size);
292 MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
294 np->next = pool->next;
297 pool->pos = (guint8*)np + SIZEOF_MEM_POOL;
298 pool->end = (guint8*)np + new_size;
299 pool->d.allocated += new_size;
300 total_bytes_allocated += new_size;
311 * mono_mempool_alloc0:
313 * same as mono_mempool_alloc, but fills memory with zero.
316 mono_mempool_alloc0 (MonoMemPool *pool, guint size)
320 // For the fast path, repeat the first few lines of mono_mempool_alloc
321 size = ALIGN_SIZE (size);
323 pool->pos = (guint8*)rval + size;
325 // If that doesn't work fall back on mono_mempool_alloc to handle new chunk allocation
326 if (G_UNLIKELY (pool->pos >= pool->end)) {
327 rval = mono_mempool_alloc (pool, size);
329 #ifdef TRACE_ALLOCATIONS
330 else if (pool == mono_get_corlib ()->mempool) {
331 mono_backtrace (size);
335 memset (rval, 0, size);
340 * mono_mempool_contains_addr:
342 * Determines whenever ADDR is inside the memory used by the mempool.
345 mono_mempool_contains_addr (MonoMemPool *pool,
348 MonoMemPool *p = pool;
351 if (addr >= (gpointer)p && addr < (gpointer)((guint8*)p + p->size))
360 * mono_mempool_strdup:
362 * Same as strdup, but allocates memory from the mempool.
363 * Returns: a pointer to the newly allocated string data inside the mempool.
366 mono_mempool_strdup (MonoMemPool *pool,
376 res = (char *)mono_mempool_alloc (pool, l + 1);
377 memcpy (res, s, l + 1);
383 * mono_mempool_get_allocated:
385 * Return the amount of memory allocated for this mempool.
388 mono_mempool_get_allocated (MonoMemPool *pool)
390 return pool->d.allocated;
394 * mono_mempool_get_bytes_allocated:
396 * Return the number of bytes currently allocated for mempools.
399 mono_mempool_get_bytes_allocated (void)
401 return total_bytes_allocated;