Hotspot 垃圾回收之CompactibleFreeListSpace(一) 源碼解析

目錄

一、FreeChunk

二、PromotionInfo

1、SpoolBlock

2、PromotedObject

3、track

4、 promoted_oops_iterate

5、ensure_spooling_space

三、CompactibleFreeListSpace

1、定義

2、構造方法和set_cms_values

3、getFromListGreater / getChunkFromGreater

 4、split / coalBirth / coalDeath


    上一篇博客《Hotspot 垃圾回收之EdenSpace 源碼解析》講解了CMS算法中表示Ffrom和To區的ContiguousSpace,表示Eden區的EdenSpace和ConcEdenSpace,本篇博客繼續講解Space的子類CompactibleFreeListSpace及其相關類的實現,該類用來表示CMS算法的老年代的內存區域。

一、FreeChunk

       FreeChunk的定義位於hotspot\src\share\vm\gc_implementation\concurrentMarkSweep\freeChunk.hpp中,是CMS中用來表示一個空閒的內存塊,其包含的屬性如下:

FreeChunk也是位於老年代的堆內存中,怎麼跟正常的Java對象區分了?如果是32位或者64位下不開啓UseCompressedOops則通過prev字段的地址最後一位來標識,如果是64位下開啓UseCompressedOops則通過size字段來標識,參考如下方法的實現:

//根據addr判斷這是否是一個FreeChunk,如果是返回true
static bool indicatesFreeChunk(const HeapWord* addr) {
    return ((volatile FreeChunk*)addr)->is_free();
}

bool is_free() const volatile {
    LP64_ONLY(if (UseCompressedOops) return mark()->is_cms_free_chunk(); else)
    return (((intptr_t)_prev) & 0x1) == 0x1;
}

markOop mark()     const volatile { return (markOop)_size; }

//size屬性的讀寫
size_t size() const volatile {
    LP64_ONLY(if (UseCompressedOops) return mark()->get_size(); else )
    return _size;
  }

void set_size(size_t sz) {
    LP64_ONLY(if (UseCompressedOops) set_mark(markOopDesc::set_size_and_free(sz)); else )
    _size = sz;
}

void set_mark(markOop m)          { _size = (size_t)m; }

//prev屬性的讀寫,|0x1就是將ptr地址的最後一位變成1,ptr因爲需要按照內存頁對齊,所以ptr通常最後的N位都是0,N位取決於內存頁的大小
void link_prev(FreeChunk* ptr) {
    LP64_ONLY(if (UseCompressedOops) _prev = ptr; else)
    _prev = (FreeChunk*)((intptr_t)ptr | 0x1);
}

FreeChunk* prev() const {
    //& ~(0x3)實際就是把ptr地址的最後兩位換成0,最後一位表示是否是FreeChunk,倒數第二位用於表示該FreeChunk不能執行合併
    return (FreeChunk*)(((intptr_t)_prev) & ~(0x3));
}

//將FreeChunk標記爲非Free
void markNotFree() {
    // Set _prev (klass) to null before (if) clearing the mark word below
    _prev = NULL;
#ifdef _LP64
    if (UseCompressedOops) {
      OrderAccess::storestore();
      set_mark(markOopDesc::prototype());
    }
#endif
    assert(!is_free(), "Error");
  }

bool cantCoalesce() const {
    assert(is_free(), "can't get coalesce bit on not free");
    return (((intptr_t)_prev) & 0x2) == 0x2;
  }

//dontCoalesce用於標記當前FreeChunk不支持合併成一個更大的FreeChunk
void dontCoalesce() {
    // the block should be free
    assert(is_free(), "Should look like a free block");
    //| 0x2將prev地址的倒數第二位置爲1
    _prev = (FreeChunk*)(((intptr_t)_prev) | 0x2);
  }

理解上述邏輯的關鍵在於,FreeChunk的前2個字寬和普通的Java對象即OopDesc是一致的,OopDesc的定義如下:

markOop其實是markOopDesc*的別名,也是一個指針,對應於FreeChunk的size屬性,size_t的長度跟指針的長度是一致的,對應一個字寬,64位下8字節,32位下4字節;第二個字寬,_metadata對應於_prev。在64位下開啓指針壓縮的條件下,都讀取第一個字寬的數據,在32位或者64位下不開啓指針壓縮時都讀取第二個字寬的數據,然後根據特殊的位來判斷是否是FreeChunk。

二、PromotionInfo

      PromotionInfo的定義在同目錄的promotionInfo.hpp中,主要用於保存分配的oop,並在必要時保存oop的對象頭。其包含的屬性如下:

  • CompactibleFreeListSpace* _space; //關聯的CompactibleFreeListSpace
  • PromotedObject* _promoHead;     // PromotedObject鏈表的頭元素
  • PromotedObject* _promoTail;     // PromotedObject鏈表的尾元素
  • SpoolBlock*     _spoolHead;     // SpoolBlock鏈表的頭元素
  • SpoolBlock*     _spoolTail;     // SpoolBlock鏈表的尾元素
  • SpoolBlock*     _splice_point;  // 臨時保存上一個spoolTail
  • SpoolBlock*     _spareSpool;    // 已經遍歷完成的SpoolBlock鏈表
  • size_t          _firstIndex;    // spoolHead中下一個遍歷的markOop的索引
  • size_t          _nextIndex;     // spoolTail中下一個分配的markOop的索引

重點關注以下方法的實現。 

1、SpoolBlock

      SpoolBlock繼承自FreeChunk,用來保存被替換的對象頭的,其定義在同目錄的promotionInfo.hpp中。SpoolBlock增加了三個屬性,如下:

其中bufferSize表示該Block可容納的markOop的個數,displacedHdr表示保存的被替換的對象頭markOop的數組。 重點關注其init方法的實現即可,如下:

void init() {
    bufferSize = computeBufferSize();
    //顯示的初始化displacedHdr
    displacedHdr = (markOop*)&displacedHdr;
    nextSpoolBlock = NULL;
}

size_t computeBufferSize() {
    //size方法該內存塊總的大小,減去SpoolBlock屬性本身佔用的空間就是剩餘的可用空間了
    return (size() * sizeof(HeapWord) - sizeof(*this)) / sizeof(markOop);
}

2、PromotedObject

      PromotedObject表示一個經過promoted的oop,是oop強轉而來的,從而操作oop的對象頭,其定義的屬性是一個union結構,如下:

_next屬性和_data屬性都是一個字寬,因此這個union屬性就是一個字寬,在內存結構上剛好對應於OopDesc的表示對象頭的_mark屬性,操作union屬性實際就是操作對象的對象頭。重點關注其next和setNext方法的實現,如下:

inline PromotedObject* next() const {
    //校驗當前這個PromotedObject不是FreeChunk
    assert(!((FreeChunk*)this)->is_free(), "Error");
    PromotedObject* res;
    if (UseCompressedOops) {
      // The next pointer is a compressed oop stored in the top 32 bits
      res = (PromotedObject*)oopDesc::decode_heap_oop(_data._narrow_next);
    } else {
      res = (PromotedObject*)(_next & next_mask);
    }
    //校驗res是oop
    assert(oop(res)->is_oop_or_null(true /* ignore mark word */), "Not an oop?");
    return res;
  }
  
inline void setNext(PromotedObject* x) {
    //校驗表示next的位沒有被佔用
    assert(((intptr_t)x & ~next_mask) == 0, "Conflict in bit usage, "
           "or insufficient alignment of objects");
    if (UseCompressedOops) {
      assert(_data._narrow_next == 0, "Overwrite?");
      _data._narrow_next = oopDesc::encode_heap_oop(oop(x));
    } else {
      //|=的效果和=是一致的
      _next |= (intptr_t)x;
    }
    assert(!((FreeChunk*)this)->is_free(), "Error");
  }

即通過對象頭可以把相關的oop串聯起來。 

3、track

      track方法用於跟蹤某個oop,具體來說是將這個oop放入PromotionInfo保存的PromotedObject鏈表中,並必要時保存原oop的對象頭。其調用鏈如下:

其實現如下: 

void PromotionInfo::track(PromotedObject* trackOop) {
  track(trackOop, oop(trackOop)->klass());
}

void PromotionInfo::track(PromotedObject* trackOop, Klass* klassOfOop) {
  // make a copy of header as it may need to be spooled
  markOop mark = oop(trackOop)->mark();
  //實際是清除原來的對象頭,後面的setDisplacedMark和setPromotedMark相當於替換了原來的對象頭
  trackOop->clear_next();
  //如果有偏向鎖了
  if (mark->must_be_preserved_for_cms_scavenge(klassOfOop)) {
    //保存原來的對象頭,然後設置DisplacedMark
    saveDisplacedHeader(mark);
    trackOop->setDisplacedMark();
  } else {
   
  }
  //如果_promoTail不爲空則插入到_promoTail的後面
  if (_promoTail != NULL) {
    assert(_promoHead != NULL, "List consistency");
    _promoTail->setNext(trackOop);
    _promoTail = trackOop;
  } else {
    //_promoTail爲空,初始化_promoHead和_promoTail
    assert(_promoHead == NULL, "List consistency");
    _promoHead = _promoTail = trackOop;
  }
  //設置PromotedMark
  assert(!trackOop->hasPromotedMark(), "Should not have been marked");
  trackOop->setPromotedMark();
}

 inline void clear_next()        {
    _next = 0;
    assert(!((FreeChunk*)this)->is_free(), "Error");
}

void PromotionInfo::saveDisplacedHeader(markOop hdr) {
  assert(_spoolHead != NULL && _spoolTail != NULL,
         "promotionInfo inconsistency");
  assert(_spoolTail->bufferSize > _nextIndex, "Off by one error at tail?");
  //保存對象頭hdr
  _spoolTail->displacedHdr[_nextIndex] = hdr;
  //_nextIndex加1後等於_spoolTail->bufferSize,說明_spoolTail已經沒有可用空間存儲新的markOop了
  if (++_nextIndex == _spoolTail->bufferSize) { // last location in this block
    // get a new spooling block
    assert(_spoolTail->nextSpoolBlock == NULL, "tail should terminate spool list");
    //_splice_point用於臨時的保存上一個_spoolTail
    _splice_point = _spoolTail;                   // save for splicing
    //獲取一個新的SpoolBlock,插入到_spoolTail的後面,然後更新_spoolTail
    _spoolTail->nextSpoolBlock = getSpoolBlock(); // might fail
    _spoolTail = _spoolTail->nextSpoolBlock;      // might become NULL ...
    //置爲1,下次分配就從索引爲1的位置保存
    _nextIndex = 1;
  }
}


inline void setDisplacedMark() {
    _next |= displaced_mark;
    assert(!((FreeChunk*)this)->is_free(), "Error");
}

inline void setPromotedMark() {
    _next |= promoted_mask;
    assert(!((FreeChunk*)this)->is_free(), "Error");
  }

inline bool hasPromotedMark() const {
    assert(!((FreeChunk*)this)->is_free(), "Error");
    return (_next & promoted_mask) == promoted_mask;
  }

inline bool markOopDesc::must_be_preserved_for_cms_scavenge(Klass* klass_of_obj_containing_mark) const {
  //UseBiasedLocking表示是否使用偏向鎖,默認爲true
  if (!UseBiasedLocking)
    return (!is_unlocked() || !has_no_hash());
  return must_be_preserved_with_bias_for_cms_scavenge(klass_of_obj_containing_mark);
}

inline bool markOopDesc::must_be_preserved_with_bias_for_cms_scavenge(Klass* klass_of_obj_containing_mark) const {
  assert(UseBiasedLocking, "unexpected");
  //has_bias_pattern方法表示已經有偏向鎖了
  if (has_bias_pattern() ||
      klass_of_obj_containing_mark->prototype_header()->has_bias_pattern()) {
    return true;
  }
  return (!is_unlocked() || !has_no_hash());
}

 bool is_unlocked() const {
    return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value);
 }

  bool has_bias_pattern() const {
    return (mask_bits(value(), biased_lock_mask_in_place) == biased_lock_pattern);
  }

4、 promoted_oops_iterate

     promoted_oops_iterate實際有多個方法,都是用來遍歷PromotedObject鏈表中的oop所引用的其他oop,如下圖:

這些方法的定義和實現都是通過宏實現的,如下:


//SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG宏定義不同類型的OopClosureType和nv_suffix
//PROMOTED_OOPS_ITERATE_DEFN定義具體的方法實現
SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG(PROMOTED_OOPS_ITERATE_DEFN)
PROMOTED_OOPS_ITERATE_DEFN(OopsInGenClosure,_v)

#define PROMOTED_OOPS_ITERATE_DEFN(OopClosureType, nv_suffix)               \
                                                                            \
void PromotionInfo::promoted_oops_iterate##nv_suffix(OopClosureType* cl) {  \
  NOT_PRODUCT(verify());                                                    \
  PromotedObject *curObj, *nextObj;                                         \
  //從_promoHead開始遍歷
  for (curObj = _promoHead; curObj != NULL; curObj = nextObj) {             \
     //獲取下一個遍歷對象
    if ((nextObj = curObj->next()) == NULL) {                               \
      //遍歷完成將_promoHead和_promoTail都置爲NULL
      assert(_promoTail == curObj, "Should have been the tail");            \
      _promoHead = _promoTail = NULL;                                       \
    }                                                                       \
    //如果對象頭被替換了
    if (curObj->hasDisplacedMark()) {                                       \
      /* 獲取原來的對象頭並恢復 */                                        \
      oop(curObj)->set_mark(nextDisplacedHeader());                         \
    } else {                                                                \
      /* 恢復成初始狀態的對象頭 */                                     \
      oop(curObj)->init_mark();                                             \
    }                                                                       \
    /* The "promoted_mark" should now not be set */                         \
    assert(!curObj->hasPromotedMark(),                                      \
           "Should have been cleared by restoring displaced mark-word");    \
    NOT_PRODUCT(_promoHead = nextObj);                                      \
    //遍歷curObj對象所有引用類型屬性oop
    if (cl != NULL) oop(curObj)->oop_iterate(cl);                           \
    if (nextObj == NULL) { /* start at head of list reset above */          \
      nextObj = _promoHead;                                                 \
    }                                                                       \
  }                                                                         \
  //校驗遍歷完成的結果
  assert(noPromotions(), "post-condition violation");                       \
  assert(_promoHead == NULL && _promoTail == NULL, "emptied promoted list");\
  assert(_spoolHead == _spoolTail, "emptied spooling buffers");             \
  assert(_firstIndex == _nextIndex, "empty buffer");                        \
}

bool noPromotions() const {
    assert(_promoHead != NULL || _promoTail == NULL, "list inconsistency");
    return _promoHead == NULL;
  }

markOop PromotionInfo::nextDisplacedHeader() {
  //校驗參數
  assert(_spoolHead != NULL, "promotionInfo inconsistency");
  assert(_spoolHead != _spoolTail || _firstIndex < _nextIndex,
         "Empty spool space: no displaced header can be fetched");
  assert(_spoolHead->bufferSize > _firstIndex, "Off by one error at head?");
  markOop hdr = _spoolHead->displacedHdr[_firstIndex];
  //增加_firstIndex,如果增加後的值等於 _spoolHead->bufferSize說明該SpoolBlock保存的markOop已經遍歷完了
  if (++_firstIndex == _spoolHead->bufferSize) { // last location in this block
    //將_spoolHead歸還到_spareSpool中,_spoolHead的nextSpoolBlock作爲新的_spoolHead
    SpoolBlock* tmp = _spoolHead->nextSpoolBlock;
    assert(_spoolHead != _spoolTail, "Spooling storage mix-up");
    _spoolHead->nextSpoolBlock = _spareSpool;
    _spareSpool = _spoolHead;
    _spoolHead = tmp;
    //對新的spoolHead,_firstIndex被置爲1,下一次遍歷從索引爲1的位置開始
    _firstIndex = 1;
  }
  return hdr;
}



//返回一個SpoolBlock的大小
size_t PromotionInfo::refillSize() const {
  const size_t CMSSpoolBlockSize = 256;
  //heap_word_size方法返回字寬數
  const size_t sz = heap_word_size(sizeof(SpoolBlock) + sizeof(markOop)
                                   * CMSSpoolBlockSize);
  //adjustObjectSize方法是按照對象分配的最低大小對齊                                 
  return CompactibleFreeListSpace::adjustObjectSize(sz);
}

SpoolBlock* PromotionInfo::getSpoolBlock() {
  SpoolBlock* res;
  //優先從空閒的SpoolBlock鏈表上分配
  if ((res = _spareSpool) != NULL) {
    _spareSpool = _spareSpool->nextSpoolBlock;
    res->nextSpoolBlock = NULL;
  } else {  // spare spool exhausted, get some from heap
    //沒有空閒的SpoolBlock,refillSize方法返回SpoolBlock的大小
    res = (SpoolBlock*)(space()->allocateScratch(refillSize()));
    if (res != NULL) {
      //初始化SpoolBlock
      res->init();
    }
  }
  assert(res == NULL || res->nextSpoolBlock == NULL, "postcondition");
  return res;
  }

5、ensure_spooling_space

     ensure_spooling_space用於判斷是否有充足的空閒保存markOop,如果沒有則獲取一個新的SpollBlock,如果獲取失敗則返回false,否則返回true。其調用鏈如下:

其源碼如下: 

bool ensure_spooling_space() {
    return has_spooling_space() || ensure_spooling_space_work();
}

inline bool has_spooling_space() {
    //_spoolTail是否有空閒空間
    return _spoolTail != NULL && _spoolTail->bufferSize > _nextIndex;
}

//判斷能否獲取一個新的SpoolBlock
bool PromotionInfo::ensure_spooling_space_work() {
  assert(!has_spooling_space(), "Only call when there is no spooling space");
  //_spoolTail沒有空閒空閒了,嘗試獲取一個新的SpoolBlock
  SpoolBlock* newSpool = getSpoolBlock();
  assert(newSpool == NULL ||
         (newSpool->bufferSize != 0 && newSpool->nextSpoolBlock == NULL),
        "getSpoolBlock() sanity check");
  if (newSpool == NULL) {
    return false;
  }
  _nextIndex = 1;
  if (_spoolTail == NULL) {
    //初始化_spoolTail
    _spoolTail = newSpool;
    if (_spoolHead == NULL) {
       //初始化_spoolHead
      _spoolHead = newSpool;
      _firstIndex = 1;
    } else {
      //不會走到此分支不可能_spoolHead不爲空而_spoolTail爲空
      assert(_splice_point != NULL && _splice_point->nextSpoolBlock == NULL,
             "Splice point invariant");
      _splice_point->nextSpoolBlock = newSpool;
    }
  } else {
    //_spoolTail不爲空,插入到_spoolTail的後面
    assert(_spoolHead != NULL, "spool list consistency");
    _spoolTail->nextSpoolBlock = newSpool;
    _spoolTail = newSpool;
  }
  return true;
}

三、CompactibleFreeListSpace

1、定義

     該類的定義在hotspot\src\share\vm\gc_implementation\concurrentMarkSweep\compactibleFreeListSpace.hpp中,繼承自CompactibleSpace,其包含的屬性如下:

  • const size_t _rescan_task_size;  //並行標記階段一次rescan處理的內存塊的大小
  • const size_t _marking_task_size; //並行標記階段一次mark 處理的內存塊的大小
  • SequentialSubTasksDone _conc_par_seq_tasks; //用來管控執行CMS GC的多個並行線程
  • BlockOffsetArrayNonContigSpace _bt; //用於記錄已分配的內存塊的起始地址
  • CMSCollector* _collector;  // 關聯的CMSCollector
  • ConcurrentMarkSweepGeneration* _gen;  //關聯的ConcurrentMarkSweepGeneration
  • static size_t IndexSetStart; //實際賦值等於最小內存塊的字寬數
  • static size_t IndexSetStride; //實際賦值等於分配對象時的最小字寬數,IndexSetStart和IndexSetStride用來限制_indexedFreeList實際使用的數組元素,如size小於IndexSetStart的_indexedFreeList數組元素實際是不使用的,會據此初始化每一個實際使用的_indexedFreeList數組元素對應的併發內存分配時使用的鎖
  • PromotionInfo _promoInfo;  // 用於保存分配的oop
  • static int   _lockRank; // _freelistLock使用的鎖類型
  • mutable Mutex _freelistLock; //併發的從CompactibleFreeListSpace中分配內存時使用的全局鎖
  • LinearAllocBlock _smallLinearAllocBlock; //用於線性分配小塊內存
  • FreeBlockDictionary<FreeChunk>::DictionaryChoice _dictionaryChoice;  //保存FreeBlockDictionary的實現類型,默認是表示二叉樹的dictionaryBinaryTree
  • AFLBinaryTreeDictionary* _dictionary;  //用於保存大小不規整的,不滿足_indexedFreeList支持的內存大小的空閒塊
  •  AdaptiveFreeList<FreeChunk> _indexedFreeList[IndexSetSize];//用來保存不同大小的空閒內存塊的鏈表,內存塊的大小從0到IndexSetSize
  •  bool       _fitStrategy;      //是否使用最佳適配策略,由配置項UseCMSBestFit決定,默認爲true
  •  bool       _adaptive_freelists; // 是否使用自適應空閒chunk 鏈表,由配置項UseCMSAdaptiveFreeLists決定,默認爲true
  • HeapWord*  _nearLargestChunk;
  • HeapWord* _sweep_limit;
  • mutable Mutex _parDictionaryAllocLock; //使用_dictionary並行分配內存時使用的鎖
  • Mutex* _indexedFreeListParLocks[IndexSetSize]; //與_indexedFreeList中實際使用的數組元素一一對應,保存其在並行分配時使用的鎖

其中三個靜態屬性的初始化如下:

 其中枚舉Mutex::leaf表示鎖類型,重點關注以下方法的實現。

 CompactibleFreeListSpace定義了一個枚舉描述內存分配時使用的常量,如下:

注意上述常量的單位都是字寬而非字節,即當申請的內存小於16字寬時使用_smallLinearAllocBlock分配內存,當申請的內存小於257字寬時使用_indexedFreeList分配。還有一個枚舉FitStrategyOptions描述內存分配的適配側率,如下:

其中FreeBlockBestFitFirst表示使用CMS內存分配時的最佳適配策略。

屬性_smallLinearAllocBlock的類LinearAllocBlock實際只是一個數據結構而已,其定義也在compactibleFreeListSpace.hpp中,如下,具體通過LinearAllocBlock線性分配內存的邏輯都CompactibleFreeListSpace的相關方法中。

其所有屬性都是public的, ptr表示分配內存的起始地址,word_size是剩餘可分配空間的大小,實際分配過程中會不斷往後移動ptr指針,同時減少word_size;refillSize是當LinearAllocBlock剩餘可用空間不足時重新申請用來填充LinearAllocBlock的內存塊的大小,_allocation_size_limit是通過LinearAllocBlock執行內存分配的內存大小上限,取值就是枚舉SmallForLinearAlloc的值。

2、構造方法和set_cms_values

     set_cms_values方法是一個靜態方法,用來初始化靜態屬性IndexSetStart和IndexSetStride,在執行構造方法前調用。構造方法用來初始化CompactibleFreeListSpace的相關屬性。這兩方法的調用鏈如下:

 這兩方法的實現如下:


void CompactibleFreeListSpace::set_cms_values() {
  // Set CMS global values
  assert(MinChunkSize == 0, "already set");

  //根據FreeChunk本身的大小計算FreeChunk,MinObjAlignmentInBytes等於配置項ObjectAlignmentInBytes的值,
  //該配置項表示對象分配時最小字節數,默認是8
  size_t min_chunk_size_in_bytes = align_size_up(sizeof(FreeChunk), MinObjAlignmentInBytes);
  MinChunkSize = min_chunk_size_in_bytes / BytesPerWord;

  assert(IndexSetStart == 0 && IndexSetStride == 0, "already set");
  IndexSetStart  = MinChunkSize;
  //MinObjAlignment由MinObjAlignmentInBytes換算成對應的字寬數,默認值是1
  IndexSetStride = MinObjAlignment;
}


CompactibleFreeListSpace::CompactibleFreeListSpace(BlockOffsetSharedArray* bs,
  MemRegion mr, bool use_adaptive_freelists,
  FreeBlockDictionary<FreeChunk>::DictionaryChoice dictionaryChoice) :
  _dictionaryChoice(dictionaryChoice),
  _adaptive_freelists(use_adaptive_freelists),
  _bt(bs, mr),
  _freelistLock(_lockRank--, "CompactibleFreeListSpace._lock", true),
  _parDictionaryAllocLock(Mutex::leaf - 1,  // == rank(ExpandHeap_lock) - 1
                          "CompactibleFreeListSpace._dict_par_lock", true),
  _rescan_task_size(CardTableModRefBS::card_size_in_words * BitsPerWord *
                    CMSRescanMultiple), //CMSRescanMultiple的默認值是32,表示並行rescan任務處理的卡表項的個數
  _marking_task_size(CardTableModRefBS::card_size_in_words * BitsPerWord *
                    CMSConcMarkMultiple), //CMSConcMarkMultiple的默認值是32,表示並行mark任務處理的卡表項的個數
  _collector(NULL)
{
  assert(sizeof(FreeChunk) / BytesPerWord <= MinChunkSize,
         "FreeChunk is larger than expected");
  _bt.set_space(this);
  //調用父類的initialize方法,初始化父類的相關屬性
  initialize(mr, SpaceDecorator::Clear, SpaceDecorator::Mangle);
  // dictionaryChoice枚舉表示FreeBlockDictionary的實現類型,現階段默認使用二叉樹實現,dictionarySplayTree的實現被暫時禁用了
  switch (dictionaryChoice) {
    case FreeBlockDictionary<FreeChunk>::dictionaryBinaryTree:
      _dictionary = new AFLBinaryTreeDictionary(mr);
      break;
    case FreeBlockDictionary<FreeChunk>::dictionarySplayTree:
    case FreeBlockDictionary<FreeChunk>::dictionarySkipList:
    default:
      warning("dictionaryChoice: selected option not understood; using"
              " default BinaryTreeDictionary implementation instead.");
  }
  assert(_dictionary != NULL, "CMS dictionary initialization");
  //初始化_indexedFreeList數組,每個FreeList都是空的,後面按需填充
  initializeIndexedFreeListArray();

  
  //use_adaptive_freelists參數最終由配置項UseCMSAdaptiveFreeLists決定,該配置項默認爲true
  //注意如果使用adaptive_freelists分配內存,則優先從_smallLinearAllocBlock中分配
  if (!use_adaptive_freelists) {
    //如果不使用adaptive_freelists分配內存
    FreeChunk* fc = _dictionary->get_chunk(mr.word_size(),
                                           FreeBlockDictionary<FreeChunk>::atLeast);
    HeapWord* addr = (HeapWord*) fc;
    _smallLinearAllocBlock.set(addr, fc->size() ,
      1024*SmallForLinearAlloc, fc->size());
  } else {
    //SmallForLinearAlloc是枚舉,值爲16
    _smallLinearAllocBlock.set(0, 0, 1024*SmallForLinearAlloc,
                               SmallForLinearAlloc);
  }
  // CMSIndexedFreeListReplenish是一個配置項,表示使用多少個chunk填充_indexedFreeList,默認值是4
  //這裏是校驗其不小於1
  CMSIndexedFreeListReplenish = MAX2((uintx)1, CMSIndexedFreeListReplenish);
  _promoInfo.setSpace(this);
  //UseCMSBestFit表示使用CMS的最佳適配策略,默認是true
  if (UseCMSBestFit) {
    _fitStrategy = FreeBlockBestFitFirst;
  } else {
    _fitStrategy = FreeBlockStrategyNone;
  }
  //check_free_list_consistency方法在生產版本中是空實現
  check_free_list_consistency();

  // Initialize locks for parallel case.

  if (CollectedHeap::use_parallel_gc_threads()) {
    for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
      //初始化並行分配內存用到的鎖,注意並不是所有的_indexedFreeList元素都有對應的鎖
      _indexedFreeListParLocks[i] = new Mutex(Mutex::leaf - 1, // == ExpandHeap_lock - 1
                                              "a freelist par lock",
                                              true);
      DEBUG_ONLY(
        _indexedFreeList[i].set_protecting_lock(_indexedFreeListParLocks[i]);
      )
    }
    _dictionary->set_par_lock(&_parDictionaryAllocLock);
  }
}

void CompactibleFreeListSpace::initializeIndexedFreeListArray() {
  //初始化_indexedFreeList,注意起始size是從0開始的,實際size爲0的元素並不會使用
  for (size_t i = 0; i < IndexSetSize; i++) {
    //reset方法將FreeList重置,並且設置hint
    _indexedFreeList[i].reset(IndexSetSize);
    //設置該FreeList保存的Chunk的大小
    _indexedFreeList[i].set_size(i);
    assert(_indexedFreeList[i].count() == 0, "reset check failed");
    assert(_indexedFreeList[i].head() == NULL, "reset check failed");
    assert(_indexedFreeList[i].tail() == NULL, "reset check failed");
    assert(_indexedFreeList[i].hint() == IndexSetSize, "reset check failed");
  }
}

#ifndef PRODUCT
void CompactibleFreeListSpace::check_free_list_consistency() const {
  assert((TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >::min_size() <= IndexSetSize),
    "Some sizes can't be allocated without recourse to"
    " linear allocation buffers");
  assert((TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >::min_size()*HeapWordSize == sizeof(TreeChunk<FreeChunk, AdaptiveFreeList<FreeChunk> >)),
    "else MIN_TREE_CHUNK_SIZE is wrong");
  assert(IndexSetStart != 0, "IndexSetStart not initialized");
  assert(IndexSetStride != 0, "IndexSetStride not initialized");
}
#endif

3、getFromListGreater / getChunkFromGreater

      getFromListGreater方法用於從比指定大小大的FreeList中查找空閒的內存塊,找到後再按照指定大小做切分,多餘的內存塊歸還到FreeList中。getChunkFromGreater方法包含了getFromListGreater,還支持從_dictionary屬性保存的空閒內存塊二叉樹中查找大於指定大小的內存塊,同樣的,找到後按照指定大小做切分,多餘的內存塊根據大小歸還到FreeList或者_dictionary屬性中保存。這兩方法的調用鏈如下:

其實現如下:

/* Requires fl->size >= numWords + MinChunkSize */
FreeChunk* CompactibleFreeListSpace::getFromListGreater(AdaptiveFreeList<FreeChunk>* fl,
  size_t numWords) {
  //獲取FreeList鏈表頭部的空閒的內存塊
  FreeChunk *curr = fl->head();
  size_t oldNumWords = curr->size();
  //校驗參數
  assert(numWords >= MinChunkSize, "Word size is too small");
  assert(curr != NULL, "List is empty");
  assert(oldNumWords >= numWords + MinChunkSize,
        "Size of chunks in the list is too small");
  
  //將該內存塊從鏈表中移除
  fl->remove_chunk(curr);
  //將curr按照numWords切分,多餘的內存塊放到_indexedFreeList或者_dictionary中
  FreeChunk* new_chunk = splitChunkAndReturnRemainder(curr, numWords);
  assert(new_chunk == NULL || new_chunk->is_free(),
    "Should be returning a free chunk");
  return new_chunk;
}

FreeChunk*
CompactibleFreeListSpace::splitChunkAndReturnRemainder(FreeChunk* chunk,
  size_t new_size) {
  assert_locked();
  //獲取chunk原來的大小
  size_t size = chunk->size();
  assert(size > new_size, "Split from a smaller block?");
  assert(is_aligned(chunk), "alignment problem");
  assert(size == adjustObjectSize(size), "alignment problem");
  //計算需要切分掉的大小
  size_t rem_size = size - new_size;
  assert(rem_size == adjustObjectSize(rem_size), "alignment problem");
  assert(rem_size >= MinChunkSize, "Free chunk smaller than minimum");
  //ffc就表示被切分掉的內存塊
  FreeChunk* ffc = (FreeChunk*)((HeapWord*)chunk + new_size);
  assert(is_aligned(ffc), "alignment problem");
  ffc->set_size(rem_size);
  ffc->link_next(NULL);
  ffc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
  //強制所有的修改指令執行
  OrderAccess::storestore();
  assert(chunk->is_free() && ffc->is_free(), "Error");
  //bt中記錄新切分出來的內存塊的起始位置
  _bt.split_block((HeapWord*)chunk, chunk->size(), new_size);
  if (rem_size < SmallForDictionary) {
    //是否並行垃圾收集,如果是則獲取對應FreeList的鎖
    bool is_par = (SharedHeap::heap()->n_par_threads() > 0);
    if (is_par) _indexedFreeListParLocks[rem_size]->lock();
    assert(!is_par ||
           (SharedHeap::heap()->n_par_threads() ==
            SharedHeap::heap()->workers()->active_workers()), "Mismatch");
    //將新的被切分出去的內存塊放入對應大小的_indexedFreeList中        
    returnChunkToFreeList(ffc);
    split(size, rem_size);
    //解鎖
    if (is_par) _indexedFreeListParLocks[rem_size]->unlock();
  } else {
    //將新的被切分出來的內存塊放到_dictionary中
    returnChunkToDictionary(ffc);
    split(size ,rem_size);
  }
  //重置chunk的大小
  chunk->set_size(new_size);
  return chunk;
}

void
CompactibleFreeListSpace::returnChunkToFreeList(FreeChunk* fc) {
  assert_locked();
  size_t size = fc->size();
  //校驗fc是一個單獨的沒有被切分的內存塊
  _bt.verify_single_block((HeapWord*) fc, size);
  //校驗fc還未被分配出去
  _bt.verify_not_unallocated((HeapWord*) fc, size);
  if (_adaptive_freelists) {
     //如果使用adaptive_freelists,插入到鏈表尾部
    _indexedFreeList[size].return_chunk_at_tail(fc);
  } else {
     //插入到鏈表頭部
    _indexedFreeList[size].return_chunk_at_head(fc);
  }
}

void
CompactibleFreeListSpace::returnChunkToDictionary(FreeChunk* chunk) {
  assert_locked();

  size_t size = chunk->size();
  _bt.verify_single_block((HeapWord*)chunk, size);
  //free方法實際是調整_unallocated_block屬性,標記chunk對應的內存塊未分配出去
  _bt.freed((HeapWord*)chunk, size);
  //將chunk放到_dictionary中管理
  _dictionary->return_chunk(chunk);
}

FreeChunk* CompactibleFreeListSpace::getChunkFromGreater(size_t numWords) {
  FreeChunk* ret;

  assert(numWords >= MinChunkSize, "Size is less than minimum");
  assert(linearAllocationWouldFail() || bestFitFirst(),
    "Should not be here");

  size_t i;
  size_t currSize = numWords + MinChunkSize;
  assert(currSize % MinObjAlignment == 0, "currSize should be aligned");
  //從currSize開始遍歷查找,如果某個FreeList有空閒的內存塊,則從該FreeList中分配指定大小的內存塊
  for (i = currSize; i < IndexSetSize; i += IndexSetStride) {
    AdaptiveFreeList<FreeChunk>* fl = &_indexedFreeList[i];
    if (fl->head()) {
      ret = getFromListGreater(fl, numWords);
      assert(ret == NULL || ret->is_free(), "Should be returning a free chunk");
      return ret;
    }
  }
  //所有的_indexedFreeList都是空的,嘗試從dictionary中分配
  //從dictionary中分配時要求內存大於SmallForDictionary,即IndexSetSize
  currSize = MAX2((size_t)SmallForDictionary,
                  (size_t)(numWords + MinChunkSize));

  /* Try to get a chunk that satisfies request, while avoiding
     fragmentation that can't be handled. */
  {
    //從_dictionary中查找滿足大小的內存塊
    ret =  dictionary()->get_chunk(currSize);
    if (ret != NULL) {
      assert(ret->size() - numWords >= MinChunkSize,
             "Chunk is too small");
      //調整bt的_unallocated_block屬性,表示ret對應的內存塊被分配出去了       
      _bt.allocated((HeapWord*)ret, ret->size());
      /*將ret按照numWords做切分,多餘的內存塊歸還到freeList或者_dictionary中*/
      (void) splitChunkAndReturnRemainder(ret, numWords);
      /* Label this as no longer a free chunk. */
      assert(ret->is_free(), "This chunk should be free");
      //去掉prev引用
      ret->link_prev(NULL);
    }
    assert(ret == NULL || ret->is_free(), "Should be returning a free chunk");
    return ret;
  }
  ShouldNotReachHere();
}

bool CompactibleFreeListSpace::linearAllocationWouldFail() const {
  return _smallLinearAllocBlock._word_size == 0;
}


FreeBlockDictionary<FreeChunk>* dictionary() const { return _dictionary; }

 4、split / coalBirth / coalDeath

     split方法是某個內存塊發生切分時調用的,用來增減AllocationStats類維護的計數器,_indexedFreeList和_dictionary都有一個AllocationStats類屬性。coalBirth和coalDeath的功能同split,不過是在發生內存塊合併時調用的。這幾個方法涉及的計數器的定義如下:

  • ssize_t     _coal_births;      // 因爲內存塊合併增加的內存塊的個數
  • ssize_t     _coal_deaths;      // 因爲內存塊合併而減少的內存塊的個數
  • ssize_t     _split_births;     // 因爲內存塊切分增加的內存塊的個數
  • ssize_t     _split_deaths;     // 因爲內存塊切分減少的內存塊的個數
  • ssize_t     _surplus;         // 一個綜合指標,CompactibleFreeListSpace::bestFitSmall方法使用的,隨着_coal_births和_split_births的增加而減少,隨着_coal_deaths和_split_deaths的增加而減少

其實現如下: 

//from是被切分前的大小,to1是被切分後多餘的內存塊的大小
void CompactibleFreeListSpace::split(size_t from, size_t to1) {
  size_t to2 = from - to1;
  splitDeath(from);
  split_birth(to1);
  split_birth(to2);
}

void CompactibleFreeListSpace::splitDeath(size_t size) {
  if (size  < SmallForDictionary) {
    smallSplitDeath(size);
  } else {
    //底層實現跟smallSplitDeath,都是調整計數器
    dictionary()->dict_census_update(size,
                                   true /* split */,
                                   false /* birth */);
  }
}

void CompactibleFreeListSpace::smallSplitDeath(size_t size) {
  assert(size < SmallForDictionary, "Size too large for indexed list");
  AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
  //這兩方法都是調整AdaptiveFreeList的_allocation_stats屬性保存的計數器,用來統計內存分配情況
  fl->increment_split_deaths();
  fl->decrement_surplus();
}

//AdaptiveFreeList的實現
void increment_split_deaths() {
    assert_proper_lock_protection();
    _allocation_stats.increment_split_deaths();
}

void decrement_surplus() {
    assert_proper_lock_protection();
    _allocation_stats.decrement_surplus();
  }

//_allocation_stats屬性即AllocationStats類的實現
void increment_split_deaths() { _split_deaths++; }

 void decrement_surplus() { _surplus--; }

//實現同splitDeath,也是調整計數器
void CompactibleFreeListSpace::split_birth(size_t size) {
  if (size  < SmallForDictionary) {
    smallSplitBirth(size);
  } else {
    dictionary()->dict_census_update(size,
                                   true /* split */,
                                   true /* birth */);
  }
}


void CompactibleFreeListSpace::smallSplitBirth(size_t size) {
  assert(size < SmallForDictionary, "Size too large for indexed list");
  AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
  fl->increment_split_births();
  fl->increment_surplus();
}

//coalBirth實現同上,增減AllocationStats類維護的計數器
void CompactibleFreeListSpace::coalBirth(size_t size) {
  if (size  < SmallForDictionary) {
    smallCoalBirth(size);
  } else {
    dictionary()->dict_census_update(size,
                                   false /* split */,
                                   true /* birth */);
  }
}

void CompactibleFreeListSpace::smallCoalBirth(size_t size) {
  assert(size < SmallForDictionary, "Size too large for indexed list");
  AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
  fl->increment_coal_births();
  fl->increment_surplus();
}

void CompactibleFreeListSpace::coalDeath(size_t size) {
  if(size  < SmallForDictionary) {
    smallCoalDeath(size);
  } else {
    dictionary()->dict_census_update(size,
                                   false /* split */,
                                   false /* birth */);
  }
}

void CompactibleFreeListSpace::smallCoalDeath(size_t size) {
  assert(size < SmallForDictionary, "Size too large for indexed list");
  AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[size];
  fl->increment_coal_deaths();
  fl->decrement_surplus();
}

  這些方法的調用鏈如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章