MySQL memory handling and memory handling in Falcon
← Back to MySQL University main page- Presenters: Ann Harrison and Konstantin Osipov
- Time: Thursday, July 19, 2007, at 13:00 UTC.
- Scribe: MC Brown
- Attendees:
- Alexander Nozdrin
- Hakan Kuecuekyilmaz
- Ingo Struewing
- Shane Bester
- Victoria Reznichenko
- Kristofer Pettersson
Contents |
[edit] MySQL Memory Handling
[edit] Mechanisms
[edit] System Heap
When working with system heap allocation, please use MySQL convenience wrappers:
Implemented in mysys/my_malloc.c
gptr my_malloc(unsigned int size, myf my_flags) void my_free(gptr ptr, myf my_flags); #define MY_ZEROFILL 32 /* my_malloc(), fill array with zero */ #define MY_ALLOW_ZERO_PTR 64 /* my_realloc() ; zero ptr -> malloc */ #define MY_FREE_ON_ERROR 128 /* my_realloc() ; Free old ptr on error */ #define MY_HOLD_ON_ERROR 256 /* my_realloc() ; Return old ptr on error */ #define MY_FAE 8 /* Fatal if any error */ #define MY_WME 16 /* Write message on error */
These functions should always be used because:
- they change implementation if compiled with SAFEMALLOC and allow for leak detection
- they automatically call error_handler_hook, which sets an error in THD if needed
- they hide any possible discrepancies in different libc implementations
- they accept useful flags
Convenience macros:
- safeFree -- assigns the pointer to 0 after calling my_free()
- x_free -- calls my_free with MY_ALLOW_ZERO_PTR
Aslo available:
- my_memdup
- my_strdup
- my_strdup_with_length
See the source code of mysys/my_malloc.c
Do not use plain 'malloc' and 'free', because they don't provide any of the essential features above.
Life time properties of system heap:
- you define the life time of your allocation
- you're responsible for freeing memory
[edit] MEM_ROOT block allocator
Allocates memory in blocks of 4K (or other parameterizable size).
- helps deal with the problem of memory framentation
- always thread local, unlike system heap
- does not have overhead of metadata per each allocation unit
- subject to internal fragmentation
- CHEAP and should be used instead of system heap whenever possible
- if out of memory calls error_handler_hook which sets an error in THD
Life time properties of allocation:
- two-phase allocation -- you can allocate many times, but can only free all at once
- if you control MEM_ROOT, you define when freeing happens.
- you can retain some part of memory when freeing to improve performance
- in most cases you use MEM_ROOTs controlled by system runtime and freed at pre-defined locations.
In this case you must know that you won't need the memory after it's freed when you use these memory roots.
[edit] Query Cache allocator
- manages a fixed pre-defined memory area
- repeats in properties modern system allocators
- used only in the query cache
- tightly coupled with the query cache block structure
- you only need to know that Query Cache uses its own allocator
[edit] Storage Engine mechanisms
Like QC allocator, are independent on the mechanisms employed by sql/ layer.
[edit] What do you do if you got a NULL pointer?
[edit] Allocation centers
Allocation center is a pre-defined allocation area whose life time is controlled by the server runtime.
- in most cases it is represented as an instance of class Query_arena
- sometimes is just an instance of MEM_ROOT object.
Examples of allocation centers:
- thd->main_mem_root: initialized when a new connection is created, freed at the end of each user request (end of dispatch_command())
- TABLE::mem_root -- initialized when a TABLE is added to the table cache. Freed when the table is removed from the cache. Contains
TABLE::record[0,1] and TABLE::field array.
- QUICK_SELECT::mem_root: initialized when this optimizer access method is chosen. Freed in the end of each scan (e.g.
in case of a correlated subqurery - many times per query).
Most important allocation centers:
- THD -- stores memory needed during handling of one user request
- Prepared_statement -- stores Parsed Tree of a prepared statement
- sp_head -- stores Parsed Trees of all statements in a procedure and more
[edit] How to choose the right allocation center
- Life cycle of your allocation must match the life cycle of the center
- if the amount of memory you need to allocate before a given allocation center is freed is not fixed, you can't
use this allocation center, since use will lead to memory hogging.
See also: Media:Query_arena_states.pdf
The Falcon memory manager
Falcon replaces the new operator with its own implementation, using pool-based memory allocator: MemMgr. The definitions of MemMgr are in MemMgr.h and the method code is in MemMgr.cpp.
I. Overview
a. Pool based
i. Record memory pool
ii. All other pool
b. Small block allocator
i. Blocking allocate
ii. Non-blocking release
c. Large block allocator
i. Blocking allocate
ii. Blocking release
d. Large vs. small policy set differently for different pools
i. General pool large is > 1024
ii. Record pool large is > 0
II. Memory block structure
a. Header
i. Common
1. pool
2. length (> 0 large, < 0 small)
3. for debug line and file
ii. Small block header
1. Common
2. Next block of same size
iii. Large block
1. Common
2. Prior and next in pool
3. Prior and next in size
4. Prior and next twins
b. Requested space
i. Address returned
ii. Rounded up to 16 byte boundary
c. Unused tail
III. Debug
a. Block initialization
i. ‘C’ allocated
ii. ‘E’ released
iii. ‘D’ guard bytes
b. File and line of allocation
IV. Small block pool management
a. Not released or recombined
b. Maintained in lists of same sized blocks
c. Vector of list heads created a pool creation
d. Allocation
i. Seize pool small block mutex
ii. If existing block, grab block at top of list
iii. Else if space in reserve hunk, allocate block from hunk
iv. Else allocate new hunk with malloc
v. Release pool mutex
e. Release – Interlocked compare / swap of list head
V. Large block management
a. Block links
i. Physical next and prior
ii. Free next and prior by size
iii. Free next and prior of same size (twins)
b. Large block allocation
i. Seize pool large block mutex
ii. Walk ‘size’ list
1. If a large enough free block exists
a. Remove it from the size list
b. If the tail is smaller than a large block keep it
c. Else place tail in the size list, fix next next and prior
2. Allocate new hunk to pool and use it as above
iii. Release large block mutex
c. Large block release
i. Seize large block mutex
ii. If next physical block is free, combine with it
iii. If prior physical block is free, combine with it
iv. If all blocks are combined, release pool
v. Link into size & twin lists
vi. Release large block mutex
VI. Straight malloc
a. Allocating hunks to pools
b. Memory allocation analysis
[edit] Questions & Answers
- Do you have concurrency issues?
- No, we lock the entire tree. We do though specify the type of the allocation in process. In essence there is only one mutex and all requestors queue up the allocation.
- What is the size of the small block allocation?
- There are many sizes - it's not fixed. We generally round up to the nearest 16 or 32 byte boundary.


