美文网首页
CompactibleFreeListSpace 之 2

CompactibleFreeListSpace 之 2

作者: 程序员札记 | 来源:发表于2022-11-14 08:23 被阅读0次

    1、getChunkFromIndexedFreeList
    getChunkFromIndexedFreeList方法用于从_indexedFreeList中获取指定大小size的内存块,如果size对应的_indexedFreeList是没有空闲的内存块,则尝试从surplus大于0,保存的内存块大于size的_indexedFreeList中分配,如果分配成功则将内存块按照size做切分,多余的内存块归还到对应大小的_indexedFreeList中;如果依然分配失败,则从更大的_indexedFreeList或者_dictionary中一次申请等于多个size大小的内存块,申请成功后将这个内存块切分成多个等于size的内存块填充到size对应的_indexedFreeList中;如果申请失败则返回NULL。其调用链如下:

    image.png

    其实现如下:

    FreeChunk*
    CompactibleFreeListSpace::getChunkFromIndexedFreeList(size_t size) {
      assert_locked();
      //校验size小于SmallForDictionary
      assert(size < SmallForDictionary, "just checking");
      FreeChunk* res;
      //从对应大小的FreeList中获取一个空闲的内存块
      res = _indexedFreeList[size].get_chunk_at_head();
      if (res == NULL) {
        //获取失败,尝试从大于size的FreeList中分配,如果还失败则尝试填充size对应的FreeList
        res = getChunkFromIndexedFreeListHelper(size);
      }
      _bt.verify_not_unallocated((HeapWord*) res, size);
      assert(res == NULL || res->size() == size, "Incorrect block size");
      return res;
    }
     
    //replenish默认为true
    FreeChunk*
    CompactibleFreeListSpace::getChunkFromIndexedFreeListHelper(size_t size,
      bool replenish) {
      assert_locked();
      FreeChunk* fc = NULL;
      if (size < SmallForDictionary) {
        //对应大小的FreeList要么是空的,要么是超负荷的
        assert(_indexedFreeList[size].head() == NULL ||
          _indexedFreeList[size].surplus() <= 0,
          "List for this size should be empty or under populated");
        // Try best fit in exact lists before replenishing the list
        //如果没采用最佳适配策略或者使用最佳适配策略分配失败
        if (!bestFitFirst() || (fc = bestFitSmall(size)) == NULL) {
         
          FreeChunk* newFc = NULL;
          //CMSIndexedFreeListReplenish表示使用多少个内存块来填充FreeList,默认值是4
          const size_t replenish_size = CMSIndexedFreeListReplenish * size;
          if (replenish_size < SmallForDictionary) {
            if (_indexedFreeList[replenish_size].surplus() > 0 &&
                _indexedFreeList[replenish_size].head() != NULL) {
              //如果replenish_size对应的FreeList有空闲的内存块
              newFc = _indexedFreeList[replenish_size].get_chunk_at_head();
            } else if (bestFitFirst()) {
              //尝试从大于replenish_size的有空闲的内存块FreeList中分配
              newFc = bestFitSmall(replenish_size);
            }
          }
          if (newFc == NULL && replenish_size > size) {
            assert(CMSIndexedFreeListReplenish > 1, "ctl pt invariant");
            //如果分配失败,则按照replenish_size重新分配,实际逻辑是查找replenish_size*CMSIndexedFreeListReplenish大小的空闲内存块
            //如果找到了就直接返回,不用填充对应大小的FreeList
            newFc = getChunkFromIndexedFreeListHelper(replenish_size, false);
          }
          //如果newFc分配成功
          if (newFc != NULL) {
            //CMSReplenishIntermediate表示是否填充所有中间的FreeList,默认值是true
            if  (replenish || CMSReplenishIntermediate) {
              size_t i;
              FreeChunk *curFc, *nextFc;
              //计算填充FreeList的内存块的个数
              size_t num_blk = newFc->size() / size;
              assert(num_blk >= 1, "Smaller than requested?");
              assert(newFc->size() % size == 0, "Should be integral multiple of request");
              if (num_blk > 1) {
                //大于1说明newFc需要被切分成多个,需要增加replenish_size对应的FreeList的计数器
                splitDeath(replenish_size);
              }
              //按照size遍历newFc,注意 i < (num_blk - 1)
              for (curFc = newFc, nextFc = (FreeChunk*)((HeapWord*)curFc + size),
                   i = 0;
                   i < (num_blk - 1);
                   curFc = nextFc, nextFc = (FreeChunk*)((HeapWord*)nextFc + size),
                   i++) {
                curFc->set_size(size);
                //校验fc对应的内存块未分配
                _bt.verify_not_unallocated((HeapWord*) fc, size);
                //将curFc放到FreeLisst链表尾部
                _indexedFreeList[size].return_chunk_at_tail(curFc, false);
                //记录curFc的起始位置
                _bt.mark_block((HeapWord*)curFc, size);
                //增加因为切分增加的内存块的数量
                split_birth(size);
              }
     
              //i=num_blk - 1会终止循环,即保留最后一个切分出来的内存块返回给调用方
              assert((HeapWord*)nextFc == (HeapWord*)newFc + num_blk*size,
                "inconsistency in carving newFc");
              curFc->set_size(size);
              _bt.mark_block((HeapWord*)curFc, size);
              split_birth(size);
              fc = curFc;
            } else {
              //如果replenish为false,则直接返回newFc
              fc = newFc;
            }
          }
        }
      } else {
        //从_dictionary中查找大于等于size的空闲内存块,只有填充FreeList时可能走到此逻辑
        fc = getChunkFromDictionaryExact(size);
      }
      return fc;
    }
     
    bool bestFitFirst() { return _fitStrategy == FreeBlockBestFitFirst; }
     
    //尝试从surplus大于0的且有空闲内存块的FreeList中分配
    FreeChunk* CompactibleFreeListSpace::bestFitSmall(size_t numWords) {
      /* hint属性表示下一个surplus大于0的且有空闲内存块的FreeList对应的内存块大小 */
      size_t start = align_object_size(numWords + MinChunkSize);
      if (start < IndexSetSize) {
        AdaptiveFreeList<FreeChunk>* it   = _indexedFreeList;
        size_t    hint = _indexedFreeList[start].hint();
        while (hint < IndexSetSize) {
          assert(hint % MinObjAlignment == 0, "hint should be aligned");
          AdaptiveFreeList<FreeChunk> *fl = &_indexedFreeList[hint];
          //如果fl的surplus大于0且有空闲的内存块
          if (fl->surplus() > 0 && fl->head() != NULL) {
            //重置hit属性
            _indexedFreeList[start].set_hint(hint);
            //从f1中分配,因为f1中保存的内存块实际大于numWords,所以从f1中获取的空闲内存块需要做切分,多余
            //的内存块归还到FreeList中
            FreeChunk* res = getFromListGreater(fl, numWords);
            assert(res == NULL || res->is_free(),
              "Should be returning a free chunk");
            return res;
          }
          hint = fl->hint(); /* keep looking */
        }
        /*没有找到将hint置为IndexSetSize,这样下次就不会再遍历了*/
        it[start].set_hint(IndexSetSize);
      }
      return NULL;
    }
     
    FreeChunk*
    CompactibleFreeListSpace::getChunkFromDictionaryExact(size_t size) {
      assert_locked();
      //从_dictionary查找大于等于size的空闲内存块
      FreeChunk* fc = _dictionary->get_chunk(size,
                                             FreeBlockDictionary<FreeChunk>::atLeast);
      if (fc == NULL) {
        //查找失败,返回NULL
        return fc;
      }
      _bt.allocated((HeapWord*)fc, fc->size());
      if (fc->size() == size) {
        _bt.verify_single_block((HeapWord*)fc, size);
        return fc;
      }
      assert(fc->size() > size, "get_chunk() guarantee");
      if (fc->size() < size + MinChunkSize) {
        //如果fc的多余内存块小于MinChunkSize,则将fc归还重新查找
        returnChunkToDictionary(fc);
        fc = _dictionary->get_chunk(size + MinChunkSize,
                                    FreeBlockDictionary<FreeChunk>::atLeast);
        if (fc == NULL) {
          return NULL;
        }
        _bt.allocated((HeapWord*)fc, fc->size());
      }
      assert(fc->size() >= size + MinChunkSize, "tautology");
      //将fc做切分
      fc = splitChunkAndReturnRemainder(fc, size);
      assert(fc->size() == size, "chunk is wrong size");
      _bt.verify_single_block((HeapWord*)fc, size);
      return fc;
    }
    

    2、getChunkFromSmallLinearAllocBlock
    getChunkFromSmallLinearAllocBlock方法用于从_smallLinearAllocBlock中分配指定大小size的内存块,如果_smallLinearAllocBlock中剩余可用空间大于或者等于size则直接移动_smallLinearAllocBlock的ptr指针,同时减少_word_size;如果剩余可用空间不足,则将剩余空间整体归还到对应大小的FreeList中,然后从FreeList或者_dictionary中分配一个大小等于_refillSize的内存块,重新填充_smallLinearAllocBlock,然后再分配指定size大小的内存块。其调用链如下:


    image.png

    其实现如下:

    HeapWord*
    CompactibleFreeListSpace::getChunkFromSmallLinearAllocBlock(size_t size) {
      assert_locked();
      assert(size >= MinChunkSize, "minimum chunk size");
      //校验size小于smallLinearAllocBlock的分配上限_allocation_size_limit
      assert(size <  _smallLinearAllocBlock._allocation_size_limit,
        "maximum from smallLinearAllocBlock");
      return getChunkFromLinearAllocBlock(&_smallLinearAllocBlock, size);
    }
     
     
    HeapWord*
    CompactibleFreeListSpace::getChunkFromLinearAllocBlock(LinearAllocBlock *blk,
                                                           size_t size) {
      assert_locked();
      assert(size >= MinChunkSize, "too small");
      HeapWord* res = NULL;
     
      if (blk->_word_size == 0) {
        //_word_size等于0表示blk没有可分配内存
        assert(blk->_ptr == NULL, "consistency check");
        return NULL;
      }
      assert(blk->_word_size != 0 && blk->_ptr != NULL, "consistency check");
      //正常分配size大小的内存块,要求blk的剩余可用空间大于size + MinChunkSize
      res = getChunkFromLinearAllocBlockRemainder(blk, size);
      if (res != NULL) return res;
     
      // blk的剩余可用空间不足
      if (blk->_word_size == size) { //剩余空间刚好等于size
        res = blk->_ptr;
        _bt.allocated(res, blk->_word_size);
      } else if (size + MinChunkSize <= blk->_refillSize) {//剩余空间小于size
        size_t sz = blk->_word_size;
        if (sz < SmallForDictionary) {
          //sz肯定小于SmallForDictionary,更新bt的_unallocated_block属性,表明这个内存块已经分配出去了
          _bt.allocated(blk->_ptr, sz);
        }
        //将剩余的内存块归还到对应大小的FreeList中
        addChunkToFreeLists(blk->_ptr, sz);
        //增加split_birth计数器
        split_birth(sz);
      } else {
        //size大于_refillSize,实际不可能
        return NULL;
      }
      //将blk恢复成初始状态
      blk->_ptr = NULL; blk->_word_size = 0;
      //执行LinearAllocBlock的填充,即申请一个新的内存块给LinearAllocBlock分配内存
      refillLinearAllocBlock(blk);
      assert(blk->_ptr == NULL || blk->_word_size >= size + MinChunkSize,
             "block was replenished");
      if (res != NULL) {
        ///剩余空间刚好等于size时会走到此分支
        split_birth(size);
        repairLinearAllocBlock(blk);
      } else if (blk->_ptr != NULL) {
        //refill成功找到一块满足大小的空闲内存块时走到此分支
        //执行跟getChunkFromLinearAllocBlockRemainder一样的逻辑
        res = blk->_ptr;
        size_t blk_size = blk->_word_size;
        blk->_word_size -= size;
        blk->_ptr  += size;
        split_birth(size);
        repairLinearAllocBlock(blk);
        OrderAccess::storestore();
        _bt.split_block(res, blk_size, size);  // adjust block offset table
      }
      return res;
    }
     
    HeapWord*  CompactibleFreeListSpace::getChunkFromLinearAllocBlockRemainder(
                                            LinearAllocBlock* blk,
                                            size_t size) {
      assert_locked();
      assert(size >= MinChunkSize, "too small");
     
      HeapWord* res = NULL;
      // This is the common case.  Keep it simple.
      if (blk->_word_size >= size + MinChunkSize) {
        //如果剩余可分配空间充足
        assert(blk->_ptr != NULL, "consistency check");
        res = blk->_ptr;
        size_t blk_size = blk->_word_size;
        //_word_size减去size,_ptr往前移动size
        blk->_word_size -= size;
        blk->_ptr  += size;
        //增加因为split_birth计数器
        split_birth(size);
        //重置剩余空间的属性
        repairLinearAllocBlock(blk);
        //强制之前的修改指令生效
        OrderAccess::storestore();
        //记录新切分出去的内存块的起始位置
        _bt.split_block(res, blk_size, size);  // adjust block offset table
        _bt.allocated(res, size);
      }
      return res;
    }
     
    void CompactibleFreeListSpace::repairLinearAllocBlock(LinearAllocBlock* blk) {
      assert_locked();
      if (blk->_ptr != NULL) {
        assert(blk->_word_size != 0 && blk->_word_size >= MinChunkSize,
               "Minimum block size requirement");
        //重置剩余空间的size,将其标记为free和dontCoalesce      
        FreeChunk* fc = (FreeChunk*)(blk->_ptr);
        fc->set_size(blk->_word_size);
        fc->link_prev(NULL);   // mark as free
        fc->dontCoalesce();
        assert(fc->is_free(), "just marked it free");
        assert(fc->cantCoalesce(), "just marked it uncoalescable");
      }
    }
     
    void
    CompactibleFreeListSpace::addChunkToFreeLists(HeapWord* chunk,
                                                  size_t     size) {
      // check that the chunk does lie in this space!
      assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!");
      assert_locked();
      _bt.verify_single_block(chunk, size);
     
      FreeChunk* fc = (FreeChunk*) chunk;
      //设置FreeChunk大小
      fc->set_size(size);
      debug_only(fc->mangleFreed(size));
      //根据大小归还到FreeList或者dictionary中
      if (size < SmallForDictionary) {
        returnChunkToFreeList(fc);
      } else {
        returnChunkToDictionary(fc);
      }
    }
     
    void
    CompactibleFreeListSpace::refillLinearAllocBlock(LinearAllocBlock* blk) {
      assert_locked();
      assert(blk->_word_size == 0 && blk->_ptr == NULL,
             "linear allocation block should be empty");
      FreeChunk* fc;
      //如果小于SmallForDictionary则从IndexedFreeList中分配,否则从dictionary中分配
      if (blk->_refillSize < SmallForDictionary &&
          (fc = getChunkFromIndexedFreeList(blk->_refillSize)) != NULL) {
        
      } else {
        fc = getChunkFromDictionary(blk->_refillSize);
      }
      if (fc != NULL) {
        //获取成功,重置ptr和_word_size属性
        blk->_ptr  = (HeapWord*)fc;
        blk->_word_size = fc->size();
        fc->dontCoalesce();   // to prevent sweeper from sweeping us up
      }
    }
     
    FreeChunk*
    CompactibleFreeListSpace::getChunkFromDictionary(size_t size) {
      assert_locked();
      FreeChunk* fc = _dictionary->get_chunk(size,
                                             FreeBlockDictionary<FreeChunk>::atLeast);
      if (fc == NULL) {
        return NULL;
      }
      _bt.allocated((HeapWord*)fc, fc->size());
      if (fc->size() >= size + MinChunkSize) {
        //如果fc的空间过大则做切分
        fc = splitChunkAndReturnRemainder(fc, size);
      }
      assert(fc->size() >= size, "chunk too small");
      assert(fc->size() < size + MinChunkSize, "chunk too big");
      _bt.verify_single_block((HeapWord*)fc, fc->size());
      return fc;
    }
    

    注意getChunkFromLinearAllocBlock方法中如果判断LinearAllocBlock的_word_size等于0了就返回NULL了,并不会执行填充,只要当剩余空间不足分配指定大小size时才会填充,而LinearAllocBlock在CompactibleFreeListSpace的构造方法中初始化时_word_size就等于0,那么LinearAllocBlock的第一次填充究竟是什么时候了?参考执行LinearAllocBlock填充的refillLinearAllocBlock方法的调用链,如下:


    image.png

    从调用链可知第一次填充应该是发生第一次GC后执行的。

    3、allocate / par_allocate
    这两方法用来从CompactibleFreeListSpace中分配指定大小的内存块,allocate方法要求调用方已经获取了锁,par_allocate的底层实现还是allocate,不过多了一个获取锁的动作。当获取的内存大小size小于IndexSetSize时,会优先从FreeList中尝试分配,分配失败再尝试从LinearAllocBlock中分配,最后再尝试从大于size的FreeList或者dictionary中分配;当size大于IndexSetSize时,直接从dictionary中分配,分配失败再尝试从LinearAllocBlock分配,尽可能避免分配失败触发GC。其调用链如下:

    image.png

    其实现如下:

    HeapWord* CompactibleFreeListSpace::par_allocate(size_t size) {
      //获取锁
      MutexLockerEx x(freelistLock(), Mutex::_no_safepoint_check_flag);
      return allocate(size);
    }
     
    Mutex* freelistLock() const { return &_freelistLock; }
     
    HeapWord* CompactibleFreeListSpace::allocate(size_t size) {
      //校验已获取了锁
      assert_lock_strong(freelistLock());
      HeapWord* res = NULL;
      //校验size已经是经过内存对齐的
      assert(size == adjustObjectSize(size),
             "use adjustObjectSize() before calling into allocate()");
      //_adaptive_freelists默认为true
      if (_adaptive_freelists) {
        res = allocate_adaptive_freelists(size);
      } else {  // non-adaptive free lists
        res = allocate_non_adaptive_freelists(size);
      }
     
      if (res != NULL) {
        //分配成功,校验res在堆内存中
        assert(is_in_reserved(res), "Not in this space!");
        assert(is_aligned((void*)res), "alignment check");
     
        FreeChunk* fc = (FreeChunk*)res;
        //将fc标记为NotFree
        fc->markNotFree();
        assert(!fc->is_free(), "shouldn't be marked free");
        assert(oop(fc)->klass_or_null() == NULL, "should look uninitialized");
        //校验res对应的内存块的起始位置正确设置
        _bt.verify_single_block(res, size);
        _bt.verify_not_unallocated(res, size);
        debug_only(fc->mangleAllocated(size));
      }
     
      return res;
    }
     
    inline static size_t adjustObjectSize(size_t size) {
        //将size按照MinObjAlignment向上做内存对齐
        return (size_t) align_object_size(MAX2(size, (size_t)MinChunkSize));
    }
     
    HeapWord* CompactibleFreeListSpace::allocate_adaptive_freelists(size_t size) {
      assert_lock_strong(freelistLock());
      HeapWord* res = NULL;
      assert(size == adjustObjectSize(size),
             "use adjustObjectSize() before calling into allocate()");
     
      if (size < IndexSetSize) {
        //尝试从IndexedFreeList中分配
        res = (HeapWord*) getChunkFromIndexedFreeList(size);
        if(res != NULL) {
          //如果等于表示res还未从队列中移除
          assert(res != (HeapWord*)_indexedFreeList[size].head(),
            "Not removed from free list");
        } else if (size < _smallLinearAllocBlock._allocation_size_limit &&
            (res = getChunkFromSmallLinearAllocBlock(size)) != NULL) { //尝试从SmallLinearAllocBlock中分配
           
        } else {
          //尝试从大于size的FreeList或者dictionary中分配
          res = (HeapWord*) getChunkFromGreater(size);
        }
      } else {
        //大于IndexSetSize时,直接尝试从dictionary中分配
        res = (HeapWord*) getChunkFromDictionaryExact(size);
        if (res == NULL) {
          //尝试从SmallLinearAllocBlock中分配,尽可能避免分配失败触发GC
          //注意此时size肯定大于_smallLinearAllocBlock._allocation_size_limit,getChunkFromSmallLinearAllocBlockRemainder方法不会做校验
          res = getChunkFromSmallLinearAllocBlockRemainder(size);
        }
      }
     
      return res;
    }
     
    HeapWord*
    CompactibleFreeListSpace::getChunkFromSmallLinearAllocBlockRemainder(size_t size) {
      return getChunkFromLinearAllocBlockRemainder(&_smallLinearAllocBlock, size);
    }
    

    4、par_get_chunk_of_blocks
    par_get_chunk_of_blocks是CFLS_LAB类用来并行的批量获取多个指定大小size的内存块,CFLS_LAB类是并行GC时用来申请本地线程缓存用的。par_get_chunk_of_blocks方法会首先尝试从FreeList中查找,然后再尝试从dictionary中查找,查找过程中如果发现有整数倍size大小的空闲内存块,则将其切分成多个分别添加到目标FreeList的链表头部。其调用链如下:

    image.png

    其实现如下:

    void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList<FreeChunk>* fl) {
      assert(fl->count() == 0, "Precondition.");
      assert(word_sz < CompactibleFreeListSpace::IndexSetSize,
             "Precondition");
      //首先尝试从FreeList中获取最多n个word_sz的内存块并添加到fl中
      if (par_get_chunk_of_blocks_IFL(word_sz, n, fl)) {
        //获取成功
        return;
      }
     
      //获取失败,尝试从dictionary中获取
      par_get_chunk_of_blocks_dictionary(word_sz, n, fl);
    }
     
    //word_sz是fl对应的内存块的大小,n是添加到fl中的内存块的最大数量
    bool CompactibleFreeListSpace:: par_get_chunk_of_blocks_IFL(size_t word_sz, size_t n, AdaptiveFreeList<FreeChunk>* fl) {
      {
        bool found;
        int  k;
        size_t cur_sz;
        //CMSSplitIndexedFreeListBlocks表示是否允许批量的切分来自FreeList的内存块,默认是true
        //cur_sz从word_sz开始,然后成倍增加,直到cur_sz大于IndexSetSize
        for (k = 1, cur_sz = k * word_sz, found = false;
             (cur_sz < CompactibleFreeListSpace::IndexSetSize) &&
             (CMSSplitIndexedFreeListBlocks || k <= 1);
             k++, cur_sz = k * word_sz) {
          AdaptiveFreeList<FreeChunk> fl_for_cur_sz;  // Empty.
          fl_for_cur_sz.set_size(cur_sz);
          {
            //获取cur_sz对应的_indexedFreeList的锁
            MutexLockerEx x(_indexedFreeListParLocks[cur_sz],
                            Mutex::_no_safepoint_check_flag);
            AdaptiveFreeList<FreeChunk>* gfl = &_indexedFreeList[cur_sz];
            if (gfl->count() != 0) {
              //如果gfl有空闲的内存块
              //nn是我们需要从gfl中获取的内存块的个数
              const size_t nn = MAX2(n/k, (size_t)1);
              //从gfl中获取前nn个空闲内存块,将结果保存到fl_for_cur_sz中
              gfl->getFirstNChunksFromList(nn, &fl_for_cur_sz);
              found = true;
              if (k > 1) {
                // 如果k大于1说明需要做切分了,更新gfl中split_death计数器
                ssize_t deaths = gfl->split_deaths() +
                                 fl_for_cur_sz.count();
                gfl->set_split_deaths(deaths);
              }
            }
          }
          // Now transfer fl_for_cur_sz to fl.  Common case, we hope, is k = 1.
          if (found) {
            if (k == 1) {
              //如果k等于1,则不需要做切分,直接将fl_for_cur_sz中的内存块转移到f1中
              fl->prepend(&fl_for_cur_sz);
            } else {
              //如果k大于1,则需要将fl_for_cur_sz中的每个内存块切分成k个
              FreeChunk* fc;
              //get_chunk_at_head返回链表头部的FreeChunk,并将其从链表中移除
              while ((fc = fl_for_cur_sz.get_chunk_at_head()) != NULL) {
                //获取原始的内存大小
                size_t fc_size = fc->size();
                assert(fc->is_free(), "Error");
                //按照从高到低的相反的顺序遍历fc,从而当外部访问fc时依然会把fc当做一个单一的没有做过切分的空闲内存块
                for (int i = k-1; i >= 0; i--) {
                  FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
                  assert((i != 0) ||
                            ((fc == ffc) && ffc->is_free() &&
                             (ffc->size() == k*word_sz) && (fc_size == word_sz)),
                            "Counting error");
                  //设置内存块大小          
                  ffc->set_size(word_sz);
                  ffc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
                  ffc->link_next(NULL);
                  // Above must occur before BOT is updated below.
                  OrderAccess::storestore();
                  //记录起始位置
                  _bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */);
                  fc_size -= word_sz;
                  assert(fc_size == i*word_sz, "Error");
                  _bt.verify_not_unallocated((HeapWord*)ffc, word_sz);
                  _bt.verify_single_block((HeapWord*)fc, fc_size);
                  _bt.verify_single_block((HeapWord*)ffc, word_sz);
                  //将切分的内存块ffc放到fl链表的头部
                  fl->return_chunk_at_head(ffc);
                }
                //fc遍历完成,校验切分的内存块没有插入到链表的尾部
                assert(fl->tail()->next() == NULL, "List invariant.");
              }
            }
            //fl_for_cur_sz中的内存块都已经转移到fl中
            size_t num = fl->count();
            //获取fl对应的锁,更新split_birth计数器
            MutexLockerEx x(_indexedFreeListParLocks[word_sz],
                            Mutex::_no_safepoint_check_flag);
            ssize_t births = _indexedFreeList[word_sz].split_births() + num;
            _indexedFreeList[word_sz].set_split_births(births);
            //如果找到了一个就返回
            return true;
          }
        }
        return found;
      }
    }
     
    void CompactibleFreeListSpace:: par_get_chunk_of_blocks_dictionary(size_t word_sz, size_t targetted_number_of_chunks, AdaptiveFreeList<FreeChunk>* fl) {
      
      //从Dictionary中尽可能获取最大的word_sz的整数倍的内存块
      FreeChunk* fc = get_n_way_chunk_to_split(word_sz, targetted_number_of_chunks);
     
      if (fc == NULL) {
        //获取失败
        return;
      }
      //获取成功,n是最终被切分出的内存块的数量
      size_t n = fc->size() / word_sz;
     
      assert((ssize_t)n > 0, "Consistency");
      //执行切分,从高地址到低地址按照相反的顺序遍历,逻辑跟par_get_chunk_of_blocks_IFL一只
      size_t fc_size = n * word_sz;
      // All but first chunk in this loop
      for (ssize_t i = n-1; i > 0; i--) {
        FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
        ffc->set_size(word_sz);
        ffc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
        ffc->link_next(NULL);
        // Above must occur before BOT is updated below.
        OrderAccess::storestore();
        // splitting from the right, fc_size == (n - i + 1) * wordsize
        _bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */);
        fc_size -= word_sz;
        _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size());
        _bt.verify_single_block((HeapWord*)ffc, ffc->size());
        _bt.verify_single_block((HeapWord*)fc, fc_size);
        //将切分处的FreeChunk放到fl的链表头部
        fl->return_chunk_at_head(ffc);
      }
      //处理i等于0的情形,即第一个word_sz的内存块
      assert(fc->is_free() && fc->size() == n*word_sz, "Error: should still be a free block");
      // The blocks above should show their new sizes before the first block below
      fc->set_size(word_sz);
      fc->link_prev(NULL);    // idempotent wrt free-ness, see assert above
      fc->link_next(NULL);
      _bt.verify_not_unallocated((HeapWord*)fc, fc->size());
      _bt.verify_single_block((HeapWord*)fc, fc->size());
      fl->return_chunk_at_head(fc);
     
      assert((ssize_t)n > 0 && (ssize_t)n == fl->count(), "Incorrect number of blocks");
      {
        //获取word_sz对应的FreeList的锁,并更新计数器
        MutexLockerEx x(_indexedFreeListParLocks[word_sz],
                        Mutex::_no_safepoint_check_flag);
        const ssize_t births = _indexedFreeList[word_sz].split_births() + n;
        _indexedFreeList[word_sz].set_split_births(births);
      }
     
      // TRAP
      assert(fl->tail()->next() == NULL, "List invariant.");
    }
     
    FreeChunk* CompactibleFreeListSpace::get_n_way_chunk_to_split(size_t word_sz, size_t n) {
     
      FreeChunk* fc = NULL;
      FreeChunk* rem_fc = NULL;
      size_t rem;
      {
        //获取Dictionary对应的锁
        MutexLockerEx x(parDictionaryAllocLock(),
                        Mutex::_no_safepoint_check_flag);
        //不断遍历,尝试找到最大的word_sz的整数倍的内存块
        while (n > 0) {
          fc = dictionary()->get_chunk(MAX2(n * word_sz, _dictionary->min_size()),
                                      FreeBlockDictionary<FreeChunk>::atLeast);
          if (fc != NULL) {
            break;
          } else {
            n--;
          }
        }
        //查找失败
        if (fc == NULL) return NULL;
        // Otherwise, split up that block.
        assert((ssize_t)n >= 1, "Control point invariant");
        assert(fc->is_free(), "Error: should be a free block");
        _bt.verify_single_block((HeapWord*)fc, fc->size());
        //nn肯定小于等于n
        const size_t nn = fc->size() / word_sz;
        n = MIN2(nn, n);
        assert((ssize_t)n >= 1, "Control point invariant");
        //计算多余的内存空间,如果小于MinChunkSize,则加上一个word_sz
        rem = fc->size() - n * word_sz;
        if (rem > 0 && rem < MinChunkSize) {
          n--; rem += word_sz;
        }
        // Note that at this point we may have n == 0.
        assert((ssize_t)n >= 0, "Control point invariant");
     
        if (n == 0) {
          //n等于0,说明找到的fc内存空间小于word_sz+MinChunkSize,将其归还
          returnChunkToDictionary(fc);
          return NULL;
        }
     
        _bt.allocated((HeapWord*)fc, fc->size(), true /* reducing */);  // update _unallocated_blk
        //更新split_birth计数器
        dictionary()->dict_census_update(fc->size(),
                                         true /*split*/,
                                         false /*birth*/);
     
        if (rem > 0) {
          //prefix_size是获取的有效内存块的大小
          size_t prefix_size = n * word_sz;
          //rem_fc表示多余的内存块
          rem_fc = (FreeChunk*)((HeapWord*)fc + prefix_size);
          rem_fc->set_size(rem);
          rem_fc->link_prev(NULL); // Mark as a free block for other (parallel) GC threads.
          rem_fc->link_next(NULL);
          // Above must occur before BOT is updated below.
          assert((ssize_t)n > 0 && prefix_size > 0 && rem_fc > fc, "Error");
          OrderAccess::storestore();
          _bt.split_block((HeapWord*)fc, fc->size(), prefix_size);
          assert(fc->is_free(), "Error");
          //更新fc的size
          fc->set_size(prefix_size);
          if (rem >= IndexSetSize) {
            //rem较大,归还到Dictionary中
            returnChunkToDictionary(rem_fc);
            //更新计数器
            dictionary()->dict_census_update(rem, true /*split*/, true /*birth*/);
            rem_fc = NULL;
          }
          // Otherwise, return it to the small list below.
        }
      }
      //释放Dictionary对应的锁
      if (rem_fc != NULL) {
        //rem较小,不能归还到Dictionary中,则获取对应大小的FreeList的锁,归还到FreeList中
        MutexLockerEx x(_indexedFreeListParLocks[rem],
                        Mutex::_no_safepoint_check_flag);
        _bt.verify_not_unallocated((HeapWord*)rem_fc, rem_fc->size());
        _indexedFreeList[rem].return_chunk_at_head(rem_fc);
        //更新计数器
        smallSplitBirth(rem);
      }
      assert(n * word_sz == fc->size(),
        err_msg("Chunk size " SIZE_FORMAT " is not exactly splittable by "
        SIZE_FORMAT " sized chunks of size " SIZE_FORMAT,
        fc->size(), n, word_sz));
      return fc;
    }
     
    Mutex* parDictionaryAllocLock() const { return &_parDictionaryAllocLock; }
    

    5、addChunkAndRepairOffsetTable /removeFreeChunkFromFreeLists
    addChunkAndRepairOffsetTable用于往FreeList或者dictionary中添加新的内存块,removeFreeChunkFromFreeLists与之相反,用于从FreeList或者dictionary中移除旧的内存块,这两方法的实现比较简单,如下:

    void
    CompactibleFreeListSpace::addChunkAndRepairOffsetTable(HeapWord* chunk,
      size_t size, bool coalesced) {
      assert_locked();
      assert(chunk != NULL, "null chunk");
      //coalesced表示是否需要合并
      if (coalesced) {
        // repair BOT
        _bt.single_block(chunk, size);
      }
      addChunkToFreeLists(chunk, size);
    }
     
    void
    CompactibleFreeListSpace::addChunkToFreeLists(HeapWord* chunk,
                                                  size_t     size) {
      // check that the chunk does lie in this space!
      assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!");
      assert_locked();
      _bt.verify_single_block(chunk, size);
     
      FreeChunk* fc = (FreeChunk*) chunk;
      fc->set_size(size);
      debug_only(fc->mangleFreed(size));
      if (size < SmallForDictionary) {
        returnChunkToFreeList(fc);
      } else {
        returnChunkToDictionary(fc);
      }
    }
     
    void
    CompactibleFreeListSpace::removeFreeChunkFromFreeLists(FreeChunk* fc) {
      size_t size = fc->size();
      assert_locked();
      debug_only(verifyFreeLists());
      if (size < SmallForDictionary) {
        removeChunkFromIndexedFreeList(fc);
      } else {
        removeChunkFromDictionary(fc);
      }
      _bt.verify_single_block((HeapWord*)fc, size);
      debug_only(verifyFreeLists());
    }
     
    void
    CompactibleFreeListSpace::removeChunkFromDictionary(FreeChunk* fc) {
      size_t size = fc->size();
      assert_locked();
      assert(fc != NULL, "null chunk");
      _bt.verify_single_block((HeapWord*)fc, size);
      //从_dictionary中移除
      _dictionary->remove_chunk(fc);
      // adjust _unallocated_block upward, as necessary
      _bt.allocated((HeapWord*)fc, size);
    }
     
    void
    CompactibleFreeListSpace::removeChunkFromIndexedFreeList(FreeChunk* fc) {
      assert_locked();
      size_t size = fc->size();
      _bt.verify_single_block((HeapWord*)fc, size);
      //从_indexedFreeList中移除
      _indexedFreeList[size].remove_chunk(fc);
    }
    

    重点关注其调用链,如下:

    image.png image.png

    从调用链可知当从CompactibleFreeListSpace分配内存失败时会尝试扩展当前Generation,扩展成功后会往FreeList或者dictionary中添加一个新的空闲内存块。当执行垃圾回收后,如果需要将小的空闲内存块合并成一个大的,则需要先将小的内存块从FreeList或者dictionary中移除,然后再将合并后的大的空闲内存块添加到FreeList或者dictionary中。

    6、set_end
    set_end方法是CompactibleFreeListSpace的容量发生变更时调用的方法,如果是缩容则不做任何处理,如果是扩容则需要将扩容的内存块添加到FreeList或者dictionary中,并做必要的合并处理,更新相关计数器。其实现如下:

    void CompactibleFreeListSpace::set_end(HeapWord* value) {
      HeapWord* prevEnd = end();
      assert(prevEnd != value, "unnecessary set_end call");
      //BlockOffsetArrayUseUnallocatedBlock表示是否维护bt的_unallocated_block属性,默认值是false
      assert(prevEnd == NULL || !BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(),
            "New end is below unallocated block");
      _end = value;
      if (prevEnd != NULL) {
        //更新bt对应的内存区域
        _bt.resize(pointer_delta(value, bottom()));
        if (value <= prevEnd) {
          //如果缩容了则不做处理
          assert(!BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(),
                 "New end is below unallocated block");
        } else {
          // 如果扩容了则需要添加一个新的内存块
          size_t newFcSize = pointer_delta(value, prevEnd);
          //如果_adaptive_freelists为false,即不使用adaptive_freelists,该属性默认为true
          if (!_adaptive_freelists && _smallLinearAllocBlock._ptr == NULL) {
            // Mark the boundary of the new block in BOT
            _bt.mark_block(prevEnd, value);
            // put it all in the linAB
            if (ParallelGCThreads == 0) {
              _smallLinearAllocBlock._ptr = prevEnd;
              _smallLinearAllocBlock._word_size = newFcSize;
              repairLinearAllocBlock(&_smallLinearAllocBlock);
            } else { // ParallelGCThreads > 0
              MutexLockerEx x(parDictionaryAllocLock(),
                              Mutex::_no_safepoint_check_flag);
              _smallLinearAllocBlock._ptr = prevEnd;
              _smallLinearAllocBlock._word_size = newFcSize;
              repairLinearAllocBlock(&_smallLinearAllocBlock);
            }
          } else {
            // _adaptive_freelists为true时走到此路径,将新的扩容产生的内存块添加到FreeList或者dictionary中
            //并做必要的合并
            addChunkToFreeListsAtEndRecordingStats(prevEnd, newFcSize);
          }
        }
      }
    }
     
    //如果dictionary中最大的一个内存块是内存地址最大的一个内存块,即该内存块的终止地址就是CompactibleFreeListSpace的end地址,
    //则将其与新增的内存块合并,将合并后的内存块添加到FreeList或者dictionary中,否则只是添加新增的内存块
    void
    CompactibleFreeListSpace::addChunkToFreeListsAtEndRecordingStats(
      HeapWord* chunk, size_t     size) {
      // check that the chunk does lie in this space!
      assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!");
      
      Mutex* lock = NULL;
      if (ParallelGCThreads != 0) {
        lock = &_parDictionaryAllocLock;
      }
      FreeChunk* ec;
      {
        // 如果是并行GC则获取Dictionary属性的锁
        MutexLockerEx x(lock, Mutex::_no_safepoint_check_flag);
        //获取最大的一个内存块
        ec = dictionary()->find_largest_dict();  // get largest block
        if (ec != NULL && ec->end() == (uintptr_t*) chunk) {
          //如果ec跟chunk刚好是地址相邻的,则将ec从Dictionary中删除,然后与新的FreeChunk合并
          size_t old_size = ec->size();
          //更新coal_death计数器
          coalDeath(old_size);
          /从Dictionary中删除
          removeChunkFromDictionary(ec);
          //更新size
          size += old_size;
        } else {
          ec = (FreeChunk*)chunk;
        }
      }
      ec->set_size(size);
      debug_only(ec->mangleFreed(size));
      if (size < SmallForDictionary && ParallelGCThreads != 0) {
        //如果小于SmallForDictionary,则应添加到FreeList中,获取对应的锁
        lock = _indexedFreeListParLocks[size];
      }
      //获取锁,将新的FreeChunk添加到FreeList或者Dictionary中
      MutexLockerEx x(lock, Mutex::_no_safepoint_check_flag);
      addChunkAndRepairOffsetTable((HeapWord*)ec, size, true);
      //更新coal_birth计数器
      coalBirth(size);
    }
    

    其调用链如下:


    image.png

    从调用链可知当CompactibleFreeListSpace初始化的时候会调用set_end方法设置一个初始内存大小对应的end地址,然后就会往dictionary中放一个大的空闲内存块,当通过FreeList分配内存时,FreeList一开始是空的就会从dictionary中查找满足大小的内存块,将最初的那个大的内存块不断的做切分,用切分后的内存块填充FreeList,然后FreeList就可以执行内存分配了。当从FreeList或者dictionary中分配内存失败时就会扩容CompactibleFreeListSpace,扩容产生的内存块又会添加到FreeList或者dictionary中供下一次的内存分配。当扩容达到上限后就会触发垃圾回收,执行完垃圾回收后空闲的内存块会在满足一定条件时合并成一个大的内存块,如果不合并则原样放到原来的FreeList或者dictionary中,如此就形成了一个完整的循环。

    相关文章

      网友评论

          本文标题:CompactibleFreeListSpace 之 2

          本文链接:https://www.haomeiwen.com/subject/fjxdxdtx.html