Dynamic Memory Allocation: Alan L. Cox Alc@rice - Edu
Dynamic Memory Allocation: Alan L. Cox Alc@rice - Edu
Alan L. Cox
[email protected]
Heap Memory
Unused
0x00000000
Cox / Fagan Dynamic Memory Allocation 6
Malloc Package
#include <stdlib.h>
void *malloc(size_t size)
If successful:
• Returns a pointer to a memory block of at least size bytes,
(typically) aligned to 8-byte boundary
• If size == 0, returns NULL (or a unique pointer)
If unsuccessful: returns NULL (0) and sets errno
void free(void *ptr)
Returns the block pointed at by ptr to pool of available
memory
ptr must come from a previous call to malloc or realloc
void *realloc(void *ptr, size_t size)
Changes size of block pointed at by ptr and returns
pointer to new block
Contents of new block unchanged up to the minimum of
the old and new sizes
Free word
Allocated block Free block
(4 words) (3 words) Allocated word
p1 = malloc(4*sizeof(int))
p2 = malloc(5*sizeof(int))
p3 = malloc(6*sizeof(int))
free(p2)
p4 = malloc(2*sizeof(int))
Allocators:
Can’t control number or size of allocated blocks
Must respond immediately to all allocation requests
• i.e., can’t reorder or buffer requests
Must allocate blocks from free memory
• i.e., can only place allocated blocks in free memory
Must align blocks so they satisfy all alignment
requirements
• 8-byte alignment for libc malloc on many systems
Can only manipulate and modify free memory
Can’t move the allocated blocks once they are allocated
• i.e., compaction is not allowed
Primary goals
Good time performance for malloc and free
• Ideally should take constant time (not always possible)
• Should certainly not take linear time in the number of blocks
Good space utilization
• User allocated structures should use most of the heap
• Want to minimize “fragmentation”
Internal Internal
payload fragmentation
fragmentation
p2 = malloc(5*sizeof(int))
p3 = malloc(6*sizeof(int))
free(p2)
p4 = malloc(7*sizeof(int))
oops!
External fragmentation depends on the pattern of future
requests, and thus is difficult to measure
Cox / Fagan Dynamic Memory Allocation 16
Implementation Issues
How do we know how much memory to free just
given a pointer?
How do we keep track of the free blocks?
What do we do with the extra space when
allocating a structure that is smaller than the free
block it is placed in?
How do we pick a block to use for allocation –
many might fit?
How do we reinsert a freed block?
p0
free(p0)
p1 = malloc(1)
Standard method
Keep the length of a block in the word preceding
the block.
• This word is often called the header field or header
Requires an extra word for every allocated block
p0 = malloc(4*sizeof(int)) p0
5 4 6 2
5 4 6 2
4 4 6 2
p
void addblock(ptr p, int len) {
int newsize = ((len + 1) >> 1) << 1; // add 1 and round up
int oldsize = *p & ~0x1; // mask out low bit
*p = newsize | 0x1; // set new length
if (newsize < oldsize)
*(p+newsize) = oldsize - newsize; // set length in remaining
} // part of block
addblock(p, 4)
4 4 4 2 2
Cox / Fagan Dynamic Memory Allocation 22
Implicit List: Freeing a Block
Simplest implementation:
Only need to clear allocated flag
• void free_block(ptr p) { *p = *p & ~0x1}
But can lead to “false fragmentation”
4 4 4 2 2
free(p) p
4 4 4 2 2
malloc(5*sizeof(int))
Oops!
There is enough free space, but the allocator won’t
be able to find it!
4 4 4 2 2
free(p) p
4 4 6 2
4 4 4 4 6 6 4 4
m1 1 m1 1
m1 1 m1 1
n 1 n 0
n 1 n 0
m2 1 m2 1
m2 1 m2 1
m1 1 m1 1
m1 1 m1 1
n 1 n+m2 0
n 1
m2 0
m2 0 n+m2 0
m1 0 n+m1 0
m1 0
n 1
n 1 n+m1 0
m2 1 m2 1
m2 1 m2 1
m1 0 n+m1+m2 0
m1 0
n 1
n 1
m2 0
m2 0 n+m1+m2 0
5 4 6 2
5 4 6 2
Forward links
A B
4 4 4 4 6 6 4 4 4 4
C
Back links
pred succ
pred succ
After:
(with splitting) free block
Case 2: a-a-f p s
Splice out next, coalesce
self and next, and add to before:
beginning of free list a self f
p s h
after:
a f
p s h
after:
f a
p1 s1 p2 s2
Case 4: f-a-f
Splice out prev and next, before:
coalesce with self, and add
to beginning of list
f self f
p1 s1 p2 s2 h
after:
f
Cox / Fagan Dynamic Memory Allocation 38
Explicit List Summary
5 4 6 2
5 4 6 2
5-8
9-16
5 4 6 2
5 4 6 2
Changes API
malloc() and free()
must take a region
as an argument
Cox / Fagan Dynamic Memory Allocation 46
For More Info on Allocators
Many options:
Data structures for keeping track of free blocks
Block choice policy
Splitting & coalescing policies
Root nodes
unreachable
(garbage)
Overall idea
Maintain a free list of unallocated blocks
Maintain a count of the number of references to
each allocated block
To allocate, grab a sufficiently large block from the
free list
When a count goes to zero, deallocate it
a = cons(10,empty)
b = cons(20,a)
a = b
b = …
a = …
a = cons(10,empty) a 1 10
b = cons(20,a)
a = b
b = …
a = …
a = cons(10,empty) a 2 10
b = cons(20,a)
a = b
b = …
a = … b 1 20
a = cons(10,empty) 1 10
b = cons(20,a)
a = b
a
b = …
a = … b 2 20
a = cons(10,empty) 1 10
b = cons(20,a)
a = b
a
b = …
a = … 1 20
a = cons(10,empty) 1 10
b = cons(20,a)
a = b
b = …
a = … 0 20
a = cons(10,empty) 0 10
b = cons(20,a)
a = b
b = …
a = …
a = cons(10,empty)
b = cons(20,a)
a = b
b = …
a = …
Disadvantages:
Managing & testing counts is generally expensive
• Can optimize
Doesn’t work with cycles!
• Approach can be modified to work, with difficulty
Advantage:
Simple
• Easily adapted, e.g., for parallel or distributed GC
Overall idea
Maintain a free list of unallocated blocks
To allocate, grab a sufficiently large block from
free list
When no such block exists, GC
• Should find blocks & put them on free list
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Unmarked= Marked=
Root pointers:
Heap:
Free list:
Advantages:
No space overhead for reference counts
No time overhead for reference counts
Handles cycles
Disadvantage:
Noticeable pauses for GC
Overall idea:
Maintain From and To spaces in heap
To allocate, get sequentially next block in From
space
• No free list!
When From space full, GC into To space
• Swap From & To names
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Uncopied= Copied=
Root pointers:
From:
To:
Root pointers:
To:
From:
Advantages:
Only one pass over data
Only touches reachable data
Little space overhead per data item
Very simple allocation
“Compacts” data
Handles cycles
Disadvantages:
Noticeable pauses for GC
Double the basic heap size
Goal
Allow GC in C-like languages
Virtual Memory