目錄
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();
}
這些方法的調用鏈如下: