.Net GC從空閑列表里面分配對象
1.前言
空閑列表(free_list)實質上是GC經(jīng)過垃圾回收之后形成的碎片空間,它的空間可以進行重新分配新的對象。本篇看下它的重新分配過程。
2.概述
當進行一個對象分配的時候,如果是慢速分配(JIT_New)的時候,它會查找空閑列表里面是否有足夠的空間來分配這個對象。如果有就把這個對象放到空閑列表里面去。
CLR堆棧如下:
> WKS::gc_heap::a_fit_free_list_p C++
WKS::gc_heap::soh_try_fit C++
WKS::gc_heap::allocate_soh C++
WKS::gc_heap::try_allocate_more_space C++
WKS::gc_heap::allocate_more_space C++
WKS::gc_heap::allocate C++
WKS::GCHeap::Alloc C++
Alloc C++
AllocateObject C++
JIT_New C++
//此處省略一萬行
RtlUserThreadStart() 未知
著重看下這個a_fit_free_list_p函數(shù),它就是進行空閑列表分配,關鍵代碼如下:
BOOL gc_heap::a_fit_free_list_p (int gen_number,
size_t size,
alloc_context* acontext,
uint32_t flags,
int align_const)
{
BOOL can_fit = FALSE;
//獲取當前收集代地址
generation* gen = generation_of (gen_number);
//獲取當前收集代 空閑列表一直
allocator* gen_allocator = generation_allocator (gen);
//遍歷冫空閑列表里面的bucket,找到何時的空間存儲對象
for (unsigned int a_l_idx = gen_allocator->first_suitable_bucket(size); a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
{
//索引的空閑列表的頭部指針
uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
uint8_t* prev_free_item = 0;
//如果獲取到的空閑列表空間不為0
while (free_list != 0)
{
//獲取當前索引空閑了free_list里面bucket的長度
size_t free_list_size = unused_array_size (free_list);
//如果需要分配對象長度小于free_lis里面獲取的長度,那么表示對象可以放入空閑列表
if ((size + Align (min_obj_size, align_const)) <= free_list_size)
{
//unlink,斷開鏈接。意思是從空閑列表里面把空間拿出來,因為它操作的是鏈表。
// 把獲取到的free_lsit的bucket長度從free_lsit鏈表里面拿掉,也就是個鏈表操作
gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
//計算出對象需要的空間也就是參數(shù)size,最終需要分配的長度
size_t limit = limit_from_size (size, flags, free_list_size, gen_number, align_const);
//收集代靜態(tài)數(shù)據(jù)new_allocation減去對象最終分配空間大小
dd_new_allocation (dynamic_data_of (gen_number)) -= limit;
//空閑列表頭部指針+對象占用空間大小,指向的是剩余的空間地址
uint8_t* remain = (free_list + limit);
// 當前代的空閑列表大小減去分配對象的大小,剩余值remain_size
size_t remain_size = (free_list_size - limit);
//如果剩余值,大于或者等于一個空閑列表的大小
if (remain_size >= Align(min_free_list, align_const))
{
//重新把它作為空閑列表,放入free_list的bucket里面去
make_unused_array (remain, remain_size);
gen_allocator->thread_item_front (remain, remain_size);
assert (remain_size >= Align (min_obj_size, align_const));
}
else //如果剩余值小于
{
//這里直接把剩余值給分配對象大小得了,也就是上面計算的分配對象大小+剩余的空間
limit += remain_size;
}
//代的空閑空間減去對象需要的空間
generation_free_list_space (gen) -= limit;
assert ((ptrdiff_t)generation_free_list_space (gen) >= 0);
//把alloc_context指向計算的空間,對象就分配到alloc_context指向的地址
adjust_limit_clr (free_list, limit, size, acontext, flags, 0, align_const, gen_number);
can_fit = TRUE;
goto end;
}
}
}
end:
return can_fit;
}
以上整體的思路是
1.先找到當前垃圾回收的代
2.通過這個代找到次代的空閑列表
3.遍歷循環(huán)空閑列表里的number_of_buckets,以找到合適的空間放置需要分配的對象
4.如果找到了此空間,就設置alloc_context指向此空間。對象分配就通過alloc_context來分配。
它這個整體上是操作空閑列表管理類,然后通過空閑列表管理類給對象分配空間。