struct Span {
//span首个页面的基数树ID
PageID start; // Starting page number
//页面数量
Length length; // Number of pages in span
//双向链表辅助指针
Span* next; // Used when in link list
Span* prev; // Used when in link list
//内存对象
void* objects; // Linked list of free objects
//正在使用中的内存对象数量
unsigned int refcount :16; // Number of non-free objects
//内存区间ID
unsigned int sizeclass :8; // Size-class for small objects (or 0)
//位置(或者使用状态)
unsigned int location :2; // Is the span on a freelist, and if so, which?
unsigned int sample :1; // Sampled object?
// What freelist the span is on: IN_USE if on none, or normal or returned
enum {
IN_USE, ON_NORMAL_FREELIST, ON_RETURNED_FREELIST
};
};
void CentralFreeList::Populate() {
// Release central list lock while operating on pageheap
lock_.Unlock();
//查询sizemap,获得该区间每一次拉去多少个页面
const size_t npages = Static::sizemap()->class_to_pages(size_class_);
Span* span;
{
SpinLockHolder h(Static::pageheap_lock());
//去pageheap申请页面
span = Static::pageheap()->New(npages);
//初始化span的sizeclass,并对Span的每一个页面在基数树中的索引完成初始化
//在前面提到,基数树对每一个页面对应一个指针,在pageheap申请之后,只会
//将Span首个和最后一个页面的指针初始化,
//这里对Span的所有页面的指针都完成初始化,之所以这样,是因为在返还内存时
//需要首先根据内存地址定位内存页,然后定位Span,最后才将内存回收到具体的Span中
if (span)
Static::pageheap()->RegisterSizeClass(span, size_class_);
}
if (span == NULL) {
MESSAGE("tcmalloc: allocation failed", npages << kPageShift);
lock_.Lock();
return;
}
ASSERT(span->length == npages);
// Cache sizeclass info eagerly. Locking is not necessary.
// (Instead of being eager, we could just replace any stale info
// about this span, but that seems to be no better in practice.)
//作cache
for (int i = 0; i < npages; i++) {
Static::pageheap()->CacheSizeClass(span->start + i, size_class_);
}
// Split the block into pieces and add to the free-list
// TODO: coloring of objects to avoid cache conflicts?
void** tail = &span->objects;
//获得Span所管理内存的起始地址和终止地址
char* ptr = reinterpret_cast<char*>(span->start << kPageShift);
char* limit = ptr + (npages << kPageShift);
//获得每一个内存对象的大小
const size_t size = Static::sizemap()->ByteSizeForClass(size_class_);
int num = 0;
//对这些内存建一个链表
while (ptr + size <= limit) {
*tail = ptr;
tail = reinterpret_cast<void**>(ptr);
ptr += size;
//num表示一个划分了多少个对象
num++;
}
ASSERT(ptr <= limit);
*tail = NULL;
//全部内存对象都未使用
span->refcount = 0; // No sub-object in use yet
// Add span to list of non-empty spans
lock_.Lock();
//将span放入nonempty_链表
tcmalloc::DLL_Prepend(&nonempty_, span);
//CentralFreeList增加了num个内存对象
counter_ += num;
}
//通过内存地址定位Span
Span* MapObjectToSpan(void* object) {
//通过内存地址定位页ID
const PageID p = reinterpret_cast<uintptr_t>(object) >> kPageShift;
//通过查询基数树,来获得特定的Span
Span* span = Static::pageheap()->GetDescriptor(p);
return span;
}