MySQL 8.4.3
Source Code Documentation
|
The MEM_ROOT is a simple arena, where allocations are carved out of larger blocks. More...
#include <my_alloc.h>
Classes | |
struct | Block |
Public Member Functions | |
MEM_ROOT () | |
MEM_ROOT (PSI_memory_key key, size_t block_size) | |
MEM_ROOT (const MEM_ROOT &)=delete | |
noexcept | m_current_free_start (other.m_current_free_start) |
noexcept | m_current_free_end (other.m_current_free_end) |
noexcept | m_block_size (other.m_block_size) |
noexcept | m_orig_block_size (other.m_orig_block_size) |
noexcept | m_max_capacity (other.m_max_capacity) |
noexcept | m_allocated_size (other.m_allocated_size) |
noexcept | m_error_for_capacity_exceeded (other.m_error_for_capacity_exceeded) |
noexcept | m_error_handler (other.m_error_handler) |
noexcept | m_psi_key (other.m_psi_key) |
MEM_ROOT & | operator= (const MEM_ROOT &)=delete |
MEM_ROOT & | operator= (MEM_ROOT &&other) noexcept |
~MEM_ROOT () | |
void * | Alloc (size_t length) |
Allocate memory. More... | |
template<class T , class... Args> | |
T * | ArrayAlloc (size_t num, Args... args) |
Allocate 'num' objects of type T, and initialize them to a default value that is created by passing the supplied args to T's constructor. More... | |
void | Claim (bool claim) |
Claim all the allocated memory for the current thread in the performance schema. More... | |
void | Clear () |
Deallocate all the RAM used. More... | |
void | ClearForReuse () |
Similar to Clear(), but anticipates that the block will be reused for further allocations. More... | |
bool | inited () const |
Whether the constructor has run or not. More... | |
void | set_max_capacity (size_t max_capacity) |
Set maximum capacity for this MEM_ROOT. More... | |
size_t | get_max_capacity () const |
Return maximum capacity for this MEM_ROOT. More... | |
void | set_error_for_capacity_exceeded (bool report_error) |
Enable/disable error reporting for exceeding the maximum capacity. More... | |
bool | get_error_for_capacity_exceeded () const |
Return whether error is to be reported when maximum capacity exceeds for MEM_ROOT. More... | |
void | set_error_handler (void(*error_handler)(void)) |
Set the error handler on memory allocation failure (or nullptr for none). More... | |
size_t | allocated_size () const |
Amount of memory we have allocated from the operating system, not including overhead. More... | |
void | set_block_size (size_t block_size) |
Set the desired size of the next block to be allocated. More... | |
Raw interface | |
Peek(), ForceNewBlock() and RawCommit() together define an alternative interface to MEM_ROOT, for special uses. The raw interface gives direct access to the underlying blocks, allowing a user to bypass the normal alignment requirements and to write data directly into them without knowing beforehand exactly how long said data is going to be, while still retaining the convenience of block management and automatic freeing. It generally cannot be combined with calling Alloc() as normal; see RawCommit. The raw interface, unlike Alloc(), is not affected by running under ASan or Valgrind. | |
std::pair< char *, char * > | Peek () const |
Get the bounds of the currently allocated memory block. More... | |
bool | ForceNewBlock (size_t minimum_length) |
Allocate a new block of at least 'minimum_length' bytes; usually more. More... | |
void | RawCommit (size_t length) |
Mark the first N bytes as the current block as used. More... | |
bool | Contains (void *ptr) const |
Returns whether this MEM_ROOT contains the given pointer, ie., whether it was given back from Alloc(n) (given n >= 1) at some point. More... | |
Public Attributes | |
noexcept | __pad0__: m_current_block(other.m_current_block) |
Private Member Functions | |
Block * | AllocBlock (size_t wanted_length, size_t minimum_length) |
Allocate a new block of the given length (plus overhead for the block header). More... | |
void * | AllocSlow (size_t length) |
Allocate memory that doesn't fit into the current free block. More... | |
Static Private Member Functions | |
static void | FreeBlocks (Block *start) |
Free all blocks in a linked list, starting at the given block. More... | |
Private Attributes | |
Block * | m_current_block = nullptr |
The current block we are giving out memory from. More... | |
char * | m_current_free_start = &s_dummy_target |
Start (inclusive) of the current free block. More... | |
char * | m_current_free_end = &s_dummy_target |
End (exclusive) of the current free block. More... | |
size_t | m_block_size |
Size of the next block we intend to allocate. More... | |
size_t | m_orig_block_size |
The original block size the user asked for on construction. More... | |
size_t | m_max_capacity = 0 |
Maximum amount of memory this MEM_ROOT can hold. More... | |
size_t | m_allocated_size = 0 |
Total allocated size for this MEM_ROOT. More... | |
bool | m_error_for_capacity_exceeded = false |
If enabled, exceeding the capacity will lead to a my_error() call. More... | |
void(* | m_error_handler )(void) = nullptr |
PSI_memory_key | m_psi_key = 0 |
Static Private Attributes | |
static char | s_dummy_target |
Something to point on that exists solely to never return nullptr from Alloc(0). More... | |
The MEM_ROOT is a simple arena, where allocations are carved out of larger blocks.
Using an arena over plain malloc gives you two main advantages:
Thus, if you need to do many small allocations that all are to have roughly the same lifetime, the MEM_ROOT is probably a good choice. The flip side is that no memory is freed until the arena is destroyed, and no destructors are run (although you can run them manually yourself).
This specific implementation works by allocating exponentially larger blocks each time it needs more memory (generally increasing them by 50%), which guarantees O(1) total calls to malloc and free. Only one free block is ever used; as soon as there's an allocation that comes in that doesn't fit, that block is stored away and never allocated from again. (There's an exception for allocations larger than the block size; see AllocSlow for details.)
The MEM_ROOT is thread-compatible but not thread-safe. This means you cannot use the same instance from multiple threads at the same time without external synchronization, but you can use different MEM_ROOTs concurrently in different threads.
For C compatibility reasons, MEM_ROOT is a struct, even though it is logically a class and follows the style guide for classes.
|
inline |
|
inline |
|
delete |
|
inline |
|
inline |
Allocate memory.
Will return nullptr if there's not enough memory, or if the maximum capacity is reached.
Note that a zero-length allocation can return any pointer, including nullptr or a pointer that has been given out before. The current implementation takes some pains to make sure we never return nullptr (although it might return a bogus pointer), since there is code that assumes nullptr always means 'out of memory', but you should not rely on it, as it may change in the future.
The returned pointer will always be 8-aligned.
|
inline |
Amount of memory we have allocated from the operating system, not including overhead.
|
private |
Allocate a new block of the given length (plus overhead for the block header).
If the MEM_ROOT is near capacity, it may allocate less memory than wanted_length, but if it cannot allocate at least minimum_length, will return nullptr.
|
private |
Allocate memory that doesn't fit into the current free block.
|
inline |
Allocate 'num' objects of type T, and initialize them to a default value that is created by passing the supplied args to T's constructor.
If args is empty, value-initialization is used. For primitive types, like int and pointers, this means the elements will be set to the equivalent of 0 (or false or nullptr).
If the constructor throws an exception, behavior is undefined.
We don't use new[], as it can put extra data in front of the array.
void MEM_ROOT::Claim | ( | bool | claim | ) |
Claim all the allocated memory for the current thread in the performance schema.
Use when transferring responsibility for a MEM_ROOT from one thread to another.
void MEM_ROOT::Clear | ( | ) |
Deallocate all the RAM used.
The MEM_ROOT itself continues to be valid, so you can make new calls to Alloc() afterwards.
void MEM_ROOT::ClearForReuse | ( | ) |
Similar to Clear(), but anticipates that the block will be reused for further allocations.
This means that even though all the data is gone, one memory block (typically the largest allocated) will be kept and made immediately available for calls to Alloc() without having to go to the OS for new memory. This can yield performance gains if you use the same MEM_ROOT many times. Also, the block size is not reset.
|
inline |
Returns whether this MEM_ROOT contains the given pointer, ie., whether it was given back from Alloc(n) (given n >= 1) at some point.
This means it will be legally accessible until the next Clear() or ClearForReuse() call.
bool MEM_ROOT::ForceNewBlock | ( | size_t | minimum_length | ) |
Allocate a new block of at least 'minimum_length' bytes; usually more.
This holds no matter how many bytes are free in the current block. The new block will always become the current block, ie., the next call to Peek() will return the newly allocated block. (This is different from Alloc(), where it is possible to allocate a new block that is not made into the current block.)
|
staticprivate |
Free all blocks in a linked list, starting at the given block.
|
inline |
Return whether error is to be reported when maximum capacity exceeds for MEM_ROOT.
|
inline |
Return maximum capacity for this MEM_ROOT.
|
inline |
Whether the constructor has run or not.
This exists solely to support legacy code that memset()s the MEM_ROOT to all zeros, which wants to distinguish between that state and a properly initialized MEM_ROOT. If you do not run the constructor nor do memset(), you are invoking undefined behavior.
noexcept MEM_ROOT::m_allocated_size | ( | other. | m_allocated_size | ) |
noexcept MEM_ROOT::m_block_size | ( | other. | m_block_size | ) |
noexcept MEM_ROOT::m_current_free_end | ( | other. | m_current_free_end | ) |
noexcept MEM_ROOT::m_current_free_start | ( | other. | m_current_free_start | ) |
noexcept MEM_ROOT::m_error_for_capacity_exceeded | ( | other. | m_error_for_capacity_exceeded | ) |
noexcept MEM_ROOT::m_error_handler | ( | other. | m_error_handler | ) |
noexcept MEM_ROOT::m_max_capacity | ( | other. | m_max_capacity | ) |
noexcept MEM_ROOT::m_orig_block_size | ( | other. | m_orig_block_size | ) |
|
inline |
|
inline |
Get the bounds of the currently allocated memory block.
Assuming no other MEM_ROOT calls are made in the meantime, you can start writing into this block and then call RawCommit() once you know how many bytes you actually needed. (This is useful when e.g. packing rows.)
|
inline |
Mark the first N bytes as the current block as used.
WARNING: If you use RawCommit() with a length that is not a multiple of 8, you cannot use Alloc() afterwards! The exception is that if EnsureSpace() has just returned, you've got a new block, and can use Alloc() again.
|
inline |
Set the desired size of the next block to be allocated.
Note that future allocations will grow in size over this, although a Clear() will reset the size again.
|
inline |
Enable/disable error reporting for exceeding the maximum capacity.
If error reporting is enabled, an error is flagged to indicate that the capacity is exceeded. However, allocation will still happen for the requested memory.
report_error | whether the error should be reported |
|
inline |
Set the error handler on memory allocation failure (or nullptr for none).
The error handler is called called whenever my_malloc() failed to allocate more memory from the OS (which causes my_alloc() to return nullptr).
|
inline |
Set maximum capacity for this MEM_ROOT.
Whenever the MEM_ROOT has allocated more than this (not including overhead), and the free block is empty, future allocations will fail.
max_capacity | Maximum capacity this mem_root can hold |
noexcept MEM_ROOT::__pad0__ |
|
private |
Total allocated size for this MEM_ROOT.
Does not include overhead for block headers or malloc overhead, since especially the latter is impossible to quantify portably.
|
private |
Size of the next block we intend to allocate.
The current block we are giving out memory from.
nullptr if none.
|
private |
End (exclusive) of the current free block.
|
private |
Start (inclusive) of the current free block.
|
private |
If enabled, exceeding the capacity will lead to a my_error() call.
|
private |
|
private |
Maximum amount of memory this MEM_ROOT can hold.
A value of 0 implies there is no limit.
|
private |
The original block size the user asked for on construction.
|
private |
|
staticprivate |
Something to point on that exists solely to never return nullptr from Alloc(0).