TCMalloc源码学习-5-总结
前面几篇分别介绍了TCMalloc的整体,PageHeap,CentralFreeList和ThreadCache,这一篇介绍一下最上层的用户接口,其实只要理解了前面的所有结构,用户接口的实现就非常好理解了
do_malloc
用户调用malloc后,主要做的事情都在do_malloc里,直接上代码:
inline void* do_malloc(size_t size) {
void* ret = NULL;
// The following call forces module initialization
//获取本线程的ThreadCache对象
ThreadCache* heap = ThreadCache::GetCache();
//如果是小内存申请
if (size <= kMaxSize) {
//通过查策略找到对应的size和cl
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.
//调用本线程ThreadCache的Allocate接口从缓存中申请内存,具体实现上一篇有讲
ret = CheckedMallocResult(heap->Allocate(size, cl));
}
} else {
//如果是大内存申请,则直接调用do_malloc_pages从PageHeap来申请
ret = do_malloc_pages(heap, size);
}
if (ret == NULL) errno = ENOMEM;
return ret;
}
// Helper for do_malloc().
inline void* do_malloc_pages(ThreadCache* heap, size_t size) {
void* result;
bool report_large;
//通过size算出需要多少页
Length num_pages = tcmalloc::pages(size);
//得到实际要实际分配的大小
size = num_pages << kPageShift;
if ((FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) {
result = DoSampledAllocation(size);
SpinLockHolder h(Static::pageheap_lock());
report_large = should_report_large(num_pages);
} else {
//锁住PageHeap
SpinLockHolder h(Static::pageheap_lock());
//从PageHeap中申请需要的页数
Span* span = Static::pageheap()->New(num_pages);
//将返回的大内存的首页号缓存到PageHeap的pagemap_cache,对应的cl是0,这个0会
//在将来还这块内存的时候表明这是个大内存
result = (span == NULL ? NULL : SpanToMallocResult(span));
report_large = should_report_large(num_pages);
}
if (report_large) {
ReportLargeAlloc(num_pages, result);
}
return result;
}
还是十分简单的,再来看看释放内存的实现
do_free
// The default "do_free" that uses the default callback.
inline void do_free(void* ptr) {
//实际调用
return do_free_with_callback(ptr, &InvalidFree);
}
// This lets you call back to a given function pointer if ptr is invalid.
// It is used primarily by windows code which wants a specialized callback.
inline void do_free_with_callback(void* ptr, void (*invalid_free_fn)(void*)) {
if (ptr == NULL) return;
if (Static::pageheap() == NULL) {
// We called free() before malloc(). This can occur if the
// (system) malloc() is called before tcmalloc is loaded, and then
// free() is called after tcmalloc is loaded (and tc_free has
// replaced free), but before the global constructor has run that
// sets up the tcmalloc data structures.
(*invalid_free_fn)(ptr); // Decide how to handle the bad free request
return;
}
//通过ptr算出PageID
const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
Span* span = NULL;
//从PageHeap的pagemap_cache中找到这个page对应的cl,如果没找到就返回0
size_t cl = Static::pageheap()->GetSizeClassIfCached(p);
if (cl == 0) {
//这里cl等于0有两种情况,一种是页p没有在pagemap_cache中找到默认返回0,
//另一种是页p对应的是大内存的首页,所以当初存的cl就是0
span = Static::pageheap()->GetDescriptor(p);
if (!span) {
// span can be NULL because the pointer passed in is invalid
// (not something returned by malloc or friends), or because the
// pointer was allocated with some other allocator besides
// tcmalloc. The latter can happen if tcmalloc is linked in via
// a dynamic library, but is not listed last on the link line.
// In that case, libraries after it on the link line will
// allocate with libc malloc, but free with tcmalloc's free.
(*invalid_free_fn)(ptr); // Decide how to handle the bad free request
return;
}
cl = span->sizeclass;
Static::pageheap()->CacheSizeClass(p, cl);
}
if (cl != 0) {
//不等于0说明归还的是86种之一的小内存
ASSERT(!Static::pageheap()->GetDescriptor(p)->sample);
//得到本线程的ThreadCache对象
ThreadCache* heap = GetCacheIfPresent();
if (heap != NULL) {
//如果有就放进线程缓存
heap->Deallocate(ptr, cl);
} else {
//如果没有就返回CentralFreeList
// Delete directly into central cache
tcmalloc::SLL_SetNext(ptr, NULL);
Static::central_cache()[cl].InsertRange(ptr, ptr, 1);
}
} else {
//等于0,大内存,直接还给PageHeap
SpinLockHolder h(Static::pageheap_lock());
ASSERT(reinterpret_cast<uintptr_t>(ptr) % kPageSize == 0);
ASSERT(span != NULL && span->start == p);
if (span->sample) {
StackTrace* st = reinterpret_cast<StackTrace*>(span->objects);
tcmalloc::DLL_Remove(span);
Static::stacktrace_allocator()->Delete(st);
span->objects = NULL;
}
//还给PageHeap
Static::pageheap()->Delete(span);
}
}
总结
TCMalloc的源码学习收获还是不少的,不过里面还是有一些疑惑的,过阵子再回头来看看没准会有新的理解