內存釋放free
Public_fREe()
void public_fREe(Void_t* mem)
{
mstate ar_ptr; mchunkptr p;
/* chunk corresponding to mem */
void (*hook) (__malloc_ptr_t, __const __malloc_ptr_t)
= force_reg (__free_hook);
if (__builtin_expect (hook != NULL, 0))
{
(*hook)(mem, RETURN_ADDRESS (0));
return;
}
如果存在__free_hook,執(zhí)行hook函數(shù)。
if (mem == 0)
/* free(0) has no effect */
return;
p = mem2chunk(mem);
free NULL指針直接返回,然后根據(jù)內存指針獲得chunk的指針。
#if HAVE_MMAP
if (chunk_is_mmapped(p))
/* release mmapped memory. */
{
/* see if the dynamic brk/mmap threshold needs adjusting */
if (!mp_.no_dyn_threshold
&& p- >size > mp_.mmap_threshold
&& p- >size <= DEFAULT_MMAP_THRESHOLD_MAX)
{
mp_.mmap_threshold = chunksize (p);
mp_.trim_threshold = 2 * mp_.mmap_threshold;
}
munmap_chunk(p);
return;
}
#endif
如果當前free的chunk是通過mmap()分配的,調用munmap_chunk()函數(shù)unmap本chunk。 munmap_chunk()函數(shù)調用 munmap()函數(shù)釋放mmap()分配的內存塊。同時查看是否開啟了mmap分配閾值動態(tài)調整機制,默認是開啟的,如果當前free的chunk的大小大于設置的mmap分配閾值,小于mmap分配閾值的最大值,將當前chunk的大小賦值給mmap分配閾值,并修改mmap收縮閾值為mmap分配閾值的2倍。默認情況下mmap分配閾值與mmap收縮閾值相等,都為128KB。
ar_ptr = arena_for_chunk(p);
根據(jù)chunk指針獲得分配區(qū)的指針。
#ifdef ATOMIC_FASTBINS
_int_free(ar_ptr, p, 0);
如果開啟了ATOMIC_FASTBINS優(yōu)化,不需要對分配區(qū)加鎖,調用_int_free()函數(shù)執(zhí)行實際的釋放工作。
#else
#if THREAD_STATS
if(!mutex_trylock(&ar_ptr- >mutex))
++(ar_ptr- >stat_lock_direct);
else
{
(void)mutex_lock(&ar_ptr- >mutex);
++(ar_ptr- >stat_lock_wait);
}
#else
(void)mutex_lock(&ar_ptr- >mutex);
#endif
_int_free(ar_ptr, p);
(void)mutex_unlock(&ar_ptr- >mutex);
#endif
}
如果沒有開啟了ATOMIC_FASTBINS優(yōu)化,或去分配區(qū)的鎖,調用_int_free()函數(shù)執(zhí)行實際的釋放工作,然后對分配區(qū)解鎖。
_int_free()
static void
#ifdef ATOMIC_FASTBINS
_int_free(mstate av, mchunkptr p, int have_lock)
#else
_int_free(mstate av, mchunkptr p)
#endif
{
INTERNAL_SIZE_T size; /* its size */
mfastbinptr* fb; /* associated fastbin */
mchunkptr nextchunk; /* next contiguous chunk */
INTERNAL_SIZE_T nextsize; /* its size */
int nextinuse; /* true if nextchunk is used */
INTERNAL_SIZE_T prevsize; /* size of previous contiguous chunk */
mchunkptr bck; /* misc temp for linking */
mchunkptr fwd; /* misc temp for linking */
const char *errstr = NULL;
#ifdef ATOMIC_FASTBINS
int locked = 0;
#endif
size = chunksize(p);
獲取需要釋放的chunk的大小。
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
{
errstr = "free(): invalid pointer";
errout:
#ifdef ATOMIC_FASTBINS
if (! have_lock && locked)
(void)mutex_unlock(&av- >mutex);
#endif
malloc_printerr (check_action, errstr, chunk2mem(p));
return;
}
/* We know that each chunk is at least MINSIZE bytes in size. */
if (__builtin_expect (size < MINSIZE, 0))
{
errstr = "free(): invalid size";
goto errout;
}
check_inuse_chunk(av, p);
上面的代碼用于安全檢查,chunk的指針地址不能溢出,chunk的大小必須是按是按 2*SIZE_SZ 對齊的且大于等于MINSIZE。
/*
If eligible, place chunk on a fastbin so it can be found
and used quickly in malloc.
*/
if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
#if TRIM_FASTBINS
/*
If TRIM_FASTBINS set, don't place chunks
bordering top into fastbins
*/
&& (chunk_at_offset(p, size) != av- >top)
#endif)
{
if (__builtin_expect (chunk_at_offset (p, size)- >size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size))
>= av- >system_mem, 0))
{
#ifdef ATOMIC_FASTBINS
/* We might not have a lock at this point and concurrent modifications
of system_mem might have let to a false positive. Redo the test
after getting the lock. */
if (have_lock
|| ({ assert (locked == 0);
mutex_lock(&av- >mutex);
locked = 1;
chunk_at_offset (p, size)- >size <= 2 * SIZE_SZ
|| chunksize (chunk_at_offset (p, size)) >= av- >system_mem;
}))
#endif
{
errstr = "free(): invalid next size (fast)";
goto errout;
}
#ifdef ATOMIC_FASTBINS
if (! have_lock)
{
(void)mutex_unlock(&av- >mutex);
locked = 0;
}
#endif
}
如果當前free的chunk屬于fast bins,查看下一個相鄰的chunk的大小是否小于等于2*SIZE_SZ,下一個相鄰chunk的大小是否大于分配區(qū)所分配的內存總量,如果是,報錯。
if (__builtin_expect (perturb_byte, 0))
free_perturb (chunk2mem(p), size - SIZE_SZ);
set_fastchunks(av);
unsigned int idx = fastbin_index(size);
fb = &fastbin (av, idx);
設置當前分配區(qū)的fast bin flag,表示當前分配區(qū)的fast bins中已有空閑chunk。然后根據(jù)當前free的chunk大小獲取所屬的fast bin。
#ifdef ATOMIC_FASTBINS
mchunkptr fd;
mchunkptr old = *fb;
unsigned int old_idx = ~0u;
do
{
/* Another simple check: make sure the top of the bin is not the
record we are going to add (i.e., double free). */
if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
if (old != NULL)
old_idx = fastbin_index(chunksize(old));
p- >fd = fd = old;
}while ((old = catomic_compare_and_exchange_val_rel (fb, p, fd)) != fd);
if (fd != NULL && __builtin_expect (old_idx != idx, 0))
{
errstr = "invalid fastbin entry (free)";
goto errout;
}
如果開啟了ATOMIC_FASTBINS優(yōu)化,使用lock-free技術實現(xiàn)fast bin的單向鏈表插入操作。
#else
if (__builtin_expect (*fb == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
if (*fb != NULL
&& __builtin_expect (fastbin_index(chunksize(*fb)) != idx, 0))
{
errstr = "invalid fastbin entry (free)";
goto errout;
}
p- >fd = *fb; *fb = p
#endif
}
如果沒有開啟了ATOMIC_FASTBINS優(yōu)化,將free的chunk加入fast bin的單向鏈表中, 修改過鏈表表頭為當前free的chunk。同時需要校驗是否為double free錯誤,即free的chunk不能是表頭的chunk。同時校驗表頭不為NULL情況下,保證表頭chunk的所屬的fast bin與當前free的chunk所屬的fast bin相同。
else if (!chunk_is_mmapped(p))
{
#ifdef ATOMIC_FASTBINS
if (! have_lock)
{
# if THREAD_STATS
if(!mutex_trylock(&av- >mutex))
++(av- >stat_lock_direct);
else
{
(void)mutex_lock(&av- >mutex);
++(av- >stat_lock_wait);
}
# else
(void)mutex_lock(&av- >mutex);
# endif
locked = 1;
}
#endif
如果當前 free 的 chunk 不是通過 mmap()分配的,并且當前還沒有獲得分配區(qū)的鎖,獲取分配區(qū)的鎖。
nextchunk = chunk_at_offset(p, size);
獲取當前 free 的 chunk 的下一個相鄰的 chunk。
if (__builtin_expect (p == av- >top, 0))
{
errstr = "double free or corruption (top)";
goto errout;
}
/* Or whether the next chunk is beyond the boundaries of the arena. */
if (__builtin_expect (contiguous (av)
&& (char *) nextchunk
>= ((char *) av- >top + chunksize(av- >top)), 0))
{
errstr = "double free or corruption (out)";
goto errout;
}
/* Or whether the block is actually not marked used. */
if (__builtin_expect (!prev_inuse(nextchunk), 0))
{
errstr = "double free or corruption (!prev)";
goto errout;
}
安全檢查,當前 free 的 chunk 不能為 top chunk,因為 top chunk 為空閑 chunk,如果再次 free 就可能為double free 錯誤了。如果當前 free 的 chunk 是通過 sbrk()分配的,并且下一個相鄰的 chunk 的地址已經(jīng)超過了 top chunk 的結束地址,超過了當前分配區(qū)的結束地址,報錯。如果當前 free 的 chunk 的下一個相鄰 chunk 的 size 中標志位沒有標識當前 free chunk 為 inuse 狀態(tài),可能為 double free 錯誤。
nextsize = chunksize(nextchunk);
if (__builtin_expect (nextchunk- >size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (nextsize >= av- >system_mem, 0))
{
errstr = "free(): invalid next size (normal)";
goto errout;
}
if (__builtin_expect (perturb_byte, 0))
free_perturb (chunk2mem(p), size - SIZE_SZ);
計算當前 free 的 chunk 的下一個相鄰 chunk 的大小,該大小如果小于等于 2*SIZE_SZ 或是大于了分配區(qū)所分配區(qū)的內存總量,報錯。
/* consolidate backward */
if (!prev_inuse(p))
{
prevsize = p- >prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(p, bck, fwd);
}
如果當前 free 的 chunk 的前一個相鄰 chunk 為空閑狀態(tài),與前一個空閑 chunk 合并。計算合并后的 chunk 大小,并將前一個相鄰空閑 chunk 從空閑 chunk 鏈表中刪除。
if (nextchunk != av- >top)
{
/* get and clear inuse bit */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
如果與當前 free 的 chunk 相鄰的下一個 chunk 不是分配區(qū)的 top chunk,查看與當前 chunk 相鄰的下一個 chunk 是否處于 inuse 狀態(tài)。
/* consolidate forward */
if (!nextinuse)
{
unlink(nextchunk, bck, fwd);
size += nextsize;
}
else
clear_inuse_bit_at_offset(nextchunk, 0);
如果與當前 free 的 chunk 相鄰的下一個 chunk 處于 inuse 狀態(tài),清除當前 chunk 的 inuse 狀態(tài),則當前 chunk 空閑了。否則,將相鄰的下一個空閑 chunk 從空閑鏈表中刪除,并計算當前 chunk 與下一個 chunk 合并后的 chunk 大小。
bck = unsorted_chunks(av);
fwd = bck- >fd;
if (__builtin_expect (fwd- >bk != bck, 0))
{
errstr = "free(): corrupted unsorted chunks";
goto errout;
}
p- >fd = fwd;
p- >bk = bck;
if (!in_smallbin_range(size))
{
p- >fd_nextsize = NULL;
p- >bk_nextsize = NULL;
}
bck- >fd = p;
fwd- >bk = p;
將合并后的 chunk 加入 unsorted bin 的雙向循環(huán)鏈表中。如果合并后的 chunk 屬于 large bins,將 chunk 的 fd_nextsize 和 bk_nextsize 設置為 NULL。
set_head(p, size | PREV_INUSE);
set_foot(p, size);
設置合并后的空閑 chunk 大小,并標識前一個 chunk 處于 inuse 狀態(tài),因為必須保證不能有兩個相鄰的 chunk 都處于空閑狀態(tài)。然后將合并后的 chunk 加入 unsorted bin 的雙向循環(huán)鏈表中。最后設置合并后的空閑 chunk 的 foot,chunk 空閑時必須設置 foot,該 foot 處于下一個 chunk 的 prev_size 中,只有 chunk 空閑是 foot 才是有效的。
check_free_chunk(av, p);
}
/*
If the chunk borders the current high end of memory,
consolidate into top
*/
else
{
size += nextsize;
set_head(p, size | PREV_INUSE);
av- >top = p;
check_chunk(av, p);
}
如果當前 free 的 chunk 下一個相鄰的 chunk 為 top chunk,則將當前 chunk 合并入 top chunk,修改 top chunk 的大小。
if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD)
{
if (have_fastchunks(av))
malloc_consolidate(av);
如果合并后的 chunk 大小大于 64KB,并且 fast bins 中存在空閑 chunk,調用 malloc_consolidate()函數(shù)合并 fast bins 中的空閑 chunk 到 unsorted bin 中。
if (av == &main_arena)
{
#ifndef MORECORE_CANNOT_TRIM
if ((unsigned long)(chunksize(av- >top)) >=
(unsigned long)(mp_.trim_threshold))
sYSTRIm(mp_.top_pad, av);
#endif
如果當前分配區(qū)為主分配區(qū),并且top chunk的大小大于heap的收縮閾值,調用sYSTRIm() 函數(shù)首先 heap。
else
{
/* Always try heap_trim(), even if the top chunk is not
large, because the correspo nding heap might go away. */
heap_info *heap = heap_for_ptr(top(av));
assert(heap- >ar_ptr == av);
heap_trim(heap, mp_.top_pad);
}
}
如果為非主分配區(qū),調用 heap_trim()函數(shù)收縮非主分配區(qū)的 sub_heap。
#ifdef ATOMIC_FASTBINS
if (! have_lock)
{
assert (locked);
(void)mutex_unlock(&av- >mutex);
}
}
#endif
如果開啟了 ATOMIC_FASTBINS 優(yōu)化并獲得分配區(qū)的鎖,則對分配區(qū)解鎖。
else
{
#if HAVE_MMAP
munmap_chunk (p);
#endif
}
}
如果當前 free 的 chunk 是通過 mmap()分配的,調用 munma_chunk()釋放內存。
check
1.釋放chunk的時候,chunk必須按2 size_sz對齊,chunk1的大小必須大于等于MINSIZE且指針地址不能溢出。2.釋放fast bin的時候,chunk必須大于2SIZE_SZ且小于分配區(qū)所分配的內存總量。 3.釋放fast bin時,檢查當前free的chunk是否是fast bin中的鏈表頭(double free),以及當前free的chunk的size要與相應的fast bin一致。 4.釋放chunk的時候,chunk不能為top chunk,next chunk的地址不能超過當前分配區(qū)結束的地址,以及next chunk中chunk的inuse標志位需置1。 5.當前 free 的 chunk 的下一個相鄰 chunk 的大小需要大于 2*SIZE_SZ 且小于分配區(qū)所分配區(qū)的內存總量。 6.釋放的chunk通過unlink脫鏈,注意unlink的檢查。 7.將free掉的chunk放入unsorted bin中時,有unsorted_chunks(av)->fd->bk == unsorted_chunks(av)的檢查。
總結
free大致步驟: 1.判斷是否有_free_hook,有則執(zhí)行hook函數(shù)。 2.判斷是否是mmap chunk,是則調用munmap_chunk釋放(同時可能做調整閾值操作),否則調用_int_free()。 3.判斷是否是fast bin,是則插入fast bin鏈表中(inuse值不置0)。 4.如果不是mmap chunk,判斷相鄰chunk是否為空閑,是就合并(top chunk除外),將合并后的chunk插入unsorted bin鏈表中。 5.如果跟top chunk相鄰,則合并入top chunk。 6.依據(jù)heap的情況可能執(zhí)行合并fast bin、收縮閾值以及收縮sub_heap等操作。 7.判斷是否還存在mmap chunk,是則調用munmap_chunk釋放。
二次分析后總結
1.有_free_hook運行_free_hook,沒有則通過用戶指針-2獲得chunk的指針。 2.如果內存是通過mmap()分配的,調用munmap_chunk()函數(shù)unmap chunk,同時動態(tài)調整閾值。 3.通過size位獲得chunk的大小,size的大小必須是按 2 SIZE_SZ 對齊并且大于等于MINSIZE的。4.如果size在fastbin范圍內,通過chunk的size得到nextchunk的指針,并通過nextchunk的size域判斷nextchunk的size不能小于等于2SIZE_SZ,不能大于等于分配區(qū)所分配的內存總量。 5.根據(jù)chunk的size獲得fastbin對應的idx,根據(jù)idx獲得fastbin對應的表頭,這里要保證free的chunk不能是fastbin表頭的chunk,同時保證表頭chunk對應的idx與free chunk對應的idx相同。這時就將free chunk插入到表頭,通過fd指針鏈接原來的表頭chunk。 6.如果chunk不是mmap分配的,通過size獲得nextchunk的指針,這里保證free chunk不能是top chunk,nextchunk不能超過當前分配區(qū)的地址,并且nextchunk中的prev_size位不能為0。 7.通過nextchunk的size位獲得nextchunk的大小,保證nextchunk的大小在2*SIZE_SZ和分配區(qū)所分配的內存總量之間。 8.通過prev_size位判斷前一個chunk為空閑,則合并,通過prev_size獲得新的chunk的指針,并將前一個chunk從空閑鏈表中脫鏈。 9.通過下下個chunk的inuse位判斷nextchunk是否處于空閑狀態(tài),如果處于是使用狀態(tài),則將nextchunk里的inuse置0,即表明當前chunk空閑,如果nextchunk處于空閑狀態(tài),且nextchunk不是topchunk,則將nextchunk脫鏈,合并到chunk中。 10.將chunk加入到unsortedbin雙向循環(huán)鏈表的頭部,并設置對應指針,注意這里在chunk加入之前必須保證unsorted bin成環(huán)的。 11.設置合并后的空閑chunk大小,并標識前一個chunk處于inuse狀態(tài),再設置設置合并后的空閑chunk的foot。 12.如果nextchunk為top chunk,合并到top chunk中,并修改相應的size和標志位 13.依據(jù)heap的情況可能執(zhí)行合并fast bin、收縮閾值以及收縮sub_heap等操作。
-
內存
+關注
關注
8文章
2966瀏覽量
73812 -
Free
+關注
關注
0文章
16瀏覽量
11073 -
函數(shù)
+關注
關注
3文章
4277瀏覽量
62323 -
代碼
+關注
關注
30文章
4722瀏覽量
68230
發(fā)布評論請先 登錄
相關推薦
評論