分类: LINUX
2011-10-08 16:02:54
到此为止ThreadCache::GetCache函数分析完毕,那么我们返回do_malloc函数,首先判断size是否小于kMaxSize(8u * kPageSize),这个是区分要分配的是large object还是small obj。如果是small obj,那么首先通过Static::sizemap将size转换为classNum,然后通过classNum调用class_to_size获取次class中能够分配的obj的最大size。然后通过判断FLAGS_tcmalloc_sample_parameter和heap->SampleAllocation判断是否需要sample,如果是那么调用专门的DoSampledAllocation分配相应的span和stacktrace_allocator。具体我们后面会分析到。
inline void* do_malloc(size_t size) {
void* ret = NULL;
// The following call forces module initialization
ThreadCache* heap = ThreadCache::GetCache();
if (size <= kMaxSize) {
size_t cl = Static::sizemap()->SizeClass(size);
size = Static::sizemap()->class_to_size(cl);
if ((FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) {
ret = DoSampledAllocation(size);
} else {
// The common case, and also the simplest. This just pops the
// size-appropriate freelist, after replenishing it if it's empty.
ret = CheckedMallocResult(heap->Allocate(size, cl));
}
} else {
ret = do_malloc_pages(heap, size);
}
if (ret == NULL) errno = ENOMEM;
return ret;
}
如果不需要sample那么直接调用heap->Allocate直接从freelist里面拿,代码如下所示。首先做两个断言,以保证Allocate的正确性,通过cl和list_获取本class的object list如果list为空,那么代表本class里面没有相应的free object,那么我么直接从CentralCache中拿(FetchFromCentralCache(cl, size))否则修改ThreadCache的size_,并通过调用list->Pop弹出需要的obj。
inline void* ThreadCache::Allocate(size_t size, size_t cl) {
ASSERT(size <= kMaxSize);
ASSERT(size == Static::sizemap()->ByteSizeForClass(cl));
FreeList* list = &list_[cl];
if (list->empty()) {
return FetchFromCentralCache(cl, size);
}
size_ -= size;
return list->Pop();
}
FetchFromCentralCache用来从CentralCache转移obj到ThreadCache中,首先通过Static::sizemap()->num_objects_to_move(cl)获取一次转移的obj数量,由于本threadcache的list都有自己的一个max_length(max_length可以动态调整,等会儿就会看到),因此还需要考虑。
void* ThreadCache::FetchFromCentralCache(size_t cl, size_t byte_size) {
FreeList* list = &list_[cl];
ASSERT(list->empty());
const int batch_size = Static::sizemap()->num_objects_to_move(cl);
const int num_to_move = min
void *start, *end;
int fetch_count = Static::central_cache()[cl].RemoveRange(
&start, &end, num_to_move);
获取到num_to_move后通过调用CentralCache的RemoveRange获取需要Remove的Range,这个Range通过指针start和end标示。返回值fetch_count是获取的obj数量,然后通过list->PushRange将fetch出来的obj加入到列表。
ASSERT((start == NULL) == (fetch_count == 0));
if (--fetch_count >= 0) {//此处的目的就是为了配合alloc一个object,那么直接把他提出来,而不插入list里面,否则还需要再次从list中alloc这个obj。切割出这个obj除了fetch_count-1之外,还需要在PushRange时做一些处理,SLL_Next就是这个目的跳过第一个obj,直接以下一个obj为头结点。
size_ += byte_size * fetch_count;
list->PushRange(fetch_count, SLL_Next(start), end);
}
// Increase max length slowly up to batch_size. After that,
// increase by batch_size in one shot so that the length is a
// multiple of batch_size.
//此处主要设置max_length的大小,当max_length< batch_size时每次+1,如果不是那么每次增加一个batch_size,最大值应该是kMaxDynamicFreeListLength(8192个obj)。
if (list->max_length() < batch_size) {
list->set_max_length(list->max_length() + 1);
} else {
// Don't let the list get too long. In 32 bit builds, the length
// is represented by a 16 bit int, so we need to watch out for
// integer overflow.
int new_length = min
kMaxDynamicFreeListLength);
// The list's max_length must always be a multiple of batch_size,
// and kMaxDynamicFreeListLength is not necessarily a multiple
// of batch_size.
new_length -= new_length % batch_size;
ASSERT(new_length % batch_size == 0);
list->set_max_length(new_length);
}
return start;
}
RemoveRange获取相应CentrallFreeList的锁喉,判断used_solts_是否>0,如果>0代表着已经有预分配的objs在tc_slots_中,tc_slots的分配是从0开始到kNumTransferEntries(kNumClasses大小),如果有预分配的,且需要分配的size是num_objects_to_move那么直接将tc_slots_[slot]拿出来,分别将entry->head赋值给start,而entry->tail赋值给end,并解锁返回。
int CentralFreeList::RemoveRange(void **start, void **end, int N) {
ASSERT(N > 0);
lock_.Lock();
if (N == Static::sizemap()->num_objects_to_move(size_class_) &&
used_slots_ > 0) {
int slot = --used_slots_;
ASSERT(slot >= 0);
TCEntry *entry = &tc_slots_[slot];
*start = entry->head;
*end = entry->tail;
lock_.Unlock();
return N;
}
如果以上条件不成立,那么代表着要么tc_slots_为空或者请求的size不等于num_object_to_move。这时候通过调用FetchFromSpansSafe从Span中分配。FetchFromSpansSafe和FetchFromSpans的主要差别在于,FetchFromSpans如果发现不能从Span分配直接返回,而FetchFromSpansSafe则将调用Populate从heapPage中分配需要的页面填充到CentralFreeList中并再次尝试分配。如果FetchFromSpansSafe成功,那么就已经获得了需要分配的第一个obj,设置result为1并调用while (result < N)循环分N-1次通过FetchFromSpans将获取的obj通过SLL_Push插入head所标示的列表,这个列表的组织方式比较好玩,每个obj的头上8bytes存储了下一个obj的地址,以这种方式组织的列表,如果出现溢出那么将是致命的,因为一般情况下span是连续的那么A和B很大可能性上也是连续的,所以一旦A溢出,那么B中存储的C地址将被毁掉,那么C将丢失。
int result = 0;
void* head = NULL;
void* tail = NULL;
// TODO: Prefetch multiple TCEntries?
tail = FetchFromSpansSafe();
if (tail != NULL) {
SLL_SetNext(tail, NULL);
head = tail;
result = 1;
while (result < N) {
void *t = FetchFromSpans();
if (!t) break;
SLL_Push(&head, t);
result++;
}
}
lock_.Unlock();
*start = head;
*end = tail;
return result;
}