Last active
March 4, 2024 18:17
-
-
Save AdamGold/5053eea2ba05867caa55dcc43d9bc560 to your computer and use it in GitHub Desktop.
glibc malloc struct
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/malloc/malloc.c#L1044 */ | |
struct malloc_chunk { | |
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */ | |
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */ | |
struct malloc_chunk* fd; /* double links -- used only if free. */ | |
struct malloc_chunk* bk; | |
/* Only used for large blocks: pointer to next larger size. */ | |
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ | |
struct malloc_chunk* bk_nextsize; | |
}; | |
/* | |
malloc_chunk details: | |
(The following includes lightly edited explanations by Colin Plumb.) | |
Chunks of memory are maintained using a `boundary tag' method as | |
described in e.g., Knuth or Standish. (See the paper by Paul | |
Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a | |
survey of such techniques.) Sizes of free chunks are stored both | |
in the front of each chunk and at the end. This makes | |
consolidating fragmented chunks into bigger chunks very fast. The | |
size fields also hold bits representing whether chunks are free or | |
in use. | |
An allocated chunk looks like this: | |
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Size of previous chunk, if unallocated (P clear) | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Size of chunk, in bytes |A|M|P| | |
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| User data starts here... . | |
. . | |
. (malloc_usable_size() bytes) . | |
. | | |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| (size of chunk, but used for application data) | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Size of next chunk, in bytes |A|0|1| | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
Where "chunk" is the front of the chunk for the purpose of most of | |
the malloc code, but "mem" is the pointer that is returned to the | |
user. "Nextchunk" is the beginning of the next contiguous chunk. | |
Chunks always begin on even word boundaries, so the mem portion | |
(which is returned to the user) is also on an even word boundary, and | |
thus at least double-word aligned. | |
Free chunks are stored in circular doubly-linked lists, and look like this: | |
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Size of previous chunk, if unallocated (P clear) | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
`head:' | Size of chunk, in bytes |A|0|P| | |
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Forward pointer to next chunk in list | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Back pointer to previous chunk in list | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Unused space (may be 0 bytes long) . | |
. . | |
. | | |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
`foot:' | Size of chunk, in bytes | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| Size of next chunk, in bytes |A|0|0| | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
The P (PREV_INUSE) bit, stored in the unused low-order bit of the | |
chunk size (which is always a multiple of two words), is an in-use | |
bit for the *previous* chunk. If that bit is *clear*, then the | |
word before the current chunk size contains the previous chunk | |
size, and can be used to find the front of the previous chunk. | |
The very first chunk allocated always has this bit set, | |
preventing access to non-existent (or non-owned) memory. If | |
prev_inuse is set for any given chunk, then you CANNOT determine | |
the size of the previous chunk, and might even get a memory | |
addressing fault when trying to do so. | |
The A (NON_MAIN_ARENA) bit is cleared for chunks on the initial, | |
main arena, described by the main_arena variable. When additional | |
threads are spawned, each thread receives its own arena (up to a | |
configurable limit, after which arenas are reused for multiple | |
threads), and the chunks in these arenas have the A bit set. To | |
find the arena for a chunk on such a non-main arena, heap_for_ptr | |
performs a bit mask operation and indirection through the ar_ptr | |
member of the per-heap header heap_info (see arena.c). | |
Note that the `foot' of the current chunk is actually represented | |
as the prev_size of the NEXT chunk. This makes it easier to | |
deal with alignments etc but can be very confusing when trying | |
to extend or adapt this code. | |
The three exceptions to all this are: | |
1. The special chunk `top' doesn't bother using the | |
trailing size field since there is no next contiguous chunk | |
that would have to index off it. After initialization, `top' | |
is forced to always exist. If it would become less than | |
MINSIZE bytes long, it is replenished. | |
2. Chunks allocated via mmap, which have the second-lowest-order | |
bit M (IS_MMAPPED) set in their size fields. Because they are | |
allocated one-by-one, each must contain its own trailing size | |
field. If the M bit is set, the other bits are ignored | |
(because mmapped chunks are neither in an arena, nor adjacent | |
to a freed chunk). The M bit is also used for chunks which | |
originally came from a dumped heap via malloc_set_state in | |
hooks.c. | |
3. Chunks in fastbins are treated as allocated chunks from the | |
point of view of the chunk allocator. They are consolidated | |
with their neighbors only in bulk, in malloc_consolidate. | |
*/ | |
/* | |
Bins | |
An array of bin headers for free chunks. Each bin is doubly | |
linked. The bins are approximately proportionally (log) spaced. | |
There are a lot of these bins (128). This may look excessive, but | |
works very well in practice. Most bins hold sizes that are | |
unusual as malloc request sizes, but are more usual for fragments | |
and consolidated sets of chunks, which is what these bins hold, so | |
they can be found quickly. All procedures maintain the invariant | |
that no consolidated chunk physically borders another one, so each | |
chunk in a list is known to be preceeded and followed by either | |
inuse chunks or the ends of memory. | |
Chunks in bins are kept in size order, with ties going to the | |
approximately least recently used chunk. Ordering isn't needed | |
for the small bins, which all contain the same-sized chunks, but | |
facilitates best-fit allocation for larger chunks. These lists | |
are just sequential. Keeping them in order almost never requires | |
enough traversal to warrant using fancier ordered data | |
structures. | |
Chunks of the same size are linked with the most | |
recently freed at the front, and allocations are taken from the | |
back. This results in LRU (FIFO) allocation order, which tends | |
to give each chunk an equal opportunity to be consolidated with | |
adjacent freed chunks, resulting in larger free chunks and less | |
fragmentation. | |
To simplify use in double-linked lists, each bin header acts | |
as a malloc_chunk. This avoids special-casing for headers. | |
But to conserve space and improve locality, we allocate | |
only the fd/bk pointers of bins, and then use repositioning tricks | |
to treat these as the fields of a malloc_chunk*. | |
*/ | |
typedef struct malloc_chunk *mbinptr; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment