1
/************************************************************************
2
The lowest-level memory management
6
Created 5/12/1997 Heikki Tuuri
7
*************************************************************************/
11
#include "mem0pool.ic"
14
#include "sync0sync.h"
20
/* We would like to use also the buffer frames to allocate memory. This
21
would be desirable, because then the memory consumption of the database
22
would be fixed, and we might even lock the buffer pool to the main memory.
23
The problem here is that the buffer management routines can themselves call
24
memory allocation, while the buffer pool mutex is reserved.
26
The main components of the memory consumption are:
29
2. parsed and optimized SQL statements,
30
3. data dictionary cache,
32
5. locks for each transaction,
33
6. hash table for the adaptive index,
34
7. state and buffers for each SQL query currently being executed,
35
8. session for each user, and
36
9. stack for each OS thread.
38
Items 1 and 2 are managed by an LRU algorithm. Items 5 and 6 can potentially
39
consume very much memory. Items 7 and 8 should consume quite little memory,
40
and the OS should take care of item 9, which too should consume little memory.
42
A solution to the memory management:
44
1. the buffer pool size is set separately;
45
2. log buffer size is set separately;
46
3. the common pool size for all the other entries, except 8, is set separately.
48
Problems: we may waste memory if the common pool is set too big. Another
49
problem is the locks, which may take very much space in big transactions.
50
Then the shared pool size should be set very big. We can allow locks to take
51
space from the buffer pool, but the SQL optimizer is then unaware of the
52
usable size of the buffer pool. We could also combine the objects in the
53
common pool and the buffers in the buffer pool into a single LRU list and
54
manage it uniformly, but this approach does not take into account the parsing
55
and other costs unique to SQL statements.
57
The locks for a transaction can be seen as a part of the state of the
58
transaction. Hence, they should be stored in the common pool. We still
59
have the problem of a very big update transaction, for example, which
60
will set very many x-locks on rows, and the locks will consume a lot
61
of memory, say, half of the buffer pool size.
63
Another problem is what to do if we are not able to malloc a requested
64
block of memory from the common pool. Then we can request memory from
65
the operating system. If it does not help, a system error results.
67
Because 5 and 6 may potentially consume very much memory, we let them grow
68
into the buffer pool. We may let the locks of a transaction take frames
69
from the buffer pool, when the corresponding memory heap block has grown to
70
the size of a buffer frame. Similarly for the hash node cells of the locks,
71
and for the adaptive index. Thus, for each individual transaction, its locks
72
can occupy at most about the size of the buffer frame of memory in the common
73
pool, and after that its locks will grow into the buffer pool. */
75
/* Mask used to extract the free bit from area->size */
76
#define MEM_AREA_FREE 1
78
/* The smallest memory area total size */
79
#define MEM_AREA_MIN_SIZE (2 * MEM_AREA_EXTRA_SIZE)
82
/* Data structure for a memory pool. The space is allocated using the buddy
83
algorithm, where free list i contains areas of size 2 to power i. */
84
struct mem_pool_struct{
85
byte* buf; /* memory pool */
86
ulint size; /* memory common pool size */
87
ulint reserved; /* amount of currently allocated
89
mutex_t mutex; /* mutex protecting this struct */
90
UT_LIST_BASE_NODE_T(mem_area_t)
91
free_list[64]; /* lists of free memory areas: an
92
area is put to the list whose number
93
is the 2-logarithm of the area size */
96
/* The common memory pool */
97
mem_pool_t* mem_comm_pool = NULL;
99
/* We use this counter to check that the mem pool mutex does not leak;
100
this is to track a strange assertion failure reported at
101
mysql@lists.mysql.com */
103
ulint mem_n_threads_inside = 0;
105
/************************************************************************
106
Reserves the mem pool mutex. */
109
mem_pool_mutex_enter(void)
110
/*======================*/
112
mutex_enter(&(mem_comm_pool->mutex));
115
/************************************************************************
116
Releases the mem pool mutex. */
119
mem_pool_mutex_exit(void)
120
/*=====================*/
122
mutex_exit(&(mem_comm_pool->mutex));
125
/************************************************************************
126
Returns memory area size. */
132
mem_area_t* area) /* in: area */
134
return(area->size_and_free & ~MEM_AREA_FREE);
137
/************************************************************************
138
Sets memory area size. */
143
mem_area_t* area, /* in: area */
144
ulint size) /* in: size */
146
area->size_and_free = (area->size_and_free & MEM_AREA_FREE)
150
/************************************************************************
151
Returns memory area free bit. */
156
/* out: TRUE if free */
157
mem_area_t* area) /* in: area */
159
#if TRUE != MEM_AREA_FREE
160
# error "TRUE != MEM_AREA_FREE"
162
return(area->size_and_free & MEM_AREA_FREE);
165
/************************************************************************
166
Sets memory area free bit. */
171
mem_area_t* area, /* in: area */
172
ibool free) /* in: free bit value */
174
#if TRUE != MEM_AREA_FREE
175
# error "TRUE != MEM_AREA_FREE"
177
area->size_and_free = (area->size_and_free & ~MEM_AREA_FREE)
181
/************************************************************************
182
Creates a memory pool. */
187
/* out: memory pool */
188
ulint size) /* in: pool size in bytes */
197
pool = ut_malloc(sizeof(mem_pool_t));
199
/* We do not set the memory to zero (FALSE) in the pool,
200
but only when allocated at a higher level in mem0mem.c.
201
This is to avoid masking useful Purify warnings. */
203
pool->buf = ut_malloc_low(size, FALSE, TRUE);
206
mutex_create(&pool->mutex, SYNC_MEM_POOL);
208
/* Initialize the free lists */
210
for (i = 0; i < 64; i++) {
212
UT_LIST_INIT(pool->free_list[i]);
217
while (size - used >= MEM_AREA_MIN_SIZE) {
219
i = ut_2_log(size - used);
221
if (ut_2_exp(i) > size - used) {
223
/* ut_2_log rounds upward */
228
area = (mem_area_t*)(pool->buf + used);
230
mem_area_set_size(area, ut_2_exp(i));
231
mem_area_set_free(area, TRUE);
232
UNIV_MEM_FREE(MEM_AREA_EXTRA_SIZE + (byte*) area,
233
ut_2_exp(i) - MEM_AREA_EXTRA_SIZE);
235
UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
237
used = used + ut_2_exp(i);
247
/************************************************************************
248
Fills the specified free list. */
251
mem_pool_fill_free_list(
252
/*====================*/
253
/* out: TRUE if we were able to insert a
254
block to the free list */
255
ulint i, /* in: free list index */
256
mem_pool_t* pool) /* in: memory pool */
262
ut_ad(mutex_own(&(pool->mutex)));
265
/* We come here when we have run out of space in the
271
area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
274
if (UT_LIST_GET_LEN(pool->free_list[i + 1]) > 0) {
275
ut_print_timestamp(stderr);
278
" InnoDB: Error: mem pool free list %lu"
280
"InnoDB: though the list is empty!\n",
283
UT_LIST_GET_LEN(pool->free_list[i + 1]));
286
ret = mem_pool_fill_free_list(i + 1, pool);
293
area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
296
if (UT_LIST_GET_LEN(pool->free_list[i + 1]) == 0) {
297
mem_analyze_corruption(area);
302
UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area);
304
area2 = (mem_area_t*)(((byte*)area) + ut_2_exp(i));
305
UNIV_MEM_ALLOC(area2, MEM_AREA_EXTRA_SIZE);
307
mem_area_set_size(area2, ut_2_exp(i));
308
mem_area_set_free(area2, TRUE);
310
UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area2);
312
mem_area_set_size(area, ut_2_exp(i));
314
UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
319
/************************************************************************
320
Allocates memory from a pool. NOTE: This low-level function should only be
321
used in mem0mem.*! */
326
/* out, own: allocated memory buffer */
327
ulint size, /* in: allocated size in bytes; for optimum
328
space usage, the size should be a power of 2
329
minus MEM_AREA_EXTRA_SIZE */
330
mem_pool_t* pool) /* in: memory pool */
336
n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE));
338
mutex_enter(&(pool->mutex));
339
mem_n_threads_inside++;
341
ut_a(mem_n_threads_inside == 1);
343
area = UT_LIST_GET_FIRST(pool->free_list[n]);
346
ret = mem_pool_fill_free_list(n, pool);
349
/* Out of memory in memory pool: we try to allocate
350
from the operating system with the regular malloc: */
352
mem_n_threads_inside--;
353
mutex_exit(&(pool->mutex));
355
return(ut_malloc(size));
358
area = UT_LIST_GET_FIRST(pool->free_list[n]);
361
if (!mem_area_get_free(area)) {
363
"InnoDB: Error: Removing element from mem pool"
364
" free list %lu though the\n"
365
"InnoDB: element is not marked free!\n",
368
mem_analyze_corruption(area);
370
/* Try to analyze a strange assertion failure reported at
371
mysql@lists.mysql.com where the free bit IS 1 in the
374
if (mem_area_get_free(area)) {
376
"InnoDB: Probably a race condition"
377
" because now the area is marked free!\n");
383
if (UT_LIST_GET_LEN(pool->free_list[n]) == 0) {
385
"InnoDB: Error: Removing element from mem pool"
387
"InnoDB: though the list length is 0!\n",
389
mem_analyze_corruption(area);
394
ut_ad(mem_area_get_size(area) == ut_2_exp(n));
396
mem_area_set_free(area, FALSE);
398
UT_LIST_REMOVE(free_list, pool->free_list[n], area);
400
pool->reserved += mem_area_get_size(area);
402
mem_n_threads_inside--;
403
mutex_exit(&(pool->mutex));
405
ut_ad(mem_pool_validate(pool));
406
UNIV_MEM_ALLOC(MEM_AREA_EXTRA_SIZE + (byte*)area,
407
ut_2_exp(n) - MEM_AREA_EXTRA_SIZE);
409
return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*)area)));
412
/************************************************************************
413
Gets the buddy of an area, if it exists in pool. */
418
/* out: the buddy, NULL if no buddy in pool */
419
mem_area_t* area, /* in: memory area */
420
ulint size, /* in: memory area size */
421
mem_pool_t* pool) /* in: memory pool */
427
if (((((byte*)area) - pool->buf) % (2 * size)) == 0) {
429
/* The buddy is in a higher address */
431
buddy = (mem_area_t*)(((byte*)area) + size);
433
if ((((byte*)buddy) - pool->buf) + size > pool->size) {
435
/* The buddy is not wholly contained in the pool:
441
/* The buddy is in a lower address; NOTE that area cannot
442
be at the pool lower end, because then we would end up to
443
the upper branch in this if-clause: the remainder would be
446
buddy = (mem_area_t*)(((byte*)area) - size);
452
/************************************************************************
453
Frees memory to a pool. */
458
void* ptr, /* in, own: pointer to allocated memory
460
mem_pool_t* pool) /* in: memory pool */
468
/* It may be that the area was really allocated from the OS with
469
regular malloc: check if ptr points within our memory pool */
471
if ((byte*)ptr < pool->buf || (byte*)ptr >= pool->buf + pool->size) {
477
area = (mem_area_t*) (((byte*)ptr) - MEM_AREA_EXTRA_SIZE);
479
if (mem_area_get_free(area)) {
481
"InnoDB: Error: Freeing element to mem pool"
482
" free list though the\n"
483
"InnoDB: element is marked free!\n");
485
mem_analyze_corruption(area);
489
size = mem_area_get_size(area);
490
UNIV_MEM_FREE(ptr, size - MEM_AREA_EXTRA_SIZE);
494
"InnoDB: Error: Mem area size is 0. Possibly a"
495
" memory overrun of the\n"
496
"InnoDB: previous allocated area!\n");
498
mem_analyze_corruption(area);
502
#ifdef UNIV_LIGHT_MEM_DEBUG
503
if (((byte*)area) + size < pool->buf + pool->size) {
507
next_size = mem_area_get_size(
508
(mem_area_t*)(((byte*)area) + size));
509
if (ut_2_power_up(next_size) != next_size) {
511
"InnoDB: Error: Memory area size %lu,"
512
" next area size %lu not a power of 2!\n"
513
"InnoDB: Possibly a memory overrun of"
514
" the buffer being freed here.\n",
515
(ulong) size, (ulong) next_size);
516
mem_analyze_corruption(area);
522
buddy = mem_area_get_buddy(area, size, pool);
526
mutex_enter(&(pool->mutex));
527
mem_n_threads_inside++;
529
ut_a(mem_n_threads_inside == 1);
531
if (buddy && mem_area_get_free(buddy)
532
&& (size == mem_area_get_size(buddy))) {
534
/* The buddy is in a free list */
536
if ((byte*)buddy < (byte*)area) {
537
new_ptr = ((byte*)buddy) + MEM_AREA_EXTRA_SIZE;
539
mem_area_set_size(buddy, 2 * size);
540
mem_area_set_free(buddy, FALSE);
544
mem_area_set_size(area, 2 * size);
547
/* Remove the buddy from its free list and merge it to area */
549
UT_LIST_REMOVE(free_list, pool->free_list[n], buddy);
551
pool->reserved += ut_2_exp(n);
553
mem_n_threads_inside--;
554
mutex_exit(&(pool->mutex));
556
mem_area_free(new_ptr, pool);
560
UT_LIST_ADD_FIRST(free_list, pool->free_list[n], area);
562
mem_area_set_free(area, TRUE);
564
ut_ad(pool->reserved >= size);
566
pool->reserved -= size;
569
mem_n_threads_inside--;
570
mutex_exit(&(pool->mutex));
572
ut_ad(mem_pool_validate(pool));
575
/************************************************************************
576
Validates a memory pool. */
581
/* out: TRUE if ok */
582
mem_pool_t* pool) /* in: memory pool */
589
mutex_enter(&(pool->mutex));
593
for (i = 0; i < 64; i++) {
595
UT_LIST_VALIDATE(free_list, mem_area_t, pool->free_list[i]);
597
area = UT_LIST_GET_FIRST(pool->free_list[i]);
599
while (area != NULL) {
600
ut_a(mem_area_get_free(area));
601
ut_a(mem_area_get_size(area) == ut_2_exp(i));
603
buddy = mem_area_get_buddy(area, ut_2_exp(i), pool);
605
ut_a(!buddy || !mem_area_get_free(buddy)
606
|| (ut_2_exp(i) != mem_area_get_size(buddy)));
608
area = UT_LIST_GET_NEXT(free_list, area);
614
ut_a(free + pool->reserved == pool->size);
616
mutex_exit(&(pool->mutex));
621
/************************************************************************
622
Prints info of a memory pool. */
627
FILE* outfile,/* in: output file to write to */
628
mem_pool_t* pool) /* in: memory pool */
632
mem_pool_validate(pool);
634
fprintf(outfile, "INFO OF A MEMORY POOL\n");
636
mutex_enter(&(pool->mutex));
638
for (i = 0; i < 64; i++) {
639
if (UT_LIST_GET_LEN(pool->free_list[i]) > 0) {
642
"Free list length %lu for"
643
" blocks of size %lu\n",
644
(ulong) UT_LIST_GET_LEN(pool->free_list[i]),
645
(ulong) ut_2_exp(i));
649
fprintf(outfile, "Pool size %lu, reserved %lu.\n", (ulong) pool->size,
650
(ulong) pool->reserved);
651
mutex_exit(&(pool->mutex));
654
/************************************************************************
655
Returns the amount of reserved memory. */
658
mem_pool_get_reserved(
659
/*==================*/
660
/* out: reserved memory in bytes */
661
mem_pool_t* pool) /* in: memory pool */
665
mutex_enter(&(pool->mutex));
667
reserved = pool->reserved;
669
mutex_exit(&(pool->mutex));