TCMalloc源码阅读(四)--ThreadCache分析之空闲内存链表

前面几篇博文中已经描述了TCMalloc将内存从小到大划分成很多个固定大小的内存块,将每种大小的空闲内存块使用链表管理起来。本文就来分析下ThreadCache中空闲链表的实现。
TreadCache::FreeList的代码如下:
[cpp]  view plain copy
  1.  class FreeList {  
  2.    private:  
  3.     void*    list_;       // Linked list of nodes  
  4.   
  5.   
  6. #ifdef _LP64  
  7.     // On 64-bit hardware, manipulating 16-bit values may be slightly slow.  
  8.     uint32_t length_;      // Current length.  
  9.     uint32_t lowater_;     // Low water mark for list length.  
  10.     uint32_t max_length_;  // Dynamic max list length based on usage.  
  11.     // Tracks the number of times a deallocation has caused  
  12.     // length_ > max_length_.  After the kMaxOverages'th time, max_length_  
  13.     // shrinks and length_overages_ is reset to zero.  
  14.     uint32_t length_overages_;  
  15. #else  
  16.     // If we aren't using 64-bit pointers then pack these into less space.  
  17.     uint16_t length_;  
  18.     uint16_t lowater_;  
  19.     uint16_t max_length_;  
  20.     uint16_t length_overages_;  
  21. #endif  
  22.   
  23.   
  24.    public:  
  25.     void Init() {  
  26.       list_ = NULL;  
  27.       length_ = 0;  
  28.       lowater_ = 0;  
  29.       max_length_ = 1;  
  30.       length_overages_ = 0;  
  31.     }  
  32.   
  33.   
  34.     // Return current length of list  
  35.     size_t length() const {  
  36.       return length_;  
  37.     }  
  38.   
  39.   
  40.     // Return the maximum length of the list.  
  41.     size_t max_length() const {  
  42.       return max_length_;  
  43.     }  
  44.   
  45.   
  46.     // Set the maximum length of the list.  If 'new_max' > length(), the  
  47.     // client is responsible for removing objects from the list.  
  48.     void set_max_length(size_t new_max) {  
  49.       max_length_ = new_max;  
  50.     }  
  51.   
  52.   
  53.     // Return the number of times that length() has gone over max_length().  
  54.     size_t length_overages() const {  
  55.       return length_overages_;  
  56.     }  
  57.   
  58.   
  59.     void set_length_overages(size_t new_count) {  
  60.       length_overages_ = new_count;  
  61.     }  
  62.   
  63.   
  64.     // Is list empty?  
  65.     bool empty() const {  
  66.       return list_ == NULL;  
  67.     }  
  68.   
  69.   
  70.     // Low-water mark management  
  71.     int lowwatermark() const { return lowater_; }  
  72.     void clear_lowwatermark() { lowater_ = length_; }  
  73.   
  74.   
  75.     void Push(void* ptr) {  
  76.       SLL_Push(&list_, ptr);  
  77.       length_++;  
  78.     }  
  79.   
  80.   
  81.     void* Pop() {  
  82.       ASSERT(list_ != NULL);  
  83.       length_--;  
  84.       if (length_ < lowater_) lowater_ = length_;  
  85.       return SLL_Pop(&list_);  
  86.     }  
  87.   
  88.   
  89.     void* Next() {  
  90.       return SLL_Next(&list_);  
  91.     }  
  92.   
  93.   
  94.     void PushRange(int N, void *start, void *end) {  
  95.       SLL_PushRange(&list_, start, end);  
  96.       length_ += N;  
  97.     }  
  98.   
  99.   
  100.     void PopRange(int N, void **start, void **end) {  
  101.       SLL_PopRange(&list_, N, start, end);  
  102.       ASSERT(length_ >= N);  
  103.       length_ -= N;  
  104.       if (length_ < lowater_) lowater_ = length_;  
  105.     }  


FreeList的成员list_是一个指向首个空闲内存块的指针,它也是链表的head。链表所有的操作均在SSL_*系列函数中实现。SSL_*系列接口如下:
[cpp]  view plain copy
  1. void *SLL_Next(void *t);//取t的下一块空闲内存。t也是一个空闲内存块。  
  2. void SLL_SetNext(void *t, void *n);//设置空闲内存块t的下一个空闲内存快n。  
  3. void SLL_Push(void **list, void *element);//在链表最前面插入一个空闲内存块element。  
  4. void *SLL_Pop(void **list);//取出链表中第一个空闲内存块。  
  5. void SLL_PopRange(void **head, int N, void **start, void **end);//取出链表中前面N个空闲内存块。  
  6. void SLL_PushRange(void **head, void *start, void *end);//在链表前面插入从start到end一个链表。  
  7. size_t SLL_Size(void *head);//计算链表的长度。  
SLL链表利用空闲内存本身来存储其下一个空闲内存块的地址,即将内存块的前4个字节(32位)或者8个字节(64位)用来存储next。示意图如下:
TCMalloc源码阅读(四)--ThreadCache分析之空闲内存链表_第1张图片

各接口实现代码在linked_list.h中:
[cpp]  view plain copy
  1. inline void *SLL_Next(void *t) {  
  2.   return *(reinterpret_cast<void**>(t));//将当前内存块重新解释为一个二级指针,它指向一个void*,即空闲内存块。再通过*操作符获取它指向下一块空闲内存的地址。  
  3. }  
  4. inline void SLL_SetNext(void *t, void *n) {  
  5.   *(reinterpret_cast<void**>(t)) = n;  
  6. }  
  7.   
  8.   
  9. inline void SLL_Push(void **list, void *element) {  
  10.   SLL_SetNext(element, *list);  
  11.   *list = element;  
  12. }  
  13.   
  14.   
  15. inline void *SLL_Pop(void **list) {  
  16.   void *result = *list;  
  17.   *list = SLL_Next(*list);  
  18.   return result;  
  19. }  
  20.   
  21.   
  22. // Remove N elements from a linked list to which head points.  head will be  
  23. // modified to point to the new head.  start and end will point to the first  
  24. // and last nodes of the range.  Note that end will point to NULL after this  
  25. // function is called.  
  26. inline void SLL_PopRange(void **head, int N, void **start, void **end) {  
  27.   if (N == 0) {  
  28.     *start = NULL;  
  29.     *end = NULL;  
  30.     return;  
  31.   }  
  32.   
  33.   
  34.   void *tmp = *head;  
  35.   for (int i = 1; i < N; ++i) {  
  36.     tmp = SLL_Next(tmp);  
  37.   }  
  38.   
  39.   
  40.   *start = *head;  
  41.   *end = tmp;  
  42.   *head = SLL_Next(tmp);  
  43.   // Unlink range from list.  
  44.   SLL_SetNext(tmp, NULL);  
  45. }  
  46.   
  47.   
  48. inline void SLL_PushRange(void **head, void *start, void *end) {  
  49.   if (!start) return;  
  50.   SLL_SetNext(end, *head);  
  51.   *head = start;  
  52. }  
  53.   
  54.   
  55. inline size_t SLL_Size(void *head) {  
  56.   int count = 0;  
  57.   while (head) {  
  58.     count++;  
  59.     head = SLL_Next(head);  
  60.   }  
  61.   return count;  
  62. }  

你可能感兴趣的:(TCMalloc源码阅读(四)--ThreadCache分析之空闲内存链表)