Cross-platform C SDK logo

Cross-platform C SDK

Heap - Memory manager

❮ Back
Next ❯
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

voidheap_start_mt (void)
voidheap_end_mt (void)
voidheap_verbose (...)
voidheap_stats (...)
bool_theap_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 (...)
voidheap_free (...)
type*heap_new (...)
type*heap_new0 (...)
type*heap_new_n (...)
type*heap_new_n0 (...)
type*heap_realloc_n (...)
voidheap_delete (...)
voidheap_delete_n (...)
voidheap_auditor_add (...)
voidheap_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.

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:

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 with heap_start_mt must be closed with heap_end_mt.
There is no problem in activating multi-threaded support in single-threaded sections, except for a slight performance penalty.
Listing 1: Multi-thread sections.
 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).

Draw of a memory page when running malloc.
Figure 1: Reserve a new memory block with heap_malloc().
Request to the operating system for a new memory page.
Figure 2: Request to the operating system of a new empty page.
Draw of a memory page when running free.
Figure 3: Releasing a block of memory (only updates the header).
Destruction of a memory page when all its elements have been released.
Figure 4: Destroying the entire page.

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

TRUE to activate.

Remarks

By default FALSE.


heap_stats ()

Enable/disable memory auditor statistics.

void
heap_stats(bool_t stats);
stats

TRUE to activate.

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

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

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 NULL after the release.

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

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 NULL after the release.

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 NULL after the release.

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.

❮ Back
Next ❯