_libc_free
-
先检查是否有钩子函数,有则调用并返回。
-
如果是
mmap
分配的 chunk,则用munmap
将其释放,如果释放的chunk
大小大于mmap
分配的阈值,且未关闭动态调整阈值开关,则调整一下mmap
的阈值为当前chunk
大小。 -
调用
_int_free
释放内存。 -
若
chunk_size
<128B
,且 chunk 不与top chunk
相邻则放入fast bins
中,这里不会加锁,而是用的CAS
,返回。 -
加锁分配区,前一个 chunk 若空闲,则合并。
-
后一个 chunk 若为
top chunk
,则将其合并到top chunk
中,若不是也合并,将其放到unosrted bin
。 -
如果合并的
chunk
大于64KB
,则开始整合fast bins
到unsorted bin
,若top chunk
的大小 大过 收缩阈值了,默认为128K
,则收缩堆,也就是还给内核。 -
也就是说 释放内存回内核 需要两个条件,
chunk_size
>64KB
,且top chunk
大于收缩阈值,则释放。
void
__libc_free (void *mem)
{
mstate ar_ptr;
mchunkptr p; /* chunk corresponding to mem */
void (*hook) (void *, const void *)
= atomic_forced_read (__free_hook);
if (__builtin_expect (hook != NULL, 0))
{
(*hook)(mem, RETURN_ADDRESS (0));
return;
}
if (mem == 0) /* free(0) has no effect */
return;
p = mem2chunk (mem);
if (chunk_is_mmapped (p)) /* release mmapped memory. */
{
/* See if the dynamic brk/mmap threshold needs adjusting.
Dumped fake mmapped chunks do not affect the threshold. */
if (!mp_.no_dyn_threshold
&& chunksize_nomask (p) > mp_.mmap_threshold
&& chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX
&& !DUMPED_MAIN_ARENA_CHUNK (p))
{
mp_.mmap_threshold = chunksize (p);
mp_.trim_threshold = 2 * mp_.mmap_threshold;
LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,
mp_.mmap_threshold, mp_.trim_threshold);
}
munmap_chunk (p);
return;
}
MAYBE_INIT_TCACHE ();
ar_ptr = arena_for_chunk (p);
_int_free (ar_ptr, p, 0);
}
__libc_free
和__lib_malloc
一样首先查看是否有__free_hook
函数,如果有就直接调用,这里假设没有默认函数可用。接下来通过mem2chunk
将虚拟内存的指针mem
转换为对应的chunk指针p
,
因为一个使用中的chunk结构体只使用其prev_size
和size
字段,因此这里只需要减去2*SIZE_SZ
。接下来,chunk_is_mmapped
用来检查size最低三位中的标志位,判断该chunk是否是由mmap分配的,如果是,就调用munmap_chunk
释放该chunk并返回,在调用munmap_chunk
之前,需要更新全局的mmap阀值和收缩阀值。
munmap_chunk
static void
munmap_chunk (mchunkptr p)
{
size_t pagesize = GLRO (dl_pagesize);
INTERNAL_SIZE_T size = chunksize (p);
assert (chunk_is_mmapped (p));
/* Do nothing if the chunk is a faked mmapped chunk in the dumped
main arena. We never free this memory. */
if (DUMPED_MAIN_ARENA_CHUNK (p))
return;
uintptr_t mem = (uintptr_t) chunk2mem (p);
uintptr_t block = (uintptr_t) p - prev_size (p);
size_t total_size = prev_size (p) + size;
/* Unfortunately we have to do the compilers job by hand here. Normally
we would test BLOCK and TOTAL-SIZE separately for compliance with the
page size. But gcc does not recognize the optimization possibility
(in the moment at least) so we combine the two values into one before
the bit test. */
if (__glibc_unlikely ((block | total_size) & (pagesize - 1)) != 0
|| __glibc_unlikely (!powerof2 (mem & (pagesize - 1))))
malloc_printerr ("munmap_chunk(): invalid pointer");
atomic_decrement (&mp_.n_mmaps);
atomic_add (&mp_.mmapped_mem, -total_size);
/* If munmap failed the process virtual memory address space is in a
bad shape. Just leave the block hanging around, the process will
terminate shortly anyway since not much can be done. */
__munmap ((char *) block, total_size);
}
首先将chunk转换为size,获得前一个chunk的指针block
,计算这两个chunk的size
之和至total_size
,接着对全局结构mp_
进行相应的设置后,就通过__munmap
释放这两个chunk。根据malloc的源码可知,由mmap分配的chunk是独立的,大部分情况下,p->prev_size
为0,因此这里还是释放一个chunk,特殊情况下需要释放两个chunk,特殊情况请参考_int_malloc
中的代码。
__munmap
再往下就是系统调用了。