Tcmalloc源码简单分析(11)

到此为止ThreadCache::GetCache函数分析完毕,那么我们返回do_malloc函数,首先判断size是否小于kMaxSize8u * kPageSize),这个是区分要分配的是large object还是small obj。如果是small obj,那么首先通过Static::sizemapsize转换为classNum,然后通过classNum调用class_to_size获取次class中能够分配的obj的最大size。然后通过判断FLAGS_tcmalloc_sample_parameterheap->SampleAllocation判断是否需要sample,如果是那么调用专门的DoSampledAllocation分配相应的spanstacktrace_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的正确性,通过cllist_获取本classobject list如果list为空,那么代表本class里面没有相应的free object,那么我么直接从CentralCache中拿(FetchFromCentralCache(cl, size))否则修改ThreadCachesize_,并通过调用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转移objThreadCache中,首先通过Static::sizemap()->num_objects_to_move(cl)获取一次转移的obj数量,由于本threadcachelist都有自己的一个max_lengthmax_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<int>(list->max_length(), batch_size);

  void *start, *end;

  int fetch_count = Static::central_cache()[cl].RemoveRange(

      &start, &end, num_to_move);

  获取到num_to_move后通过调用CentralCacheRemoveRange获取需要RemoveRange,这个Range通过指针startend标示。返回值fetch_count是获取的obj数量,然后通过list->PushRangefetch出来的obj加入到列表。

 

  ASSERT((start == NULL) == (fetch_count == 0));

  if (--fetch_count >= 0) {//此处的目的就是为了配合alloc一个object,那么直接把他提出来,而不插入list里面,否则还需要再次从listalloc这个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,最大值应该是kMaxDynamicFreeListLength8192obj)。

  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<int>(list->max_length() + batch_size,

                              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代表着已经有预分配的objstc_slots_中,tc_slots的分配是从0开始到kNumTransferEntrieskNumClasses大小),如果有预分配的,且需要分配的sizenum_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。这时候通过调用FetchFromSpansSafeSpan中分配。FetchFromSpansSafeFetchFromSpans的主要差别在于,FetchFromSpans如果发现不能从Span分配直接返回,而FetchFromSpansSafe则将调用PopulateheapPage中分配需要的页面填充到CentralFreeList中并再次尝试分配。如果FetchFromSpansSafe成功,那么就已经获得了需要分配的第一个obj,设置result1并调用while (result < N)循环分N-1次通过FetchFromSpans将获取的obj通过SLL_Push插入head所标示的列表,这个列表的组织方式比较好玩,每个obj的头上8bytes存储了下一个obj的地址,以这种方式组织的列表,如果出现溢出那么将是致命的,因为一般情况下span是连续的那么AB很大可能性上也是连续的,所以一旦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;

}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值