Inner Workings of Malloc and Free Professor Jennifer






































- Slides: 38
Inner Workings of. Malloc and Free Professor Jennifer Rexford COS 217 1
Goals of the Next Two Lectures • Understanding how the heap is managed o Malloc: allocate memory o Free: deallocate memory • Today: K&R implementation (Section 8. 7) o Maintaining a circular list of free blocks o Finding the first free block that is large enough o (Relevant pages of K&R available on Blackboard) • Next time: optimizing malloc and free o Best fit (or good fit), rather than first fit o Separate free lists based on block size • Programming outside confines of the type system o Reliance on error-prone pointer arithmetic and casting 2
Memory Layout: Heap char* string = “hello”; int i. Size; char* f(void) { char* p; i. Size = 8; p = malloc(i. Size); return p; } Needed when required memory size is not known until the program runs Text Data BSS Heap Stack 3
Using Malloc and Free • Types o void*: generic pointer to any type (can be converted to other pointer types) o size_t: unsigned integer type returned by sizeof() • void *malloc(size_t size) o Returns a pointer to space of size o … or NULL if the request cannot be satisfied o E. g. , int* x = (int *) malloc(sizeof(int)); • void free(void *p) o Deallocate the space pointed to by the pointer p o Pointer p must be pointer to space previously allocated o Do nothing if p is NULL 4
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); malloc(3); malloc(1); malloc(4); malloc(6); malloc(2); p 1 Heap Text Data } BSS Heap Stack 0 xffff 5
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); malloc(3); malloc(1); malloc(4); malloc(6); malloc(2); p 1 p 2 Heap Text Data } BSS Heap Stack 0 xffff 6
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); malloc(3); malloc(1); malloc(4); malloc(6); malloc(2); p 1 p 2 p 3 Heap Text Data } BSS Heap Stack 0 xffff 7
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); malloc(3); malloc(1); malloc(4); malloc(6); malloc(2); p 1 p 2 p 3 Heap Text Data } BSS Heap Stack 0 xffff 8
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); p 1 malloc(3); malloc(1); malloc(4); p 2 p 3 malloc(6); p 4 malloc(2); Heap Text Data } BSS Heap Stack 0 xffff 9
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); malloc(3); malloc(1); malloc(4); malloc(6); malloc(2); p 1 p 2 p 3 p 4 Heap Text Data } BSS Heap Stack 0 xffff 10
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); p 1 malloc(3); malloc(1); malloc(4); p 5, p 2 p 3 malloc(6); p 4 malloc(2); Heap Text Data } BSS Heap Stack 0 xffff 11
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); p 1 malloc(3); malloc(1); malloc(4); p 5, p 2 p 3 malloc(6); p 4 malloc(2); Heap Text Data } BSS Heap Stack 0 xffff 12
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); p 1 malloc(3); malloc(1); malloc(4); p 5, p 2 p 3 malloc(6); p 4 malloc(2); Heap Text Data } BSS Heap Stack 0 xffff 13
Heap: Dynamic Memory #include <stdlib. h> void *malloc(size_t size); void free(void *ptr); 0 char *p 1 = char *p 2 = char *p 3 = free(p 2); char *p 4 = free(p 3); char *p 5 = free(p 1); free(p 4); free(p 5); p 1 malloc(3); malloc(1); malloc(4); p 5, p 2 p 3 malloc(6); p 4 malloc(2); Heap Text Data } BSS Heap Stack 0 xffff 14
Challenges for Malloc and Free • Handling arbitrary request sequences o Memory may be allocated and freed in different order • Making immediate responses to requests o Cannot reorder/buffer requests to improve performance • Using only the heap o Data structures used by malloc/free stored on the heap • Aligning blocks (e. g. , on 8 -byte boundary) o Blocks must be able to hold any type of data object • Not modifying allocated blocks o Can only manipulate or change free blocks o Cannot modify other blocks after they are allocated 15
K&R Implementation (Section 8. 7) • Free list data structure o Free block with header (pointer and size) and user data o Aligning the header with the largest data type o Circular linked list of free blocks • Malloc function o Allocating memory in multiples of header size o Finding the first element in free list that is large enough o Allocating more memory from the OS, if needed • Free function o Putting a block back in the free list o Coalescing with adjacent blocks, if any 16
Free Block: Pointer, Size, Data • Free block in memory o Pointer to the next free block o Size of the free block o Free space (that can be allocated to user) p (address returned to the user) size user data 17
Free Block: Memory Alignment • Define a structure s for the header o Pointer to the next free block (ptr) o Size of the block (size) • To simplify memory alignment o Make all memory blocks a multiple of the header size o Ensure header is aligned with largest data type (e. g. , long) • Union: C technique forcing memory alignment o Variable that may hold objects of different types and sizes o Made large enough to hold the largest data type, e. g. , union Tag { int ival; float fval; char *sval; } u; 18
Free Block: Memory Alignment /* align to long boundary */ typedef long Align; union header { /* block header */ struct { union header *ptr; unsigned size; } s; Align x; /* Force alignment */ } typedef union header Header; 19
Allocate Memory in Units • Keep memory aligned o Requested size is rounded up to multiple of header size • Rounding up when asked for nbytes o Header has sizeof(Header) o Round: (nbytes + sizeof(Header) – 1)/sizeof(Header) • Example: o Suppose nbytes is 37 o And sizeof(header) is 16 bytes o Then (37 + 16 – 1)/16 is 52/16 which rounds down to 3 16 16 5 20
Allocate Memory in Units • Allocate space for the header, too o So, need to allocate one more unit of memory void *malloc(unsigned int nbytes) { unsigned nunits; nunits = (nbytes + sizeof(Header) – 1)/sizeof(Header) + 1; } … 21
Free List: Circular Linked List • Free blocks, linked together o Example: circular linked list • Keep list in order of increasing addresses o Makes it easier to coalesce adjacent free blocks Free list In use 22
Allocation Algorithms • Handling a request for memory (e. g. , malloc) o Find a free block that satisfies the request o Must have a “size” that is big enough, or bigger • Which block to return? o First-fit algorithm – Keep a linked list of free blocks – Search for the first one that is big enough o Best-fit algorithm – Keep a linked list of free blocks – Search for the smallest one that is big enough – Helps avoid fragmenting the free memory • K&R presents a simpler “first fit” algorithm 23
Malloc: First-Fit Algorithm • Start at the beginning of the list • Sequence through the list o Keep a pointer to the previous element • Stop when reaching first block that is big enough o Patch up the list o Return a block to the user prev p p 24
First Case: A Perfect Fit • Suppose the first fit is a perfect fit o Remove the element from the list o Link the previous element with the next element – prev->s. ptr = p->s. ptr o Return the current element to the user (skipping header) – return (void *) (p+1) p+1 prev p 25
Second Case: Block is Too Big • Suppose the block is bigger than requested o Divide the free block into two blocks o Keep first (now smaller) block in the free list – p->s. size -= nunits; o Allocate the second block to the user – p += p->s. size; p – p->s. size = nunits; p 26
Combining the Two Cases prevp = freep; /* start at beginning */ for (p=prevp->s. ptr; ; prevp=p, p=p->s. ptr) { if (p->s. size >= nunits) { if (p->s. size == nunits) /* fit */ prevp->s. ptr = p->s. ptr; else { /* too big, split in two */ p->s. size -= nunits; /* #1 */ p += p->s. size; /* #2 */ p->s. size = nunits; /* #2 */ } return (void *)(p+1); } 27 }
Beginning of the Free List • Benefit of making free list a circular list o Any element in the list can be the beginning o Don’t have to handle the “end” of the list as special o Optimization: make head be where last block was found prevp = freep; /* start at beginning */ for (p=prevp->s. ptr; ; prevp=p, p=p->s. ptr) { if (p->s. size >= nunits) { /* Do stuff on previous slide */ … freep = prevp; /* move the head */ return (void *) (p+1); } 28 }
Oops, No Block is Big Enough! • Cycling completely through the list o Check if the “for” loop returns back to the head of the list prevp = freep; /* start at beginning */ for (p=prevp->s. ptr; ; prevp=p, p=p->s. ptr) { if (p->s. size >= nunits) { /* Do stuff on previous slides */ … } if (p == freep) /* wrapped around */ Now, do something about it… } 29
What to Do When You Run Out • Ask the operating system for additional memory o Ask for a very large chunk of memory o … and insert the new chunk into the free list o. . . and then try again, this time successfully • Operating-system dependent o E. g. , sbrk command in UNIX o See the morecore() function for details if (p == freep) /* wrapped around */ if ((p = morecore(nunits)) == NULL) return NULL; /* none left */ 30
Free • User passes a pointer to the memory block o void free(void *ap); • Free function inserts block into the list o Identify the start of entry: bp = (Header *) ap – 1; o Find the location in the free list o Add to the list, coalescing entries, if needed bp ap 31
Scanning Free List for the Spot • Start at the beginning: p = freep; • Sequence through the list: p = p->s. ptr; • Stop at last entry before the to-be-freed element o (bp > p) && (bp < p->s. ptr); Free list In use p In use bp FREE ME 32
Corner Cases: Beginning or End • Check for wrap-around in memory o p >= p->s. ptr; • See if to-be-freed element is located there o (bp > p) || (bp < p->s. ptr); Free list bp FREE ME p In use 33
Inserting Into Free List • New element to add to free list: bp • Insert in between p and p->s. ptr o bp->s. ptr = p->s. ptr; o p->s. ptr = bp; • But, there may be opportunities to coalesce bp p p->s. ptr 34
Coalescing With Neighbors • Scanning the list finds the location for inserting o Pointer to to-be-freed element: bp o Pointer to previous element in free list: p • Coalescing into larger free blocks o Check if contiguous to upper and lower neighbors Free list In use p In use lower bp FREE ME upper 35
Coalesce With Upper Neighbor • Check if next part of memory is in the free list o if (bp + bp->s. size == p->s. ptr) • If so, make into one bigger block o Larger size: bp->s. size += p->s. ptr->s. size; o Copy next pointer: bp->s. ptr = p->s. ptr; • Else, simply point to the next free element o bp->s. ptr = p->s. ptr; p bp lower p->s. ptr upper 36
Coalesce With Lower Neighbor • Check if previous part of memory is in the free list o if (p + p->s. size == bp) • If so, make into one bigger block o Larger size: p->s. size += bp->s. size; o Copy next pointer: p->s. ptr = bp->s. ptr; p bp lower p->s. ptr upper 37
Conclusions • Elegant simplicity of K&R malloc and free o Simple header with pointer and size in each free block o Simple linked list of free blocks o Relatively small amount of code (~25 lines each) • Limitations of K&R functions in terms of efficiency o Malloc requires scanning the free list – To find the first free block that is big enough o Free requires scanning the free list – To find the location to insert the to-be-freed block • Next lecture, and programming assignment #4 o Making malloc and free more efficient o Note: you are encouraged to do assignment in pairs 38