ReleaseToCentralCache將某個freelist中的obj全部釋放回centralcache,函數比較簡單,如下所示,主要是通過一個while迴圈將freelist中的obj按照num_objects_to_move一批批的通過PopRange
pop出來,然後轉移到central_cache,central_cache調用InsertRange將每一批obj插入相應的freelist。
void ThreadCache::ReleaseToCentralCache(FreeList* src, size_t cl, int N) {
ASSERT(src == &list_[cl]);
if (N > src->length()) N = src->length();
size_t delta_bytes = N * Static::sizemap()->ByteSizeForClass(cl);
// We return prepackaged chains of the correct size to the central cache.
// TODO: Use the same format internally in the thread caches?
int batch_size = Static::sizemap()->num_objects_to_move(cl);
while (N > batch_size) {
void *tail, *head;
src->PopRange(batch_size, &head, &tail);
Static::central_cache()[cl].InsertRange(head, tail, batch_size);
N -= batch_size;
}
void *tail, *head;
src->PopRange(N, &head, &tail);
Static::central_cache()[cl].InsertRange(head, tail, N);
size_ -= delta_bytes;
}
InsertRange函數根據傳入的obj的數量判斷是不是正好為轉移一批的size,如果是且tc_slots_還有空那麼直接插入tc_slots_,否則將調用ReleaseListToSpans釋放到Span隊列中。
void CentralFreeList::InsertRange(void *start, void *end, int N) {
SpinLockHolder h(&lock_);
if (N == Static::sizemap()->num_objects_to_move(size_class_) &&
MakeCacheSpace()) {
int slot = used_slots_++;
ASSERT(slot >=0);
ASSERT(slot < kNumTransferEntries);
TCEntry *entry = &tc_slots_[slot];
entry->head = start;
entry->tail = end;
return;
}
ReleaseListToSpans(start);
}
ReleaseListToSpans對Range中的每個obj調用ReleaseToSpans。ReleaseToSpan首先根據obj的addr擷取obj所在的Span的地址,然後通過Span->
objects是否為NULL判斷本Span中是否還有閒置obj,如果沒有,那麼代表著本Span在empty隊列中,現在有obj
release了,那麼就得將這個Span轉移到nonempty隊列中,以表示本Span還有可用的obj。
void CentralFreeList::ReleaseToSpans(void* object) {
Span* span = MapObjectToSpan(object);
ASSERT(span != NULL);
ASSERT(span->refcount > 0);
// If span is empty, move it to non-empty list
if (span->objects == NULL) {
tcmalloc::DLL_Remove(span);
tcmalloc::DLL_Prepend(&nonempty_, span);
Event(span, 'N', 0);
}
下面開始將obj插入Span中,首先對Span現有的obj進行遍曆,以確保當前需要釋放的obj不在Span空閑obj隊列中。
// The following check is expensive, so it is disabled by default
if (false) {
// Check that object does not occur in list
int got = 0;
for (void* p = span->objects; p != NULL; p = *((void**) p)) {
ASSERT(p != object);
got++;
}
ASSERT(got + span->refcount ==
(span->length<<kPageShift) /
Static::sizemap()->ByteSizeForClass(span->sizeclass));
}
這裡看是設定整個centeral cache當前freelist中的空閑obj的數量(counter_++)和當前Span中在使用obj的數量(span->refcount--;),如果本Span中已經沒有在使用的obj了那麼通過pageheap的Delete函數將本Span還給pageheap。否則將需要釋放的obj加入Span的object
list中,並放在頭上。
counter_++;
span->refcount--;
if (span->refcount == 0) {
Event(span, '#', 0);
counter_ -= ((span->length<<kPageShift) /
Static::sizemap()->ByteSizeForClass(span->sizeclass));
tcmalloc::DLL_Remove(span);
// Release central list lock while operating on pageheap
lock_.Unlock();
{
SpinLockHolder h(Static::pageheap_lock());
Static::pageheap()->Delete(span);
}
lock_.Lock();
} else {
*(reinterpret_cast<void**>(object)) = span->objects;
span->objects = object;
}
}