MySQL 8.3.0
Source Code Documentation
MEM_ROOT Struct Reference

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_ROOToperator= (const MEM_ROOT &)=delete
 
MEM_ROOToperator= (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

BlockAllocBlock (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

Blockm_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...
 

Detailed Description

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:

  • Allocation is very cheap (only a few CPU cycles on the fast path).
  • You do not need to keep track of which memory you have allocated, as it will all be freed when the arena is destroyed.

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.

Constructor & Destructor Documentation

◆ MEM_ROOT() [1/3]

MEM_ROOT::MEM_ROOT ( )
inline

◆ MEM_ROOT() [2/3]

MEM_ROOT::MEM_ROOT ( PSI_memory_key  key,
size_t  block_size 
)
inline

◆ MEM_ROOT() [3/3]

MEM_ROOT::MEM_ROOT ( const MEM_ROOT )
delete

◆ ~MEM_ROOT()

MEM_ROOT::~MEM_ROOT ( )
inline

Member Function Documentation

◆ Alloc()

void * MEM_ROOT::Alloc ( size_t  length)
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.

◆ allocated_size()

size_t MEM_ROOT::allocated_size ( ) const
inline

Amount of memory we have allocated from the operating system, not including overhead.

◆ AllocBlock()

MEM_ROOT::Block * MEM_ROOT::AllocBlock ( size_t  wanted_length,
size_t  minimum_length 
)
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.

◆ AllocSlow()

void * MEM_ROOT::AllocSlow ( size_t  length)
private

Allocate memory that doesn't fit into the current free block.

◆ ArrayAlloc()

template<class T , class... Args>
T * MEM_ROOT::ArrayAlloc ( size_t  num,
Args...  args 
)
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.

◆ Claim()

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.

◆ Clear()

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.

Note
One can call this function either with a MEM_ROOT initialized with the constructor, or with one that's memset() to all zeros. It's also safe to call this multiple times with the same mem_root.

◆ ClearForReuse()

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.

◆ Contains()

bool MEM_ROOT::Contains ( void *  ptr) const
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.

◆ ForceNewBlock()

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.)

Returns
true Allocation failed (possibly due to size restrictions).

◆ FreeBlocks()

void MEM_ROOT::FreeBlocks ( Block start)
staticprivate

Free all blocks in a linked list, starting at the given block.

◆ get_error_for_capacity_exceeded()

bool MEM_ROOT::get_error_for_capacity_exceeded ( ) const
inline

Return whether error is to be reported when maximum capacity exceeds for MEM_ROOT.

◆ get_max_capacity()

size_t MEM_ROOT::get_max_capacity ( ) const
inline

Return maximum capacity for this MEM_ROOT.

◆ inited()

bool MEM_ROOT::inited ( ) const
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.

◆ m_allocated_size()

noexcept MEM_ROOT::m_allocated_size ( other.  m_allocated_size)

◆ m_block_size()

noexcept MEM_ROOT::m_block_size ( other.  m_block_size)

◆ m_current_free_end()

noexcept MEM_ROOT::m_current_free_end ( other.  m_current_free_end)

◆ m_current_free_start()

noexcept MEM_ROOT::m_current_free_start ( other.  m_current_free_start)

◆ m_error_for_capacity_exceeded()

noexcept MEM_ROOT::m_error_for_capacity_exceeded ( other.  m_error_for_capacity_exceeded)

◆ m_error_handler()

noexcept MEM_ROOT::m_error_handler ( other.  m_error_handler)

◆ m_max_capacity()

noexcept MEM_ROOT::m_max_capacity ( other.  m_max_capacity)

◆ m_orig_block_size()

noexcept MEM_ROOT::m_orig_block_size ( other.  m_orig_block_size)

◆ m_psi_key()

noexcept MEM_ROOT::m_psi_key ( other.  m_psi_key)
inline

◆ operator=() [1/2]

MEM_ROOT & MEM_ROOT::operator= ( const MEM_ROOT )
delete

◆ operator=() [2/2]

MEM_ROOT & MEM_ROOT::operator= ( MEM_ROOT &&  other)
inlinenoexcept

◆ Peek()

std::pair< char *, char * > MEM_ROOT::Peek ( ) const
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.)

◆ RawCommit()

void MEM_ROOT::RawCommit ( size_t  length)
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.

◆ set_block_size()

void MEM_ROOT::set_block_size ( size_t  block_size)
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.

◆ set_error_for_capacity_exceeded()

void MEM_ROOT::set_error_for_capacity_exceeded ( bool  report_error)
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.

Parameters
report_errorwhether the error should be reported

◆ set_error_handler()

void MEM_ROOT::set_error_handler ( void(*)(void)  error_handler)
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).

◆ set_max_capacity()

void MEM_ROOT::set_max_capacity ( size_t  max_capacity)
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.

Parameters
max_capacityMaximum capacity this mem_root can hold

Member Data Documentation

◆ __pad0__

noexcept MEM_ROOT::__pad0__

◆ m_allocated_size

size_t MEM_ROOT::m_allocated_size = 0
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.

◆ m_block_size

size_t MEM_ROOT::m_block_size
private

Size of the next block we intend to allocate.

◆ m_current_block

Block* MEM_ROOT::m_current_block = nullptr
private

The current block we are giving out memory from.

nullptr if none.

◆ m_current_free_end

char* MEM_ROOT::m_current_free_end = &s_dummy_target
private

End (exclusive) of the current free block.

◆ m_current_free_start

char* MEM_ROOT::m_current_free_start = &s_dummy_target
private

Start (inclusive) of the current free block.

◆ m_error_for_capacity_exceeded

bool MEM_ROOT::m_error_for_capacity_exceeded = false
private

If enabled, exceeding the capacity will lead to a my_error() call.

◆ m_error_handler

void(* MEM_ROOT::m_error_handler) (void) = nullptr
private

◆ m_max_capacity

size_t MEM_ROOT::m_max_capacity = 0
private

Maximum amount of memory this MEM_ROOT can hold.

A value of 0 implies there is no limit.

◆ m_orig_block_size

size_t MEM_ROOT::m_orig_block_size
private

The original block size the user asked for on construction.

◆ m_psi_key

PSI_memory_key MEM_ROOT::m_psi_key = 0
private

◆ s_dummy_target

char MEM_ROOT::s_dummy_target
staticprivate

Something to point on that exists solely to never return nullptr from Alloc(0).


The documentation for this struct was generated from the following files: