Heap - Memory manager
This page has been automatically translated using the Google Translate API services. We are working on improving texts. Thank you for your understanding and patience.
Optimized dynamic allocator and leaks control.
Functions
void | heap_start_mt (void) |
void | heap_end_mt (void) |
void | heap_verbose (...) |
void | heap_stats (...) |
bool_t | heap_leaks (void) |
byte_t* | heap_malloc (...) |
byte_t* | heap_calloc (...) |
byte_t* | heap_realloc (...) |
byte_t* | heap_aligned_malloc (...) |
byte_t* | heap_aligned_calloc (...) |
byte_t* | heap_aligned_realloc (...) |
void | heap_free (...) |
type* | heap_new (...) |
type* | heap_new0 (...) |
type* | heap_new_n (...) |
type* | heap_new_n0 (...) |
type* | heap_realloc_n (...) |
void | heap_delete (...) |
void | heap_delete_n (...) |
void | heap_auditor_add (...) |
void | heap_auditor_delete (...) |
Heap is a very efficient dynamic memory manager and auditor included in the core library and available for all projects based on NAppGUI (libraries and applications). It is common for applications to request a large number of small memory blocks to hold different objects (character strings, interface controls, structure instances, I/O buffer, etc). The strategy behind this manager is just to ask the operating system for memory pages of a certain size (64kb or more) using bmem_malloc and use them to solve several requests very efficiently.
- Use heap_new to dynamically create an object.
- Use heap_malloc to reserve a memory block.
- Use heap_delete to destroy an object.
- Use heap_free to free up a memory block.
1 2 3 4 5 6 7 8 |
Product *product = heap_new(Product); byte_t *memblock = heap_malloc(1024, "MyOwnBlock"); // Do something ... heap_delete(&product, Product); heap_free(&memblock, "MyOwnBlock"); |
Using Heap instead of system calls will provide us with certain benefits:
- Performance: A call to heap_malloc is solved only by increasing the value of a counter. heap_free it only updates the header of the affected page.
- Locality: Two consecutive calls to
heap_malloc()
are located in contiguous physical memory positions. This reduces the number of cache failures because, according to the locality principle, there is a high probability that two objects that are created together will be used together. - Memory leaks: heap points reservations and releases by object type. If necessary, will notify the programmer through Asserts or Log that there are objects not released. The great advantage of this auditor over other tools is that it is always being executed as part of the program. This exploits the temporal coherence, because if after a program change leaks are detected where there was not before, it is very likely that we can limit and detect the error, since it will be something we have just worked on.
- Statistics: We can obtain memory usage profiles (time/bytes). This can help us detect bottlenecks (especially at startup) or optimize page size.
1. Multi-thread memory
By default, heap is configured to work optimally in single-threaded applications. If we want several threads of the same process to reserve or release dynamic memory concurrently and safely, we must use:
- heap_start_mt to start multi-thread support.
- heap_end_mt to end multi-thread support.
The moment heap_start_mt is called, the synchronization mechanisms within the heap are activated to guarantee mutual exclusion to the memory manager until a call to heap_end_mt is received which will return to single-threaded operation mode. Successive calls to heap_start_mt
will accumulate, so it will remain in multi-threaded mode until all open blocks are closed (Listing 1). It is the responsibility of the programmer to use this pair of functions at those points of the program that require it.
Any section that begins withheap_start_mt
must be closed withheap_end_mt
.
There is no problem in activating multi-threaded support in single-threaded sections, except for a slight performance penalty.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// Single-threaded block ... ... heap_start_mt(); // Multi-threaded block ... heap_start_mt(); ... heap_end_mt(); // Continue multi-threaded block ... heap_end_mt(); // Single-threaded block ... |
2. How Heap Works
When a program starts, heap creates a default memory page. The first bytes are reserved as a header, a small structure that controls the state of the page. Each request is assigned sequentially within the same page, increasing the value of a pointer (Figure 1). When the page runs out of space, a new one is created bmem_malloc, which is linked to the previous one and labeled as the new default page (Figure 2). Each call to heap_free update the header with the number of blocks/bytes released (Figure 3). These blocks are not reused, otherwise the logic of heap would be complicated by slowing it down. The address of the header is stored at the end of each block, so do not have to iterate to locate it. When all the blocks on the page have been released, the entire page is destroyed by bmem_free and the pointers between neighboring pages restored (Figure 4).
Heap also counts the number of alloc/dealloc per object type using the parameter name
of heap_malloc. At the end of the execution of the program, if the application lacks memory leaks, it will write in Log a message like this:
1 2 3 4 5 6 7 8 |
[12:58:08] [OK] Heap Memory Staticstics [12:58:08] ============================ [12:58:08] Total a/dellocations: 1126, 1126 [12:58:08] Total bytes a/dellocated: 74611, 74611 [12:58:08] Max bytes allocated: 54939 [12:58:08] Effective reallocations: (0/34) [12:58:08] Real allocations: 2 pages of 65536 bytes [12:58:08] ============================ |
But if after the execution, the application has memory to be released, the message will be different:
1 2 3 4 5 6 7 8 9 10 11 |
[13:00:35] [FAIL] Heap Object Leaks!!! [13:00:35] =========================== [13:00:35] 'App' a/deallocations: 1, 0 (1 leaks) [13:00:35] 'String' a/deallocations: 414, 410 (4 leaks) [13:00:35] =========================== [13:00:35] [FAIL] Heap Global Memory Leaks!!! [13:00:35] ================================== [13:00:35] Total a/dellocations: 1161, 1156 (5 leaks) [13:00:35] Total bytes a/dellocated: 75704, 75596 (108 bytes) [13:00:35] Max bytes allocated: 54939 [13:00:35] ================================== |
That warns that we have an object App
and four String
without releasing. If in the previous execution there were no leaks, it is very likely that we can narrow the error without too much difficulty.
The heap auditor does not intend to replace more advanced memory testing tools, it is only a first filter that constantly alerts us during the development and test phase. Although the overhead that occurs at runtime is minimal, the auditor is completely disabled in the Release
configuration.
heap_start_mt ()
Start a multi-threaded section.
void
heap_start_mt(void);
Remarks
See Multi-thread memory.
heap_end_mt ()
End a multi-thread section.
void
heap_end_mt(void);
Remarks
See Multi-thread memory.
heap_verbose ()
Enable/disable memory auditor 'verbose' mode.
void heap_verbose(bool_t verbose);
verbose |
|
Remarks
By default FALSE
.
heap_stats ()
Enable/disable memory auditor statistics.
void heap_stats(bool_t stats);
stats |
|
Remarks
By default TRUE
.
heap_leaks ()
Returns TRUE
if there are memory leaks at the end of execution.
bool_t heap_leaks(void);
Return
TRUE
if leaks exist.
heap_malloc ()
Reserve a memory block with the default alignment sizeof(void*)
.
byte_t* heap_malloc(const uint32_t size, const char_t *name);
1 2 3 |
byte_t *mem = heap_malloc(1024 * 768, "PixelBuffer"); ... heap_free(&mem, 1024 * 768, "PixelBuffer"); |
size | Size in bytes of the block. |
name | Reference text for the auditor. |
Return
Pointer to the new block. Must be released with heap_free when it is no longer necessary.
Remarks
Use this function for generic blocks. For types use heap_new.
heap_calloc ()
Like heap_malloc, but initializing the block with 0s.
byte_t* heap_calloc(const uint32_t size, const char_t *name);
1 2 3 4 |
byte_t *mem = heap_calloc(256 * 256, "DrawCanvas"); /* mem = {0, 0, 0, 0, ..., 0}; */ ... heap_free(&mem, 256 * 256, "DrawCanvas"); |
size | Size in bytes of the block. |
name | Reference text for the auditor. |
Return
Pointer to the new block. Must be released with heap_free when it is no longer necessary.
Remarks
Use this function for generic blocks. For types use heap_new.
heap_realloc ()
Reallocs an existing memory block due to the expansion or reduction of it. Guarantees that the previous content of the block is preserved min(size, new_size)
. Try to do it without moving memory (in situ), but if it is not possible look for a new zone. It also guarantees the default alignment sizeof(void*)
if you have to reserve a new block.
byte_t* heap_realloc(byte_t *mem, const uint32_t size, const uint32_t new_size, const char_t *name);
1 2 3 4 5 |
byte_t *mem = heap_malloc(64, "ArrayData"); ... mem = heap_realloc(mem, 64, 128, ArrayData); ... heap_free(&mem, 128, "ArrayData"); |
mem | Pointer to the original block to relocate. |
size | Size in bytes of the original block |
new_size | New required size, in bytes. |
name | Reference text for the auditor. It must be the same as the one used in heap_malloc. |
Return
Pointer to the relocated block. It will be the same as the original pointer mem
if the relocation "in-situ" has been successful. Must be released with heap_free when it is no longer necessary.
Remarks
Use this function for generic blocks. For types use heap_realloc_n.
heap_aligned_malloc ()
Reserve a memory block with alignment.
byte_t* heap_aligned_malloc(const uint32_t size, const uint32_t align, const char_t *name);
1 2 3 |
byte_t *sse_data = heap_aligned_malloc(256 * 16, 16, "Vectors"); ... heap_free(&mem, 256 * 16, "Vectors"); |
size | Size in bytes of the block. |
align | Alignment. It must be power of 2. |
name | Reference text for the auditor. |
Return
Pointer to the new block. Must be released with heap_free when it is no longer necessary.
heap_aligned_calloc ()
Like heap_aligned_malloc , but initializing the block with 0s.
byte_t* heap_aligned_calloc(const uint32_t size, const uint32_t align, const char_t *name);
1 2 3 4 |
byte_t *sse_data = heap_aligned_calloc(256 * 16, 16, "Vectors"); /* see_data = {0, 0, 0, 0, ..., 0}; */ ... heap_free(&mem, 256 * 16, "Vectors"); |
size | Size in bytes of the block. |
align | Alignment. It must be power of 2. |
name | Reference text for the auditor. |
Return
Pointer to the new block. Must be released with heap_free when it is no longer necessary.
heap_aligned_realloc ()
Like heap_realloc, but guaranteeing memory alignment.
byte_t* heap_aligned_realloc(byte_t *mem, const uint32_t size, const uint32_t new_size, const uint32_t align, const char_t *name);
1 2 3 4 5 |
byte_t *sse_data = heap_aligned_malloc(256 * 16, 16, "Vectors"); ... sse_data = heap_aligned_realloc(sse_data, 256 * 16, 512 * 16, 16, "Vectors"); ... heap_free(&mem, 512 * 16, "Vectors"); |
mem | Pointer to the original block to relocate. |
size | Size in bytes of the original block |
new_size | New required size, in bytes. |
align | Alignment. It must be power of 2. |
name | Texto reference for the auditor. It must be the same as the one used in heap_aligned_malloc. |
Return
Pointer to the relocated block. Must be released with heap_free when it is no longer necessary.
heap_free ()
Free memory pointed by mem
, previously reserved by heap_malloc, heap_realloc or its equivalents with alignment.
void heap_free(byte_t **mem, const uint32_t size, const char_t *name);
mem | Double pointer to the block to be released. It will be set to |
size | Memory block size. |
name | Reference text for the auditor, must be the same as that used in heap_malloc. |
Remarks
Use this function for generic memory blocks. For types it uses heap_delete.
heap_new ()
Reserve memory for an object. The return pointer is converted to type
.
type* heap_new(type);
1 2 3 |
MyAppCtrl *ctrl = heap_new(MyAppCtrl); ... heap_delete(&ctrl, MyAppCtrl); |
type | Object type. |
Return
Pointer to the created object. It must be destroyed by heap_delete when it is no longer necessary.
heap_new0 ()
Like heap_new, but initializing the object with 0s.
type* heap_new0(type);
1 2 3 4 |
MyAppModel *model = heap_new0(MyAppModel); /* model = {0} */ ... heap_delete(&model, MyAppModel); |
type | Object type. |
Return
Pointer to the created object. It must be destroyed by heap_delete when it is no longer necessary.
heap_new_n ()
Reserve memory for n
objects. The return pointer is converted to type
.
type* heap_new_n(const uint32_t n, type);
1 2 3 |
Car *cars = heap_new_n(10, Car); ... heap_delete_n(&cars, 10, Car); |
n | Number of objects to create. |
type | Object type. |
Return
Pointer to the newly created array. It must be destroyed by heap_delete_n when it is no longer necessary.
heap_new_n0 ()
Like heap_new_n, but initializing the array with 0s.
type* heap_new_n0(const uint32_t n, type);
1 2 3 4 |
Car *cars = heap_new_n0(10, Car); /* cars = {0, 0, 0, ..., 0}; */ ... heap_delete_n(&cars, 10, Car); |
n | Number of objects to create. |
type | Object type. |
Return
Pointer to the newly created array. It must be destroyed by heap_delete_n when it is no longer necessary.
heap_realloc_n ()
Reallocs an array of objects created dynamically with heap_new_n or heap_new_n0. Guarantees that the previous objects remain unchanged min(size,new_size)
.
type* heap_realloc_n(type *mem, const uint32_t size, const uint32_t new_size, type);
1 2 3 4 5 6 |
Car *cars = heap_new_n(10, Car); ... cars = heap_realloc_n(cars, 10, 20, Car); /* cars[0]-[9] remains untouched. */ ... heap_delete_n(&cars, 20, Car); |
mem | Pointer to the array to relocate. |
size | Number of elements of the original array |
new_size | New required size (in elements). |
type | Object type. |
Return
Pointer to the relocated array. It must be destroyed by heap_delete_n when it is no longer necessary.
heap_delete ()
Releases the object targeted by obj
, previously reserved by heap_new or heap_new0.
void heap_delete(type **obj, type);
obj | Double pointer to the object to be released. It will be set to |
type | Object type. |
heap_delete_n ()
Free n
objects targeted by obj
, previously booked by heap_new_n, heap_new_n0.
void heap_delete_n(type **obj, const uint32_t n, type);
obj | Double pointer to the array to be released. It will be set to |
n | Number of objects to be released, the same as in the reservation. |
type | Object type. |
heap_auditor_add ()
Add an opaque object to the memory auditor.
void heap_auditor_add(const char_t *name);
name | Name of the object to add. |
heap_auditor_delete ()
Releases an opaque object from the memory auditor.
void heap_auditor_delete(const char_t *name);
name | Name of the object to release. |