1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
/*
* libc/stdlib/malloc/free.c -- free function
*
* Copyright (C) 2002 NEC Corporation
* Copyright (C) 2002 Miles Bader <miles@gnu.org>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License. See the file COPYING.LIB in the main
* directory of this archive for more details.
*
* Written by Miles Bader <miles@gnu.org>
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include "malloc.h"
#include "heap.h"
/* Try to release the free-area FA in HEAP back to the system. */
static void
unmap_free_area (struct heap *heap, struct heap_free_area *fa)
{
unsigned long start, end;
#ifndef MALLOC_USE_SBRK
unsigned long unmap_start, unmap_end;
#endif
end = (unsigned long)HEAP_FREE_AREA_END (fa);
#ifdef MALLOC_USE_SBRK
/* When using sbrk, we only shrink the heap from the end. It would be
possible to allow _both_ -- shrinking via sbrk when possible, and
otherwise shrinking via munmap, but this results in holes in memory
that prevent the brk from every growing back down; since we only ever
grow the heap via sbrk, this tends to produce a continuously growing
brk (though the actual memory is unmapped), which could eventually run
out of address space. Note that `sbrk(0)' shouldn't normally do a
system call, so this test is reasonably cheap. */
if ((void *)end != sbrk (0))
{
MALLOC_DEBUG (" not unmapping: 0x%lx - 0x%lx (%d bytes)\n",
(unsigned long)HEAP_FREE_AREA_START (fa),
(unsigned long)HEAP_FREE_AREA_END (fa),
fa->size);
return;
}
#endif
start = (unsigned long)HEAP_FREE_AREA_START (fa);
MALLOC_DEBUG (" unmapping: 0x%lx - 0x%lx (%ld bytes)\n",
start, end, end - start);
/* Remove FA from the heap. */
__heap_unlink_free_area (heap, fa);
if (!fa->next && !fa->prev)
/* We want to avoid the heap from losing all memory, so reserve a bit.
This test is only a heuristic -- the existance of another free area,
even if it's smaller than MALLOC_MIN_SIZE, will cause us not to
reserve anything. */
{
/* Put the reserved memory back in the heap; we asssume that
MALLOC_UNMAP_THRESHOLD is greater than MALLOC_MIN_SIZE, so we use
the latter unconditionally here. */
__heap_free (heap, (void *)start, MALLOC_MIN_SIZE);
start += MALLOC_MIN_SIZE;
}
#ifdef MALLOC_USE_SBRK
sbrk (start - end);
#else /* !MALLOC_USE_SBRK */
/* MEM/LEN may not be page-aligned, so we have to page-align them, and
return any left-over bits on the end to the heap. */
unmap_start = MALLOC_ROUND_UP_TO_PAGE_SIZE (start);
unmap_end = MALLOC_ROUND_DOWN_TO_PAGE_SIZE (end);
/* We have to be careful that any left-over bits are large enough to
return. Note that we _don't check_ to make sure there's room to
grow/shrink the start/end by another page, we just assume that the
unmap threshold is high enough so that this is always safe (i.e., it
should probably be at least 3 pages). */
if (unmap_start > start)
{
if (unmap_start - start < HEAP_MIN_FREE_AREA_SIZE)
unmap_start += MALLOC_PAGE_SIZE;
__heap_free (heap, (void *)start, unmap_start - start);
}
if (end > unmap_end)
{
if (end - unmap_end < HEAP_MIN_FREE_AREA_SIZE)
unmap_end -= MALLOC_PAGE_SIZE;
__heap_free (heap, (void *)unmap_end, end - unmap_end);
}
if (unmap_end > unmap_start)
munmap ((void *)unmap_start, unmap_end - unmap_start);
#endif /* MALLOC_USE_SBRK */
}
void free (void *mem)
{
if (mem)
{
size_t size;
struct heap_free_area *fa;
mem -= MALLOC_ALIGNMENT;
size = *(size_t *)mem;
MALLOC_DEBUG ("free: 0x%lx (base = 0x%lx, total_size = %d)\n",
(long)mem + MALLOC_ALIGNMENT, (long)mem, size);
fa = __heap_free (&__malloc_heap, mem, size);
/* Now we check to see if FA has grown big enough that it should be
unmapped. */
if (HEAP_FREE_AREA_SIZE (fa) >= MALLOC_UNMAP_THRESHOLD)
/* Get rid of it. */
unmap_free_area (&__malloc_heap, fa);
}
}
|