Tcmalloc源码简单分析(12)

本文深入探讨了内存管理中的关键概念,包括如何通过中央免费列表(Central Free List)进行内存分配与回收,以及内存块的分割与整合过程。重点介绍了FetchFromSpans和Populate两个函数的工作原理,解释了如何在不增加系统开销的情况下高效地管理内存资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面我们来看看FetchFromSpansPopulate两个函数。FetchFromSpans首先判断CentralFreeListnonempty_列表中是否有可用的span如果没有则返回NULL,否则取nonempty_第一个span,然后将span中切出一个obj,并调整span->objects指正,如果span已经被切割干净,那么把这个spannonempty列表中分离出来放入empty列表。

 

void* CentralFreeList::FetchFromSpans() {

  if (tcmalloc::DLL_IsEmpty(&nonempty_)) return NULL;

  Span* span = nonempty_.next;

 

  ASSERT(span->objects != NULL);

  span->refcount++;

  void* result = span->objects;

  span->objects = *(reinterpret_cast<void**>(result));

  if (span->objects == NULL) {

    // Move to empty list

    tcmalloc::DLL_Remove(span);

    tcmalloc::DLL_Prepend(&empty_, span);

    Event(span, 'E', 0);

  }

  counter_--;

  return result;

}

Populate函数通过Static::pageheapNew出需要的空间,首先通过Static::sizemap()->class_to_pagessize转换为npages。然后通过函数Static::pageheap()->Newpageheap获取npages个页面,然后如果span获取成功,那么将他插入pagemap_这颗raidx tree。如果spanNULL那么代表分配失败返回,返回前先获取前面释放的CentralFreeList的锁。如果分配成功,那么将这个span中的page插入pagemap_cache_这个二元组。最后通过一个while循环将整个span拆分成一个个的obj并通过*tail = ptr;     tail = reinterpret_cast<void**>(ptr);建立上图的链表。

 

void CentralFreeList::Populate() {

  // Release central list lock while operating on pageheap

  lock_.Unlock();

  const size_t npages = Static::sizemap()->class_to_pages(size_class_);

 

  Span* span;

  {

    SpinLockHolder h(Static::pageheap_lock());

    span = Static::pageheap()->New(npages);

    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.)

  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;

  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++;

  }

  ASSERT(ptr <= limit);

  *tail = NULL;

  span->refcount = 0; // No sub-object in use yet

 

  // Add span to list of non-empty spans

  lock_.Lock();

  tcmalloc::DLL_Prepend(&nonempty_, span);

  counter_ += num;

}

我们在看一下函数= Static::pageheap()->New,其中kMaxPages256

Span* PageHeap::New(Length n) {

  ASSERT(Check());

  ASSERT(n > 0);

 

  // Find first size >= n that has a non-empty list

  for (Length s = n; s < kMaxPages; s++) {//sizenpagefreelist开始查找,如果不符合那么查找比n大的下一个freelist

    Span* ll = &free_[s].normal;// 首先获取normal list,同一个sizespan分为两类,normalreturnnormal就是一切正常的spanreturn代表内存已经返回给系统的span

    // If we're lucky, ll is non-empty, meaning it has a suitable span.

    if (!DLL_IsEmpty(ll)) {//如果normal list为空

      ASSERT(ll->next->location == Span::ON_NORMAL_FREELIST);//通过spanlocation来判断span是怪哉freelist上,如果是,那么用Carve吧这个span切出。如果在normal队列里面分配失败,那么从returned队列里面切出来。

      return Carve(ll->next, n);

    }

    // Alternatively, maybe there's a usable returned span.

    ll = &free_[s].returned;

    if (!DLL_IsEmpty(ll)) {

      ASSERT(ll->next->location == Span::ON_RETURNED_FREELIST);

      return Carve(ll->next, n);

    }

    // Still no luck, so keep looking in larger classes.

  }

如果以上都没有成功,那么我们的祈求AllocLarge从系统分配了

  Span* result = AllocLarge(n);

  if (result != NULL) return result;

 

  // Grow the heap and try again

//继续尝试,首先增长Heap,尝试再次分配。

  if (!GrowHeap(n)) {

    ASSERT(Check());

    return NULL;

  }

 

  return AllocLarge(n);

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值