mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
d3c0752b6a
Merge InnoDB-3.23.50 innobase/btr/btr0btr.c: Merge InnoDB-3.23.50 innobase/btr/btr0cur.c: Merge InnoDB-3.23.50 innobase/btr/btr0sea.c: Merge InnoDB-3.23.50 innobase/buf/buf0buf.c: Merge InnoDB-3.23.50 innobase/buf/buf0flu.c: Merge InnoDB-3.23.50 innobase/dict/dict0dict.c: Merge InnoDB-3.23.50 innobase/dict/dict0load.c: Merge InnoDB-3.23.50 innobase/fil/fil0fil.c: Merge InnoDB-3.23.50 innobase/fsp/fsp0fsp.c: Merge InnoDB-3.23.50 innobase/include/buf0flu.h: Merge InnoDB-3.23.50 innobase/include/dict0dict.h: Merge InnoDB-3.23.50 innobase/include/fil0fil.h: Merge InnoDB-3.23.50 innobase/include/fsp0fsp.h: Merge InnoDB-3.23.50 innobase/include/log0log.h: Merge InnoDB-3.23.50 innobase/include/log0recv.h: Merge InnoDB-3.23.50 innobase/include/mem0mem.h: Merge InnoDB-3.23.50 innobase/include/os0file.h: Merge InnoDB-3.23.50 innobase/include/row0mysql.h: Merge InnoDB-3.23.50 innobase/include/srv0srv.h: Merge InnoDB-3.23.50 innobase/include/srv0start.h: Merge InnoDB-3.23.50 innobase/include/trx0sys.h: Merge InnoDB-3.23.50 innobase/include/ut0byte.h: Merge InnoDB-3.23.50 innobase/include/ut0rnd.h: Merge InnoDB-3.23.50 innobase/include/ut0ut.h: Merge InnoDB-3.23.50 innobase/log/log0log.c: Merge InnoDB-3.23.50 innobase/log/log0recv.c: Merge InnoDB-3.23.50 innobase/mem/mem0mem.c: Merge InnoDB-3.23.50 innobase/os/os0file.c: Merge InnoDB-3.23.50 innobase/rem/rem0cmp.c: Merge InnoDB-3.23.50 innobase/row/row0ins.c: Merge InnoDB-3.23.50 innobase/row/row0mysql.c: Merge InnoDB-3.23.50 innobase/row/row0sel.c: Merge InnoDB-3.23.50 innobase/row/row0upd.c: Merge InnoDB-3.23.50 innobase/srv/srv0srv.c: Merge InnoDB-3.23.50 innobase/srv/srv0start.c: Merge InnoDB-3.23.50 innobase/trx/trx0sys.c: Merge InnoDB-3.23.50 innobase/ut/ut0mem.c: Merge InnoDB-3.23.50 innobase/ut/ut0ut.c: Merge InnoDB-3.23.50 sql/ha_innobase.cc: Merge InnoDB-3.23.50 sql/ha_innobase.h: Merge InnoDB-3.23.50
363 lines
9.7 KiB
C
363 lines
9.7 KiB
C
/************************************************************************
|
|
The memory management
|
|
|
|
(c) 1994, 1995 Innobase Oy
|
|
|
|
Created 6/9/1994 Heikki Tuuri
|
|
*************************************************************************/
|
|
|
|
|
|
#include "mem0mem.h"
|
|
#ifdef UNIV_NONINL
|
|
#include "mem0mem.ic"
|
|
#endif
|
|
|
|
#include "mach0data.h"
|
|
#include "buf0buf.h"
|
|
#include "btr0sea.h"
|
|
#include "srv0srv.h"
|
|
#include "mem0dbg.c"
|
|
|
|
/*
|
|
THE MEMORY MANAGEMENT
|
|
=====================
|
|
|
|
The basic element of the memory management is called a memory
|
|
heap. A memory heap is conceptually a
|
|
stack from which memory can be allocated. The stack may grow infinitely.
|
|
The top element of the stack may be freed, or
|
|
the whole stack can be freed at one time. The advantage of the
|
|
memory heap concept is that we can avoid using the malloc and free
|
|
functions of C which are quite expensive, for example, on the Solaris + GCC
|
|
system (50 MHz Sparc, 1993) the pair takes 3 microseconds,
|
|
on Win NT + 100MHz Pentium, 2.5 microseconds.
|
|
When we use a memory heap,
|
|
we can allocate larger blocks of memory at a time and thus
|
|
reduce overhead. Slightly more efficient the method is when we
|
|
allocate the memory from the index page buffer pool, as we can
|
|
claim a new page fast. This is called buffer allocation.
|
|
When we allocate the memory from the dynamic memory of the
|
|
C environment, that is called dynamic allocation.
|
|
|
|
The default way of operation of the memory heap is the following.
|
|
First, when the heap is created, an initial block of memory is
|
|
allocated. In dynamic allocation this may be about 50 bytes.
|
|
If more space is needed, additional blocks are allocated
|
|
and they are put into a linked list.
|
|
After the initial block, each allocated block is twice the size of the
|
|
previous, until a threshold is attained, after which the sizes
|
|
of the blocks stay the same. An exception is, of course, the case
|
|
where the caller requests a memory buffer whose size is
|
|
bigger than the threshold. In that case a block big enough must
|
|
be allocated.
|
|
|
|
The heap is physically arranged so that if the current block
|
|
becomes full, a new block is allocated and always inserted in the
|
|
chain of blocks as the last block.
|
|
|
|
In the debug version of the memory management, all the allocated
|
|
heaps are kept in a list (which is implemented as a hash table).
|
|
Thus we can notice if the caller tries to free an already freed
|
|
heap. In addition, each buffer given to the caller contains
|
|
start field at the start and a trailer field at the end of the buffer.
|
|
|
|
The start field has the following content:
|
|
A. sizeof(ulint) bytes of field length (in the standard byte order)
|
|
B. sizeof(ulint) bytes of check field (a random number)
|
|
|
|
The trailer field contains:
|
|
A. sizeof(ulint) bytes of check field (the same random number as at the start)
|
|
|
|
Thus we can notice if something has been copied over the
|
|
borders of the buffer, which is illegal.
|
|
The memory in the buffers is initialized to a random byte sequence.
|
|
After freeing, all the blocks in the heap are set to random bytes
|
|
to help us discover errors which result from the use of
|
|
buffers in an already freed heap. */
|
|
|
|
#ifdef MEM_PERIODIC_CHECK
|
|
|
|
ibool mem_block_list_inited;
|
|
/* List of all mem blocks allocated; protected by the mem_comm_pool mutex */
|
|
UT_LIST_BASE_NODE_T(mem_block_t) mem_block_list;
|
|
|
|
#endif
|
|
|
|
/*******************************************************************
|
|
NOTE: Use the corresponding macro instead of this function.
|
|
Allocates a single buffer of memory from the dynamic memory of
|
|
the C compiler. Is like malloc of C. The buffer must be freed
|
|
with mem_free. */
|
|
|
|
void*
|
|
mem_alloc_func_noninline(
|
|
/*=====================*/
|
|
/* out, own: free storage, NULL if did not
|
|
succeed */
|
|
ulint n, /* in: desired number of bytes */
|
|
char* file_name, /* in: file name where created */
|
|
ulint line /* in: line where created */
|
|
)
|
|
{
|
|
return(mem_alloc_func(n, file_name, line));
|
|
}
|
|
|
|
/*******************************************************************
|
|
Creates a memory heap block where data can be allocated. */
|
|
|
|
mem_block_t*
|
|
mem_heap_create_block(
|
|
/*==================*/
|
|
/* out, own: memory heap block, NULL if did not
|
|
succeed */
|
|
mem_heap_t* heap,/* in: memory heap or NULL if first block should
|
|
be created */
|
|
ulint n, /* in: number of bytes needed for user data, or
|
|
if init_block is not NULL, its size in bytes */
|
|
void* init_block, /* in: init block in fast create, type must be
|
|
MEM_HEAP_DYNAMIC */
|
|
ulint type, /* in: type of heap: MEM_HEAP_DYNAMIC, or
|
|
MEM_HEAP_BUFFER possibly ORed to MEM_HEAP_BTR_SEARCH */
|
|
char* file_name,/* in: file name where created */
|
|
ulint line) /* in: line where created */
|
|
{
|
|
mem_block_t* block;
|
|
ulint len;
|
|
|
|
ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
|
|
|| (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
|
|
|
|
if (heap && heap->magic_n != MEM_BLOCK_MAGIC_N) {
|
|
mem_analyze_corruption((byte*)heap);
|
|
}
|
|
|
|
/* In dynamic allocation, calculate the size: block header + data. */
|
|
|
|
if (init_block != NULL) {
|
|
ut_ad(type == MEM_HEAP_DYNAMIC);
|
|
ut_ad(n > MEM_BLOCK_START_SIZE + MEM_BLOCK_HEADER_SIZE);
|
|
len = n;
|
|
block = init_block;
|
|
|
|
} else if (type == MEM_HEAP_DYNAMIC) {
|
|
|
|
len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
|
|
block = mem_area_alloc(len, mem_comm_pool);
|
|
} else {
|
|
ut_ad(n <= MEM_MAX_ALLOC_IN_BUF);
|
|
|
|
len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
|
|
|
|
if (len < UNIV_PAGE_SIZE / 2) {
|
|
|
|
block = mem_area_alloc(len, mem_comm_pool);
|
|
} else {
|
|
len = UNIV_PAGE_SIZE;
|
|
|
|
if ((type & MEM_HEAP_BTR_SEARCH) && heap) {
|
|
/* We cannot allocate the block from the
|
|
buffer pool, but must get the free block from
|
|
the heap header free block field */
|
|
|
|
block = (mem_block_t*)heap->free_block;
|
|
heap->free_block = NULL;
|
|
} else {
|
|
block = (mem_block_t*)buf_frame_alloc();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (block == NULL) {
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
block->magic_n = MEM_BLOCK_MAGIC_N;
|
|
ut_memcpy(&(block->file_name), file_name + ut_strlen(file_name) - 7,
|
|
7);
|
|
block->file_name[7]='\0';
|
|
block->line = line;
|
|
|
|
#ifdef MEM_PERIODIC_CHECK
|
|
mem_pool_mutex_enter();
|
|
|
|
if (!mem_block_list_inited) {
|
|
mem_block_list_inited = TRUE;
|
|
UT_LIST_INIT(mem_block_list);
|
|
}
|
|
|
|
UT_LIST_ADD_LAST(mem_block_list, mem_block_list, block);
|
|
|
|
mem_pool_mutex_exit();
|
|
#endif
|
|
mem_block_set_len(block, len);
|
|
mem_block_set_type(block, type);
|
|
mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);
|
|
mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE);
|
|
|
|
block->free_block = NULL;
|
|
|
|
if (init_block != NULL) {
|
|
block->init_block = TRUE;
|
|
} else {
|
|
block->init_block = FALSE;
|
|
}
|
|
|
|
ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len);
|
|
|
|
return(block);
|
|
}
|
|
|
|
/*******************************************************************
|
|
Adds a new block to a memory heap. */
|
|
|
|
mem_block_t*
|
|
mem_heap_add_block(
|
|
/*===============*/
|
|
/* out: created block, NULL if did not
|
|
succeed */
|
|
mem_heap_t* heap, /* in: memory heap */
|
|
ulint n) /* in: number of bytes user needs */
|
|
{
|
|
mem_block_t* block;
|
|
mem_block_t* new_block;
|
|
ulint new_size;
|
|
|
|
ut_ad(mem_heap_check(heap));
|
|
|
|
block = UT_LIST_GET_LAST(heap->base);
|
|
|
|
/* We have to allocate a new block. The size is always at least
|
|
doubled until the standard size is reached. After that the size
|
|
stays the same, except in cases where the caller needs more space. */
|
|
|
|
new_size = 2 * mem_block_get_len(block);
|
|
|
|
if (heap->type != MEM_HEAP_DYNAMIC) {
|
|
/* From the buffer pool we allocate buffer frames */
|
|
ut_a(n <= MEM_MAX_ALLOC_IN_BUF);
|
|
|
|
if (new_size > MEM_MAX_ALLOC_IN_BUF) {
|
|
new_size = MEM_MAX_ALLOC_IN_BUF;
|
|
}
|
|
} else if (new_size > MEM_BLOCK_STANDARD_SIZE) {
|
|
|
|
new_size = MEM_BLOCK_STANDARD_SIZE;
|
|
}
|
|
|
|
if (new_size < n) {
|
|
new_size = n;
|
|
}
|
|
|
|
new_block = mem_heap_create_block(heap, new_size, NULL, heap->type,
|
|
heap->file_name, heap->line);
|
|
if (new_block == NULL) {
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/* Add the new block as the last block */
|
|
|
|
UT_LIST_INSERT_AFTER(list, heap->base, block, new_block);
|
|
|
|
return(new_block);
|
|
}
|
|
|
|
/**********************************************************************
|
|
Frees a block from a memory heap. */
|
|
|
|
void
|
|
mem_heap_block_free(
|
|
/*================*/
|
|
mem_heap_t* heap, /* in: heap */
|
|
mem_block_t* block) /* in: block to free */
|
|
{
|
|
ulint type;
|
|
ulint len;
|
|
ibool init_block;
|
|
|
|
if (block->magic_n != MEM_BLOCK_MAGIC_N) {
|
|
mem_analyze_corruption((byte*)block);
|
|
}
|
|
|
|
UT_LIST_REMOVE(list, heap->base, block);
|
|
|
|
#ifdef MEM_PERIODIC_CHECK
|
|
mem_pool_mutex_enter();
|
|
|
|
UT_LIST_REMOVE(mem_block_list, mem_block_list, block);
|
|
|
|
mem_pool_mutex_exit();
|
|
#endif
|
|
type = heap->type;
|
|
len = block->len;
|
|
init_block = block->init_block;
|
|
block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
|
|
|
|
#ifdef UNIV_MEM_DEBUG
|
|
/* In the debug version we set the memory to a random combination
|
|
of hex 0xDE and 0xAD. */
|
|
|
|
mem_erase_buf((byte*)block, len);
|
|
|
|
#endif
|
|
|
|
if (init_block) {
|
|
/* Do not have to free: do nothing */
|
|
|
|
} else if (type == MEM_HEAP_DYNAMIC) {
|
|
|
|
mem_area_free(block, mem_comm_pool);
|
|
} else {
|
|
ut_ad(type & MEM_HEAP_BUFFER);
|
|
|
|
if (len >= UNIV_PAGE_SIZE / 2) {
|
|
buf_frame_free((byte*)block);
|
|
} else {
|
|
mem_area_free(block, mem_comm_pool);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
Frees the free_block field from a memory heap. */
|
|
|
|
void
|
|
mem_heap_free_block_free(
|
|
/*=====================*/
|
|
mem_heap_t* heap) /* in: heap */
|
|
{
|
|
if (heap->free_block) {
|
|
|
|
buf_frame_free(heap->free_block);
|
|
|
|
heap->free_block = NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef MEM_PERIODIC_CHECK
|
|
/**********************************************************************
|
|
Goes through the list of all allocated mem blocks, checks their magic
|
|
numbers, and reports possible corruption. */
|
|
|
|
void
|
|
mem_validate_all_blocks(void)
|
|
/*=========================*/
|
|
{
|
|
mem_block_t* block;
|
|
|
|
mem_pool_mutex_enter();
|
|
|
|
block = UT_LIST_GET_FIRST(mem_block_list);
|
|
|
|
while (block) {
|
|
if (block->magic_n != MEM_BLOCK_MAGIC_N) {
|
|
mem_analyze_corruption((byte*)block);
|
|
}
|
|
|
|
block = UT_LIST_GET_NEXT(mem_block_list, block);
|
|
}
|
|
|
|
mem_pool_mutex_exit();
|
|
}
|
|
#endif
|