分类: LINUX
2011-10-08 16:11:25
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<
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<
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
span->objects = object;
}
}