1
/************************************************************************
2
The lowest-level memory management
6
Created 5/12/1997 Heikki Tuuri
7
*************************************************************************/
11
#include "mem0pool.ic"
14
#include "sync0sync.h"
19
/* We would like to use also the buffer frames to allocate memory. This
20
would be desirable, because then the memory consumption of the database
21
would be fixed, and we might even lock the buffer pool to the main memory.
22
The problem here is that the buffer management routines can themselves call
23
memory allocation, while the buffer pool mutex is reserved.
25
The main components of the memory consumption are:
28
2. parsed and optimized SQL statements,
29
3. data dictionary cache,
31
5. locks for each transaction,
32
6. hash table for the adaptive index,
33
7. state and buffers for each SQL query currently being executed,
34
8. session for each user, and
35
9. stack for each OS thread.
37
Items 1-3 are managed by an LRU algorithm. Items 5 and 6 can potentially
38
consume very much memory. Items 7 and 8 should consume quite little memory,
39
and the OS should take care of item 9, which too should consume little memory.
41
A solution to the memory management:
43
1. the buffer pool size is set separately;
44
2. log buffer size is set separately;
45
3. the common pool size for all the other entries, except 8, is set separately.
47
Problems: we may waste memory if the common pool is set too big. Another
48
problem is the locks, which may take very much space in big transactions.
49
Then the shared pool size should be set very big. We can allow locks to take
50
space from the buffer pool, but the SQL optimizer is then unaware of the
51
usable size of the buffer pool. We could also combine the objects in the
52
common pool and the buffers in the buffer pool into a single LRU list and
53
manage it uniformly, but this approach does not take into account the parsing
54
and other costs unique to SQL statements.
56
So, let the SQL statements and the data dictionary entries form one single
57
LRU list, let us call it the dictionary LRU list. The locks for a transaction
58
can be seen as a part of the state of the transaction. Hence, they should be
59
stored in the common pool. We still have the problem of a very big update
60
transaction, for example, which will set very many x-locks on rows, and the
61
locks will consume a lot 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 truncate the LRU list of
65
the dictionary cache. 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
/* Memory area header */
77
struct mem_area_struct{
78
ulint size_and_free; /* memory area size is obtained by
79
anding with ~MEM_AREA_FREE; area in
80
a free list if ANDing with
81
MEM_AREA_FREE results in nonzero */
82
UT_LIST_NODE_T(mem_area_t)
83
free_list; /* free list node */
86
/* Mask used to extract the free bit from area->size */
87
#define MEM_AREA_FREE 1
89
/* The smallest memory area total size */
90
#define MEM_AREA_MIN_SIZE (2 * UNIV_MEM_ALIGNMENT)
92
/* Data structure for a memory pool. The space is allocated using the buddy
93
algorithm, where free list i contains areas of size 2 to power i. */
95
struct mem_pool_struct{
96
byte* buf; /* memory pool */
97
ulint size; /* memory common pool size */
98
ulint reserved; /* amount of currently allocated
100
mutex_t mutex; /* mutex protecting this struct */
101
UT_LIST_BASE_NODE_T(mem_area_t)
102
free_list[64]; /* lists of free memory areas: an
103
area is put to the list whose number
104
is the 2-logarithm of the area size */
107
/* The common memory pool */
108
mem_pool_t* mem_comm_pool = NULL;
110
ulint mem_out_of_mem_err_msg_count = 0;
112
/************************************************************************
113
Returns memory area size. */
119
mem_area_t* area) /* in: area */
121
return(area->size_and_free & ~MEM_AREA_FREE);
124
/************************************************************************
125
Sets memory area size. */
130
mem_area_t* area, /* in: area */
131
ulint size) /* in: size */
133
area->size_and_free = (area->size_and_free & MEM_AREA_FREE)
137
/************************************************************************
138
Returns memory area free bit. */
143
/* out: TRUE if free */
144
mem_area_t* area) /* in: area */
146
ut_ad(TRUE == MEM_AREA_FREE);
148
return(area->size_and_free & MEM_AREA_FREE);
151
/************************************************************************
152
Sets memory area free bit. */
157
mem_area_t* area, /* in: area */
158
ibool free) /* in: free bit value */
160
ut_ad(TRUE == MEM_AREA_FREE);
162
area->size_and_free = (area->size_and_free & ~MEM_AREA_FREE)
166
/************************************************************************
167
Creates a memory pool. */
172
/* out: memory pool */
173
ulint size) /* in: pool size in bytes */
182
pool = ut_malloc(sizeof(mem_pool_t));
184
pool->buf = ut_malloc(size);
187
mutex_create(&(pool->mutex));
188
mutex_set_level(&(pool->mutex), SYNC_MEM_POOL);
190
/* Initialize the free lists */
192
for (i = 0; i < 64; i++) {
194
UT_LIST_INIT(pool->free_list[i]);
199
while (size - used >= MEM_AREA_MIN_SIZE) {
201
i = ut_2_log(size - used);
203
if (ut_2_exp(i) > size - used) {
205
/* ut_2_log rounds upward */
210
area = (mem_area_t*)(pool->buf + used);
212
mem_area_set_size(area, ut_2_exp(i));
213
mem_area_set_free(area, TRUE);
215
UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
217
used = used + ut_2_exp(i);
227
/************************************************************************
228
Fills the specified free list. */
231
mem_pool_fill_free_list(
232
/*====================*/
233
/* out: TRUE if we were able to insert a
234
block to the free list */
235
ulint i, /* in: free list index */
236
mem_pool_t* pool) /* in: memory pool */
242
ut_ad(mutex_own(&(pool->mutex)));
245
/* We come here when we have run out of space in the
248
if (mem_out_of_mem_err_msg_count % 1000 == 0) {
249
/* We do not print the message every time: */
252
"Innobase: Warning: out of memory in additional memory pool.\n");
254
"Innobase: Innobase will start allocating memory from the OS.\n");
256
"Innobase: You should restart the database with a bigger value in\n");
258
"Innobase: the MySQL .cnf file for innobase_additional_mem_pool_size.\n");
261
mem_out_of_mem_err_msg_count++;
266
area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
269
ret = mem_pool_fill_free_list(i + 1, pool);
275
area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
278
UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area);
280
area2 = (mem_area_t*)(((byte*)area) + ut_2_exp(i));
282
mem_area_set_size(area2, ut_2_exp(i));
283
mem_area_set_free(area2, TRUE);
285
UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area2);
287
mem_area_set_size(area, ut_2_exp(i));
289
UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
294
/************************************************************************
295
Allocates memory from a pool. NOTE: This low-level function should only be
296
used in mem0mem.*! */
301
/* out, own: allocated memory buffer */
302
ulint size, /* in: allocated size in bytes; for optimum
303
space usage, the size should be a power of 2
304
minus MEM_AREA_EXTRA_SIZE */
305
mem_pool_t* pool) /* in: memory pool */
311
n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE));
313
mutex_enter(&(pool->mutex));
315
area = UT_LIST_GET_FIRST(pool->free_list[n]);
318
ret = mem_pool_fill_free_list(n, pool);
321
/* Out of memory in memory pool: we try to allocate
322
from the operating system with the regular malloc: */
324
mutex_exit(&(pool->mutex));
326
return(ut_malloc(size));
329
area = UT_LIST_GET_FIRST(pool->free_list[n]);
332
ut_a(mem_area_get_free(area));
333
ut_ad(mem_area_get_size(area) == ut_2_exp(n));
335
mem_area_set_free(area, FALSE);
337
UT_LIST_REMOVE(free_list, pool->free_list[n], area);
339
pool->reserved += mem_area_get_size(area);
341
mutex_exit(&(pool->mutex));
343
ut_ad(mem_pool_validate(pool));
345
return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*)area)));
348
/************************************************************************
349
Gets the buddy of an area, if it exists in pool. */
354
/* out: the buddy, NULL if no buddy in pool */
355
mem_area_t* area, /* in: memory area */
356
ulint size, /* in: memory area size */
357
mem_pool_t* pool) /* in: memory pool */
363
if (((((byte*)area) - pool->buf) % (2 * size)) == 0) {
365
/* The buddy is in a higher address */
367
buddy = (mem_area_t*)(((byte*)area) + size);
369
if ((((byte*)buddy) - pool->buf) + size > pool->size) {
371
/* The buddy is not wholly contained in the pool:
377
/* The buddy is in a lower address; NOTE that area cannot
378
be at the pool lower end, because then we would end up to
379
the upper branch in this if-clause: the remainder would be
382
buddy = (mem_area_t*)(((byte*)area) - size);
388
/************************************************************************
389
Frees memory to a pool. */
394
void* ptr, /* in, own: pointer to allocated memory
396
mem_pool_t* pool) /* in: memory pool */
404
if (mem_out_of_mem_err_msg_count > 0) {
405
/* It may be that the area was really allocated from the
406
OS with regular malloc: check if ptr points within
409
if ((byte*)ptr < pool->buf
410
|| (byte*)ptr >= pool->buf + pool->size) {
417
area = (mem_area_t*) (((byte*)ptr) - MEM_AREA_EXTRA_SIZE);
419
size = mem_area_get_size(area);
422
ut_a(!mem_area_get_free(area));
424
#ifdef UNIV_LIGHT_MEM_DEBUG
425
if (((byte*)area) + size < pool->buf + pool->size) {
429
next_size = mem_area_get_size(
430
(mem_area_t*)(((byte*)area) + size));
431
ut_a(ut_2_power_up(next_size) == next_size);
434
buddy = mem_area_get_buddy(area, size, pool);
438
mutex_enter(&(pool->mutex));
440
if (buddy && mem_area_get_free(buddy)
441
&& (size == mem_area_get_size(buddy))) {
443
/* The buddy is in a free list */
445
if ((byte*)buddy < (byte*)area) {
446
new_ptr = ((byte*)buddy) + MEM_AREA_EXTRA_SIZE;
448
mem_area_set_size(buddy, 2 * size);
449
mem_area_set_free(buddy, FALSE);
453
mem_area_set_size(area, 2 * size);
456
/* Remove the buddy from its free list and merge it to area */
458
UT_LIST_REMOVE(free_list, pool->free_list[n], buddy);
460
pool->reserved += ut_2_exp(n);
462
mutex_exit(&(pool->mutex));
464
mem_area_free(new_ptr, pool);
468
UT_LIST_ADD_FIRST(free_list, pool->free_list[n], area);
470
mem_area_set_free(area, TRUE);
472
ut_ad(pool->reserved >= size);
474
pool->reserved -= size;
477
mutex_exit(&(pool->mutex));
479
ut_ad(mem_pool_validate(pool));
482
/************************************************************************
483
Validates a memory pool. */
488
/* out: TRUE if ok */
489
mem_pool_t* pool) /* in: memory pool */
496
mutex_enter(&(pool->mutex));
500
for (i = 0; i < 64; i++) {
502
UT_LIST_VALIDATE(free_list, mem_area_t, pool->free_list[i]);
504
area = UT_LIST_GET_FIRST(pool->free_list[i]);
506
while (area != NULL) {
507
ut_a(mem_area_get_free(area));
508
ut_a(mem_area_get_size(area) == ut_2_exp(i));
510
buddy = mem_area_get_buddy(area, ut_2_exp(i), pool);
512
ut_a(!buddy || !mem_area_get_free(buddy)
513
|| (ut_2_exp(i) != mem_area_get_size(buddy)));
515
area = UT_LIST_GET_NEXT(free_list, area);
521
ut_a(free + pool->reserved == pool->size
522
- (pool->size % MEM_AREA_MIN_SIZE));
523
mutex_exit(&(pool->mutex));
528
/************************************************************************
529
Prints info of a memory pool. */
534
FILE* outfile,/* in: output file to write to */
535
mem_pool_t* pool) /* in: memory pool */
539
mem_pool_validate(pool);
541
fprintf(outfile, "INFO OF A MEMORY POOL\n");
543
mutex_enter(&(pool->mutex));
545
for (i = 0; i < 64; i++) {
546
if (UT_LIST_GET_LEN(pool->free_list[i]) > 0) {
549
"Free list length %lu for blocks of size %lu\n",
550
UT_LIST_GET_LEN(pool->free_list[i]),
555
fprintf(outfile, "Pool size %lu, reserved %lu.\n", pool->size,
557
mutex_exit(&(pool->mutex));
560
/************************************************************************
561
Returns the amount of reserved memory. */
564
mem_pool_get_reserved(
565
/*==================*/
566
/* out: reserved mmeory in bytes */
567
mem_pool_t* pool) /* in: memory pool */
571
mutex_enter(&(pool->mutex));
573
reserved = pool->reserved;
575
mutex_exit(&(pool->mutex));