分类:
2011-12-16 20:42:41
原文地址:Uclibc中的malloc机制分析(五) 作者:xgr180
六:free的实现:
这里有个值得注意的地方:如果合并的内存区大于一个阀值,必须要把它还给内核,以节约内存。具体的见下面的分析:
void
free (void *mem)
{
free_to_heap (mem, &__malloc_heap);
}
跟踪进free_to_heap:
static void
free_to_heap (void *mem, struct heap *heap)
{
size_t size;
struct heap_free_area *fa;
//参数检查
if (unlikely (! mem))
return;
MALLOC_DEBUG (1, "free: 0x%lx (base = 0x%lx, total_size = %d)",
(long)mem, (long)MALLOC_BASE (mem), MALLOC_SIZE (mem));
//这两个宏在上面已经分析过了
size = MALLOC_SIZE (mem);
mem = MALLOC_BASE (mem);
__heap_lock (heap);
//把释放的内存加入heap
fa = __heap_free (heap, mem, size);
//如果FA中的空闲区超过 MALLOC_UNMAP_THRESHOLD。就要进行内存回收了
// MALLOC_UNMAP_THRESHOLD在代码中被定义成8个页面大小
if (HEAP_FREE_AREA_SIZE (fa) < MALLOC_UNMAP_THRESHOLD)
/* Nope, nothing left to do, just release the lock. */
__heap_unlock (heap);
else
{
//计算起始地址
unsigned long start = (unsigned long)HEAP_FREE_AREA_START (fa);
//计算结束地址
unsigned long end = (unsigned long)HEAP_FREE_AREA_END (fa);
//下面是sbrk,unmap两种方式的内存回收。这里不详细讨论。请关注本站的后续更新
#ifndef MALLOC_USE_SBRK
# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__
struct malloc_mmb *mmb, *prev_mmb;
unsigned long mmb_start, mmb_end;
# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
unsigned long unmap_start, unmap_end;
# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
#endif /* !MALLOC_USE_SBRK */
#ifdef MALLOC_USE_SBRK
if ((void *)end != sbrk (0))
{
MALLOC_DEBUG (-1, "not unmapping: 0x%lx - 0x%lx (%ld bytes)",
start, end, end - start);
__malloc_unlock_sbrk ();
__heap_unlock (heap);
return;
}
#endif
MALLOC_DEBUG (0, "unmapping: 0x%lx - 0x%lx (%ld bytes)",
start, end, end - start);
/* Remove FA from the heap. */
__heap_delete (heap, fa);
if (__heap_is_empty (heap))
{
/* 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
/* Release the heap lock; we're still holding the sbrk lock. */
__heap_unlock (heap);
/* Lower the brk. */
sbrk (start - end);
/* Release the sbrk lock too; now we hold no locks. */
__malloc_unlock_sbrk ();
#else /* !MALLOC_USE_SBRK */
# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__
MALLOC_MMB_DEBUG (1, "walking mmb list for region 0x%x[%d]...",
start, end - start);
prev_mmb = 0;
mmb = __malloc_mmapped_blocks;
while (mmb
&& ((mmb_end = (mmb_start = (unsigned long)mmb->mem) + mmb->size)
<= end))
{
MALLOC_MMB_DEBUG (1, "considering mmb at 0x%x: 0x%x[%d]",
(unsigned)mmb, mmb_start, mmb_end - mmb_start);
if (mmb_start >= start
&& (start == mmb_start
|| mmb_start - start > HEAP_MIN_FREE_AREA_SIZE))
{
struct malloc_mmb *next_mmb = mmb->next;
if (mmb_end != end && mmb_end + HEAP_MIN_FREE_AREA_SIZE > end)
/* There's too little space left at the end to deallocate
this block, so give up. */
break;
MALLOC_MMB_DEBUG (1, "unmapping mmb at 0x%x: 0x%x[%d]",
(unsigned)mmb, mmb_start, mmb_end - mmb_start);
if (mmb_start != start)
/* We're going to unmap a part of the heap that begins after
start, so put the intervening region back into the heap. */
{
MALLOC_MMB_DEBUG (0, "putting intervening region back into heap: 0x%x[%d]",
start, mmb_start - start);
__heap_free (heap, (void *)start, mmb_start - start);
}
MALLOC_MMB_DEBUG_INDENT (-1);
/* Unlink MMB from the list. */
if (prev_mmb)
prev_mmb->next = next_mmb;
else
__malloc_mmapped_blocks = next_mmb;
/* Start searching again from the end of this block. */
start = mmb_end;
__heap_unlock (heap);
/* Release the descriptor block we used. */
//从heap中释放FA
free_to_heap (mmb, &__malloc_mmb_heap);
/* Do the actual munmap. */
munmap ((void *)mmb_start, mmb_end - mmb_start);
__heap_lock (heap);
# ifdef __UCLIBC_HAS_THREADS__
prev_mmb = 0;
mmb = __malloc_mmapped_blocks;
# else
mmb = next_mmb;
# endif
}
else
{
prev_mmb = mmb;
mmb = mmb->next;
}
MALLOC_MMB_DEBUG_INDENT (-1);
}
if (start != end)
/* Hmm, well there's something we couldn't unmap, so put it back
into the heap. */
{
MALLOC_MMB_DEBUG (0, "putting tail region back into heap: 0x%x[%d]",
start, end - start);
__heap_free (heap, (void *)start, end - start);
}
MALLOC_MMB_DEBUG_INDENT (-1);
# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
/* 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);
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);
}
__heap_unlock (heap);
if (unmap_end > unmap_start)
/* Finally, actually unmap the memory. */
munmap ((void *)unmap_start, unmap_end - unmap_start);
# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
#endif /* MALLOC_USE_SBRK */
}
MALLOC_DEBUG_INDENT (-1);
}
注意
if (__heap_is_empty (heap))
{
/* 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;
}
这里指的是,如果要删除的是堆中的整块内存,造成了堆为空的情况,此时,应该保留heap的最低内存空间,即MALLOC_MIN_SIZE大小
看懂了malloc之后,free应该是很容易的,这里不再详细分析了
七:总结:
在malloc分配内存的时候,size是按sizeof(double),那也就是说如果申请的不足sizeof(double),malloc还是会分配sizeof(double)。
内存越界的探讨:如果分配的内存为一个char 而此时,malloc实际上已经分配了一个double大小,在double大小里使用返回的指针,应该是不会造成内存越界的。
我在写这篇文章的时候,参考了风微柳细(http://blog.iyi.cn/hily) 的一篇名为“uClibc 中的 malloc 和 free”的文章,并结合他提供的uclibc代码进行分析,这里先谢谢了。此外,它在文中还提到:
在free内存的时候:
“在检查到空闲链表为空时,为何不把模块初始化时产生的静态空闲域 initial_fa._fa 赋给 heap 中的 free_areas?而偏偏要到将要释放的空间里割出一块呢?”
这是因为静态数组的空间可能已经被使用了。然后全局heap指针上移,要释放的内存的起始位置并不是初始化堆的起始位置了。
举例。最先分得512字节,然后一直没释放,然后,又申请10个page的内存,然后再把10 个page的内存释放掉,此时引起内存回收。全局heap是指向512上面的空间了。
希望你能看到,并能帮助到你 ^_^