malloc
) to acquire virtual memory (VM) at run time.
Implementation: very simple
Allocate cost: linear time worst case
Free cost: constant time worst case (even with coalescing)
Memory overhead: depends on placement policy
Not used in practice for malloc
/free
because of linear time allocation
The concepts of splitting and boundary tag coalescing are general to all allocators
Insertion policy: where in the free list do you put a newly freed block?
Insert freed blocks so that free list blocks are always in address order:
\(addr(prev) < addr(curr) < addr(next)\)Pro: studies suggest fragmentation is lower than LIFO/FIFO
Use circular, doubly linked list
Support multiple approaches with single data structure
Allocate is linear time in number of free blocks instead of all blocks (much faster)
Slightly more complicated allocate and free because we need to splice blocks in and out of the list
Often have separate classes for each small size
For larger sized: one class for each size \([2^{i} + 1, 2^{i+1}]\)
Given an array of free lists, each one for some size class
sbrk()
)The classic scanf
bug
int val;
...
scanf("%d", val);
Assuming that heap data is initialized to zero
/* return y = Ax */
int *matvec(int **A, int *x) {
int *y = malloc(N*sizeof(int)); // <-- here
int i, j;
for (i=0; i<N; i++)
for (j=0; j<N; j++)
y[i] += A[i][j]*x[j];
return y;
}
Can avoid by using calloc
Allocating the (possibly) wrong sized object
int **p;
p = malloc(N*sizeof(int));
for (i=0; i<N; i++) {
p[i] = malloc(M*sizeof(int));
}
Can you spot the bug?
Off-by-one errors
char **p;
p = malloc(N*sizeof(int *));
for (i=0; i<=N; i++) { // <-- here
p[i] = malloc(M*sizeof(int));
}
char *p;
p = malloc(strlen(s));
strcpy(p,s);
Not checking the max string size
char s[8]; // <-- too small
int i;
gets(s); /* reads “123456789” from stdin */
Basis for classic buffer overflow attacks
Misunderstanding pointer arithmetic
int *search(int *p, int val) {
while (p && *p != val)
p += sizeof(int); // <-- here
return p;
}
Referencing a pointer instead of the object it points to
int *BinheapDelete(int **binheap, int *size) {
int *packet;
packet = binheap[0];
binheap[0] = binheap[*size - 1];
*size--; // <-- here
Heapify(binheap, *size, 0);
return(packet);
}
Forgetting that local variables disappear when a function returns
int *foo () {
int val;
return &val;
}
x = malloc(N*sizeof(int));
<manipulate x>
free(x);
y = malloc(M*sizeof(int));
<manipulate y>
free(x);
x = malloc(N*sizeof(int));
<manipulate x>
free(x);
...
y = malloc(M*sizeof(int));
for (i=0; i<M; i++)
y[i] = x[i]++;
foo() {
int *x = malloc(N*sizeof(int));
...
return;
}
Freeing only part of a data structure
struct list {
int val;
struct list *next;
};
foo() {
struct list *head = malloc(sizeof(struct list));
head->val = 0;
head->next = NULL;
<create and manipulate the rest of the list>
...
free(head);
return;
}
gdb
valgrind
glibc malloc
contains checking codeGarbage collection: automatic reclamation of heap-allocated storage; application never has to explicitly free memory
Common in many dynamic languages
Variants (“conservative” garbage collectors) exist for C and C++
int
and then back again)malloc
until you “run out of space”new(n)
: returns pointer to new block with all locations clearedread(b, i)
: read location i
of block b
into registerwrite(b, i, v)
: write v
into location i
of block b
b[-1]
, for block b
is_ptr(p)
: determines whether p
is a pointerlength(b)
: returns the length of block b
, not including the headerget_roots()
: returns all the rootsMark using depth-first traversal of the memory graph
ptr mark(ptr p) {
if (!is_ptr(p)) return; // if not pointer -> do nothing
if (markBitSet(p)) return; // if already marked -> do nothing
setMarkBit(p); // set the mark bit
for (i=0; i < length(p); i++) // for each word in p’s block
mark(p[i]); // make recursive call
return;
}
Sweep using lengths to find next block
ptr sweep(ptr p, ptr end) {
while (p < end) { // for entire heap
if markBitSet(p) // did we reach this block?
clearMarkBit(); // yes -> so just clear mark bit
else if (allocateBitSet(p)) // never reached: is it allocated?
free(p); // yes -> its garbage, free it
p += length(p+1); // goto next block
}
is_ptr()
determines if a word is a pointer by checking if it points to an allocated block of memory