mempool.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /**
  2. * \file
  3. * efficient memory allocation
  4. *
  5. * MonoMemPool is for fast allocation of memory. We free
  6. * all memory when the pool is destroyed.
  7. *
  8. * Author:
  9. * Dietmar Maurer (dietmar@ximian.com)
  10. *
  11. * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  12. * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  13. * Copyright 2011 Xamarin Inc. (http://www.xamarin.com)
  14. * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  15. */
  16. #include <config.h>
  17. #include <glib.h>
  18. #include <string.h>
  19. #include "mempool.h"
  20. #include "mempool-internals.h"
  21. #include "utils/unlocked.h"
  22. /*
  23. * MonoMemPool is for fast allocation of memory. We free
  24. * all memory when the pool is destroyed.
  25. */
  26. #define MEM_ALIGN 8
  27. #define ALIGN_SIZE(s) (((s) + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1))
  28. // Size of memory at start of mempool reserved for header
  29. #define SIZEOF_MEM_POOL (ALIGN_SIZE (sizeof (MonoMemPool)))
  30. #if MONO_SMALL_CONFIG
  31. #define MONO_MEMPOOL_PAGESIZE 4096
  32. #define MONO_MEMPOOL_MINSIZE 256
  33. #else
  34. #define MONO_MEMPOOL_PAGESIZE 8192
  35. #define MONO_MEMPOOL_MINSIZE 512
  36. #endif
  37. // The --with-malloc-mempools debug-build flag causes mempools to be allocated in single-element blocks, so tools like Valgrind can run better.
  38. #if USE_MALLOC_FOR_MEMPOOLS
  39. #define INDIVIDUAL_ALLOCATIONS
  40. #define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE 0
  41. #else
  42. #define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE MONO_MEMPOOL_PAGESIZE
  43. #endif
  44. #ifndef G_LIKELY
  45. #define G_LIKELY(a) (a)
  46. #define G_UNLIKELY(a) (a)
  47. #endif
  48. // A mempool is a linked list of memory blocks, each of which begins with this header structure.
  49. // The initial block in the linked list is special, and tracks additional information.
  50. struct _MonoMemPool {
  51. // Next block after this one in linked list
  52. MonoMemPool *next;
  53. // Size of this memory block only
  54. guint32 size;
  55. // Used in "initial block" only: Beginning of current free space in mempool (may be in some block other than the first one)
  56. guint8 *pos;
  57. // Used in "initial block" only: End of current free space in mempool (ie, the first byte following the end of usable space)
  58. guint8 *end;
  59. union {
  60. // Unused: Imposing floating point memory rules on _MonoMemPool's final field ensures proper alignment of whole header struct
  61. double pad;
  62. // Used in "initial block" only: Number of bytes so far allocated (whether used or not) in the whole mempool
  63. guint32 allocated;
  64. } d;
  65. };
  66. static gint64 total_bytes_allocated = 0;
  67. /**
  68. * mono_mempool_new:
  69. *
  70. * Returns: a new memory pool.
  71. */
  72. MonoMemPool *
  73. mono_mempool_new (void)
  74. {
  75. return mono_mempool_new_size (MONO_MEMPOOL_PAGESIZE);
  76. }
  77. /**
  78. * mono_mempool_new_size:
  79. * \param initial_size the amount of memory to initially reserve for the memory pool.
  80. * \returns a new memory pool with a specific initial memory reservation.
  81. */
  82. MonoMemPool *
  83. mono_mempool_new_size (int initial_size)
  84. {
  85. MonoMemPool *pool;
  86. #ifdef INDIVIDUAL_ALLOCATIONS
  87. // In individual allocation mode, create initial block with zero storage space.
  88. initial_size = SIZEOF_MEM_POOL;
  89. #else
  90. if (initial_size < MONO_MEMPOOL_MINSIZE)
  91. initial_size = MONO_MEMPOOL_MINSIZE;
  92. #endif
  93. pool = (MonoMemPool *)g_malloc (initial_size);
  94. pool->next = NULL;
  95. pool->pos = (guint8*)pool + SIZEOF_MEM_POOL; // Start after header
  96. pool->end = (guint8*)pool + initial_size; // End at end of allocated space
  97. pool->d.allocated = pool->size = initial_size;
  98. UnlockedAdd64 (&total_bytes_allocated, initial_size);
  99. return pool;
  100. }
  101. /**
  102. * mono_mempool_destroy:
  103. * \param pool the memory pool to destroy
  104. *
  105. * Free all memory associated with this pool.
  106. */
  107. void
  108. mono_mempool_destroy (MonoMemPool *pool)
  109. {
  110. MonoMemPool *p, *n;
  111. UnlockedSubtract64 (&total_bytes_allocated, pool->d.allocated);
  112. p = pool;
  113. while (p) {
  114. n = p->next;
  115. g_free (p);
  116. p = n;
  117. }
  118. }
  119. /**
  120. * mono_mempool_invalidate:
  121. * \param pool the memory pool to invalidate
  122. *
  123. * Fill the memory associated with this pool to 0x2a (42). Useful for debugging.
  124. */
  125. void
  126. mono_mempool_invalidate (MonoMemPool *pool)
  127. {
  128. MonoMemPool *p, *n;
  129. p = pool;
  130. while (p) {
  131. n = p->next;
  132. memset (p, 42, p->size);
  133. p = n;
  134. }
  135. }
  136. /**
  137. * mono_mempool_stats:
  138. * \param pool the memory pool we need stats for
  139. *
  140. * Print a few stats about the mempool:
  141. * - Total memory allocated (malloced) by mem pool
  142. * - Number of chunks/blocks memory is allocated in
  143. * - How much memory is available to dispense before a new malloc must occur?
  144. */
  145. void
  146. mono_mempool_stats (MonoMemPool *pool)
  147. {
  148. MonoMemPool *p;
  149. int count = 0;
  150. guint32 still_free;
  151. p = pool;
  152. while (p) {
  153. p = p->next;
  154. count++;
  155. }
  156. if (pool) {
  157. still_free = pool->end - pool->pos;
  158. g_print ("Mempool %p stats:\n", pool);
  159. g_print ("Total mem allocated: %d\n", pool->d.allocated);
  160. g_print ("Num chunks: %d\n", count);
  161. g_print ("Free memory: %d\n", still_free);
  162. }
  163. }
  164. #ifdef TRACE_ALLOCATIONS
  165. #include <execinfo.h>
  166. #include "metadata/appdomain.h"
  167. #include "metadata/metadata-internals.h"
  168. static mono_mutex_t mempool_tracing_lock;
  169. #define BACKTRACE_DEPTH 7
  170. static void
  171. mono_backtrace (int size)
  172. {
  173. void *array[BACKTRACE_DEPTH];
  174. char **names;
  175. int i, symbols;
  176. static gboolean inited;
  177. if (!inited) {
  178. mono_os_mutex_init_recursive (&mempool_tracing_lock);
  179. inited = TRUE;
  180. }
  181. mono_os_mutex_lock (&mempool_tracing_lock);
  182. g_print ("Allocating %d bytes\n", size);
  183. MONO_ENTER_GC_SAFE;
  184. symbols = backtrace (array, BACKTRACE_DEPTH);
  185. names = backtrace_symbols (array, symbols);
  186. MONO_EXIT_GC_SAFE;
  187. for (i = 1; i < symbols; ++i) {
  188. g_print ("\t%s\n", names [i]);
  189. }
  190. g_free (names);
  191. mono_os_mutex_unlock (&mempool_tracing_lock);
  192. }
  193. #endif
  194. /**
  195. * get_next_size:
  196. * @pool: the memory pool to use
  197. * @size: size of the memory entity we are trying to allocate
  198. *
  199. * A mempool is growing; give a recommended size for the next block.
  200. * Each block in a mempool should be about 150% bigger than the previous one,
  201. * or bigger if it is necessary to include the new entity.
  202. *
  203. * Returns: the recommended size.
  204. */
  205. static guint
  206. get_next_size (MonoMemPool *pool, int size)
  207. {
  208. int target = pool->next? pool->next->size: pool->size;
  209. size += SIZEOF_MEM_POOL;
  210. /* increase the size */
  211. target += target / 2;
  212. while (target < size) {
  213. target += target / 2;
  214. }
  215. if (target > MONO_MEMPOOL_PAGESIZE && size <= MONO_MEMPOOL_PAGESIZE)
  216. target = MONO_MEMPOOL_PAGESIZE;
  217. return target;
  218. }
  219. /**
  220. * mono_mempool_alloc:
  221. * \param pool the memory pool to use
  222. * \param size size of the memory block
  223. *
  224. * Allocates a new block of memory in \p pool .
  225. *
  226. * \returns the address of a newly allocated memory block.
  227. */
  228. gpointer
  229. (mono_mempool_alloc) (MonoMemPool *pool, guint size)
  230. {
  231. gpointer rval = pool->pos; // Return value
  232. // Normal case: Just bump up pos pointer and we are done
  233. size = ALIGN_SIZE (size);
  234. pool->pos = (guint8*)rval + size;
  235. #ifdef TRACE_ALLOCATIONS
  236. if (pool == mono_get_corlib ()->mempool) {
  237. mono_backtrace (size);
  238. }
  239. #endif
  240. // If we have just overflowed the current block, we need to back up and try again.
  241. if (G_UNLIKELY (pool->pos >= pool->end)) {
  242. pool->pos -= size; // Back out
  243. // For large objects, allocate the object into its own block.
  244. // (In individual allocation mode, the constant will be 0 and this path will always be taken)
  245. if (size >= MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE) {
  246. guint new_size = SIZEOF_MEM_POOL + size;
  247. MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
  248. np->next = pool->next;
  249. np->size = new_size;
  250. pool->next = np;
  251. pool->d.allocated += new_size;
  252. UnlockedAdd64 (&total_bytes_allocated, new_size);
  253. rval = (guint8*)np + SIZEOF_MEM_POOL;
  254. } else {
  255. // 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)
  256. guint new_size = get_next_size (pool, size);
  257. MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
  258. np->next = pool->next;
  259. np->size = new_size;
  260. pool->next = np;
  261. pool->pos = (guint8*)np + SIZEOF_MEM_POOL;
  262. pool->end = (guint8*)np + new_size;
  263. pool->d.allocated += new_size;
  264. UnlockedAdd64 (&total_bytes_allocated, new_size);
  265. rval = pool->pos;
  266. pool->pos += size;
  267. }
  268. }
  269. return rval;
  270. }
  271. /**
  272. * mono_mempool_alloc0:
  273. *
  274. * same as \c mono_mempool_alloc, but fills memory with zero.
  275. */
  276. gpointer
  277. (mono_mempool_alloc0) (MonoMemPool *pool, guint size)
  278. {
  279. size = ALIGN_SIZE (size);
  280. const gpointer rval = mono_mempool_alloc (pool, size);
  281. if (rval)
  282. memset (rval, 0, size);
  283. return rval;
  284. }
  285. /**
  286. * mono_mempool_contains_addr:
  287. *
  288. * Determines whether \p addr is inside the memory used by the mempool.
  289. */
  290. gboolean
  291. mono_mempool_contains_addr (MonoMemPool *pool,
  292. gpointer addr)
  293. {
  294. MonoMemPool *p = pool;
  295. while (p) {
  296. if (addr >= (gpointer)p && addr < (gpointer)((guint8*)p + p->size))
  297. return TRUE;
  298. p = p->next;
  299. }
  300. return FALSE;
  301. }
  302. /**
  303. * mono_mempool_strdup:
  304. *
  305. * Same as strdup, but allocates memory from the mempool.
  306. * Returns: a pointer to the newly allocated string data inside the mempool.
  307. */
  308. char*
  309. mono_mempool_strdup (MonoMemPool *pool,
  310. const char *s)
  311. {
  312. int l;
  313. char *res;
  314. if (s == NULL)
  315. return NULL;
  316. l = strlen (s);
  317. res = (char *)mono_mempool_alloc (pool, l + 1);
  318. memcpy (res, s, l + 1);
  319. return res;
  320. }
  321. char*
  322. mono_mempool_strdup_vprintf (MonoMemPool *pool, const char *format, va_list args)
  323. {
  324. size_t buflen;
  325. char *buf;
  326. va_list args2;
  327. va_copy (args2, args);
  328. int len = vsnprintf (NULL, 0, format, args2);
  329. va_end (args2);
  330. if (len >= 0 && (buf = (char*)mono_mempool_alloc (pool, (buflen = (size_t) (len + 1)))) != NULL) {
  331. vsnprintf (buf, buflen, format, args);
  332. } else {
  333. buf = NULL;
  334. }
  335. return buf;
  336. }
  337. char*
  338. mono_mempool_strdup_printf (MonoMemPool *pool, const char *format, ...)
  339. {
  340. char *buf;
  341. va_list args;
  342. va_start (args, format);
  343. buf = mono_mempool_strdup_vprintf (pool, format, args);
  344. va_end (args);
  345. return buf;
  346. }
  347. /**
  348. * mono_mempool_get_allocated:
  349. *
  350. * Return the amount of memory allocated for this mempool.
  351. */
  352. guint32
  353. mono_mempool_get_allocated (MonoMemPool *pool)
  354. {
  355. return pool->d.allocated;
  356. }
  357. /**
  358. * mono_mempool_get_bytes_allocated:
  359. *
  360. * Return the number of bytes currently allocated for mempools.
  361. */
  362. long
  363. mono_mempool_get_bytes_allocated (void)
  364. {
  365. return UnlockedRead64 (&total_bytes_allocated);
  366. }
  367. void
  368. mono_mempool_foreach_block(MonoMemPool* pool, mono_mempool_block_proc callback, void* user_data)
  369. {
  370. MonoMemPool *current = pool;
  371. while (current)
  372. {
  373. gpointer end = (guint8*)current + current->size;
  374. callback(current, end, user_data);
  375. current = current->next;
  376. }
  377. }