Internally, every chunk - whether allocated or free - is stored in a malloc_chunk
structure. The difference is how the memory space is used.
When space is allocated from the heap using a function such as malloc()
, a pointer to a heap address is returned. Every chunk has additional metadata that it has to store in both its used and free states.
The chunk has two sections - the metadata of the chunk (information about the chunk) and the user data, where the data is actually stored.
The size
field is the overall size of the chunk, including metadata. It must be a multiple of 8
, meaning the last 3 bits of the size
are 0
. This allows the flags A
, M
and P
to take up that space, with M
being the 3rd-last bit of size
, A
the 2nd-last and P
the last.
The flags have special uses:
P
is the PREV_INUSE
flag, which is set when the previous adjacent chunk (the chunk ahead) is in use
M
is the IS_MMAPPED
flag, which is set when the chunk is allocated via mmap()
rather than a heap mechanism such as malloc()
A
is the NON_MAIN_ARENA
flag, which is set when the chunk is not located in main_arena
; we will get to Arenas in a later section, but in essence every created thread is provided a different arena (up to a limit) and chunks in these arenas have the A
bit set
prev_size
is set if the previous adjacent chunk is free, as calculated by P
being 0
. If it is not, the heap saves space and prev_size
is part of the previous chunk's user data. If it is, then prev_size
stores the size of the previous chunk.
Free chunks have additional metadata to handle the linking between them.
This can be seen in the malloc_state
struct: