HotSpot阅读(一) GC

不想每次都是背八股文,干脆直接看源码

concurrentMarkSweepGeneration.hpp文件

 bool isMarked(HeapWord* addr) const;
  bool par_isMarked(HeapWord* addr) const; // do not lock checks
  bool isUnmarked(HeapWord* addr) const;

上面这些应该是用来标记内存的

HeapWord指针,用来指向堆内存地址addr

cms的bitmap,位图,猜测是标记内存对象存活还是应该回收:

class CMSBitMap VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;

  HeapWord* _bmStartWord;   // base address of range covered by map
  size_t    _bmWordSize;    // map size (in #HeapWords covered)
  const int _shifter;       // shifts to convert HeapWord to bit position
  VirtualSpace _virtual_space; // underlying the bit map
  BitMap    _bm;            // the bit map itself
 public:
  Mutex* const _lock;       // mutex protecting _bm;

 public:

收集器的状态:

public:
  enum CollectorState { 收集器状态
    Resizing            = 0,
    Resetting           = 1,
    Idling              = 2,
    InitialMarking      = 3, 初始标记
    Marking             = 4,
    Precleaning         = 5,
    AbortablePreclean   = 6,
    FinalMarking        = 7, 最终标记
    Sweeping            = 8  清除
  };

这个方法看不太懂

public:
  CMSCollector(ConcurrentMarkSweepGeneration* cmsGen,
               CardTableRS*                   ct,
               ConcurrentMarkSweepPolicy*     cp);
  ConcurrentMarkSweepThread* cmsThread() { return _cmsThread; }

hpp文件主要是一些函数的声明,PushAndMarkClosure,这种跟遍历survivor有关,暂时看不懂

concurrentMarkSweepGeneration.cpp文件

初始化年代计数器

void ConcurrentMarkSweepGeneration::initialize_performance_counters() {

  const char* gen_name = "old";

  // Generation Counters - generation 1, 1 subspace
  _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space);

  _space_counters = new GSpaceCounters(gen_name, 0,
                                       _virtual_space.reserved_size(),
                                       this, _gen_counters);
}

Collector的一些参数:

CMSCollector::CMSCollector(ConcurrentMarkSweepGeneration* cmsGen,
                           CardTableRS*                   ct,
                           ConcurrentMarkSweepPolicy*     cp):
  _cmsGen(cmsGen),
  _ct(ct),
  _ref_processor(NULL),    // will be set later
  _conc_workers(NULL),     // may be set later
  _abort_preclean(false),
  _start_sampling(false),
  _between_prologue_and_epilogue(false),
  _markBitMap(0, Mutex::leaf + 1, "CMS_markBitMap_lock"),
  _modUnionTable((CardTableModRefBS::card_shift - LogHeapWordSize),
                 -1 /* lock-free */, "No_lock" /* dummy */),
  _modUnionClosure(&_modUnionTable),
  _modUnionClosurePar(&_modUnionTable),
  // Adjust my span to cover old (cms) gen
  _span(cmsGen->reserved()),
  // Construct the is_alive_closure with _span & markBitMap
  _is_alive_closure(_span, &_markBitMap),
  _restart_addr(NULL),
  _overflow_list(NULL),
  _stats(cmsGen),
  _eden_chunk_lock(new Mutex(Mutex::leaf + 1, "CMS_eden_chunk_lock", true)),
  _eden_chunk_array(NULL),     // may be set in ctor body
  _eden_chunk_capacity(0),     // -- ditto --
  _eden_chunk_index(0),        // -- ditto --
  _survivor_plab_array(NULL),  // -- ditto --
  _survivor_chunk_array(NULL), // -- ditto --
  _survivor_chunk_capacity(0), // -- ditto --
  _survivor_chunk_index(0),    // -- ditto --
  _ser_pmc_preclean_ovflw(0),
  _ser_kac_preclean_ovflw(0),
  _ser_pmc_remark_ovflw(0),
  _par_pmc_remark_ovflw(0),
  _ser_kac_ovflw(0),
  _par_kac_ovflw(0),

空间计数器和年代计数器叠加:

void ConcurrentMarkSweepGeneration::update_counters() {
  if (UsePerfData) {
    _space_counters->update_all();
    _gen_counters->update_all();
  }
}

HeapWordSize 应该是一个heapword的块大小

Verbose 中文意思冗长的,是一个VM Option的配置参数,比如-verbose:class,就会打印出加载的类信息

freeList jvm中空闲内存块存在freeList中,需要时分配freeList中的一块给某线程


新生代提升到老年代
oop ConcurrentMarkSweepGeneration::promote(oop obj, size_t obj_size) {
  assert(obj_size == (size_t)obj->size(), "bad obj_size passed in");
  // allocate, copy and if necessary update promoinfo --
  // delegate to underlying space.
  assert_lock_strong(freelistLock());

#ifndef PRODUCT
  if (Universe::heap()->promotion_should_fail()) {
    return NULL;
  }
#endif  // #ifndef PRODUCT

  oop res = _cmsSpace->promote(obj, obj_size);
  if (res == NULL) { 如果提升到老年代失败,说明老年代空间不足
    // expand and retry
    size_t s = _cmsSpace->expansionSpaceRequired(obj_size);  // HeapWords
    expand(s*HeapWordSize, MinHeapDeltaBytes,
      CMSExpansionCause::_satisfy_promotion);  老年代扩容
    // Since there's currently no next generation, we don't try to promote
    // into a more senior generation.
    assert(next_gen() == NULL, "assumption, based upon which no attempt "
                               "is made to pass on a possibly failing "
                               "promotion to next generation");
    res = _cmsSpace->promote(obj, obj_size);  再次尝试扩容
  }

minor gc到达指定次数,进行full gc


void CMSCollector::request_full_gc(unsigned int full_gc_count, GCCause::Cause cause) {
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  unsigned int gc_count = gch->total_full_collections();
  if (gc_count == full_gc_count) {
    MutexLockerEx y(CGC_lock, Mutex::_no_safepoint_check_flag);
    _full_gc_requested = true;
    _full_gc_cause = cause;
    CGC_lock->notify();   // nudge CMS thread
  } else {
    assert(gc_count > full_gc_count, "Error: causal loop");
  }
}

CMS GC 共分为 Background 和 Foreground 两种模式,前者就是我们常规理解中的并发收集,可以不影响正常的业务线程运行,但 Foreground Collector 却有很大的差异,他会进行一次压缩式 GC。

Compaction 压缩,所以为啥要压缩

做标记清除
void CMSCollector::do_mark_sweep_work(bool clear_all_soft_refs,
  CollectorState first_state, bool should_start_over) {
  if (PrintGC && Verbose) {
    gclog_or_tty->print_cr("Pass concurrent collection to foreground "
      "collector with count %d",
      _full_gcs_since_conc_gc);
  }
  switch (_collectorState) {
    case Idling:
      if (first_state == Idling || should_start_over) {
        // The background GC was not active, or should
        // restarted from scratch;  start the cycle.
        _collectorState = InitialMarking;
      }
      // If first_state was not Idling, then a background GC
      // was in progress and has now finished.  No need to do it
      // again.  Leave the state as Idling.
      break;
    case Precleaning:
      // In the foreground case don't do the precleaning since
      // it is not done concurrently and there is extra work
      // required.
      _collectorState = FinalMarking;
  }
  #foreground模式进行收集
  collect_in_foreground(clear_all_soft_refs, GenCollectedHeap::heap()->gc_cause());

  // For a mark-sweep, compute_new_size() will be called
  // in the heap's do_collection() method.
}

UseAdaptiveSizePolicy: 如果开启 AdaptiveSizePolicy,则每次 GC 后会重新计算 Eden、From 和 To 区的大小,计算依据是 GC 过程中统计的 GC 时间、吞吐量、内存占用量

ConcurrentMarkSweepThread 这是个啥?

gc_prologue

klass 又是个啥?

bool CMSCollector::have_cms_token() 是个啥又

class VerifyMarkedClosure: public BitMapClosure {
  CMSBitMap* _marks;
  bool       _failed;

update_should_unload_classes  如果old区内存太满,则卸载类

TLAB又是个啥

Thread Local Allocation Buffer,每个线程都有一个TLAB,所有的TLAB存在alloc_buffers[thread_count]这个buffer里面。

其实还有PLAB( promotion local allocation buffer)。每一个线程在survival space和old space中都一个PLAB。在提升的时候,可以避免多线程的竞争,从而提升效率。

更新容量以及年代计数器
bool ConcurrentMarkSweepGeneration::grow_by(size_t bytes) {
  assert_locked_or_safepoint(Heap_lock);
  bool result = _virtual_space.expand_by(bytes);
  if (result) {
    size_t new_word_size =
      heap_word_size(_virtual_space.committed_size());


增长容量到指定大小
bool ConcurrentMarkSweepGeneration::grow_to_reserved() {
  assert_locked_or_safepoint(Heap_lock);
  bool success = true;
  const size_t remaining_bytes = _virtual_space.uncommitted_size();
  if (remaining_bytes > 0) {
    success = grow_by(remaining_bytes);
    DEBUG_ONLY(if (!success) warning("grow to reserved failed");)
  }
  return success;
}

tenuredGeneration.cpp(看完)

bool TenuredGeneration::should_collect(bool  full,
                                       size_t size,
                                       bool   is_tlab) 
是否该收集,在空间不足,或者新年代晋升得到老年代内存不足时,进行收集

收集:


void TenuredGeneration::collect(bool   full,
                                bool   clear_all_soft_refs,
                                size_t size,
                                bool   is_tlab) {
  retire_alloc_buffers_before_full_gc();
  OneContigSpaceCardGeneration::collect(full, clear_all_soft_refs,
                                        size, is_tlab);
}

而OneContigSpaceCardGeneration的collect定义在tenuredGeneration.hpp文件:

class TenuredGeneration: public OneContigSpaceCardGeneration {
  friend class VMStructs;
 protected:

#if INCLUDE_ALL_GCS
  // To support parallel promotion: an array of parallel allocation
  // buffers, one per thread, initially NULL.
  ParGCAllocBufferWithBOT** _alloc_buffers;
#endif // INCLUDE_ALL_GCS

  // Retire all alloc buffers before a full GC, so that they will be
  // re-allocated at the start of the next young GC.
  void retire_alloc_buffers_before_full_gc();

  GenerationCounters*   _gen_counters;
  CSpaceCounters*       _space_counters;

par_promote,是promote的多线程版,完成新生代到老年代的提升

oop TenuredGeneration::par_promote(int thread_num,
                                   oop old, markOop m, size_t word_sz) {
其中markOOP就是传说中的mark word

parNewGeneration.cpp

overflow_stack,感觉是线程的栈


void ParScanThreadState::push_on_overflow_stack(oop p) {
  assert(ParGCUseLocalOverflow, "Else should not call");
  overflow_stack()->push(p);
  assert(young_gen()->overflow_list() == NULL, "Error");
}

adjoiningGenerations.hpp

这个主要是定义young_gen和old_gen的

class AdjoiningGenerations : public CHeapObj<mtGC> {
  friend class VMStructs;
 private:
  // The young generation and old generation, respectively
  PSYoungGen* _young_gen;
  PSOldGen* _old_gen;

  // The spaces used by the two generations.
  AdjoiningVirtualSpaces _virtual_spaces;

 psYoungGen.hpp

传说中的_eden区,from 区和to区

class PSYoungGen : public CHeapObj<mtGC> {
  friend class VMStructs;
  friend class ParallelScavengeHeap;
  friend class AdjoiningGenerations;

 protected:
  MemRegion       _reserved;
  PSVirtualSpace* _virtual_space;

  // Spaces
  MutableSpace* _eden_space;
  MutableSpace* _from_space;
  MutableSpace* _to_space;

psYoungGen.cpp

为eden space和from space,to space分配内存空间,注意有个alignment,也就是分配的空间都是对齐的


void PSYoungGen::initialize_work() {

  _reserved = MemRegion((HeapWord*)virtual_space()->low_boundary(),
                        (HeapWord*)virtual_space()->high_boundary());

  MemRegion cmr((HeapWord*)virtual_space()->low(),
                (HeapWord*)virtual_space()->high());
  Universe::heap()->barrier_set()->resize_covered_region(cmr);

  if (ZapUnusedHeapArea) {
    // Mangle newly committed space immediately because it
    // can be done here more simply that after the new
    // spaces have been computed.
    SpaceMangler::mangle_region(cmr);
  }

  if (UseNUMA) {
    _eden_space = new MutableNUMASpace(virtual_space()->alignment());
  } else {
    _eden_space = new MutableSpace(virtual_space()->alignment());
  }
  _from_space = new MutableSpace(virtual_space()->alignment());
  _to_space   = new MutableSpace(virtual_space()->alignment());

...

_eden_mark_sweep =
      new PSMarkSweepDecorator(_eden_space, NULL, MarkSweepDeadRatio);

...


_gen_counters = new PSGenerationCounters("new", 0, 3, _virtual_space);
其中new代表新生代, 0是代数,3是spaces

_eden_counters = new SpaceCounters("eden", 0, max_eden_size, _eden_space,
                                     _gen_counters);
_from_counters = new SpaceCounters("s0", 1, max_survivor_size, _from_space,
                                     _gen_counters);
_to_counters = new SpaceCounters("s1", 2, max_survivor_size, _to_space,
                                   _gen_counters);
这就是传说中的s0,s1,eden,对应的内存计数器

然后看eden,from,to的地址分配,可知他们是首尾相连的

void PSYoungGen::set_space_boundaries(size_t eden_size, size_t survivor_size) {
  assert(eden_size < virtual_space()->committed_size(), "just checking");
  assert(eden_size > 0  && survivor_size > 0, "just checking");

  // Initial layout is Eden, to, from. After swapping survivor spaces,
  // that leaves us with Eden, from, to, which is step one in our two
  // step resize-with-live-data procedure.
  char *eden_start = virtual_space()->low();
  char *to_start   = eden_start + eden_size;
  char *from_start = to_start   + survivor_size;
  char *from_end   = from_start + survivor_size;

下面有MemRegion的分配:


  MemRegion eden_mr((HeapWord*)eden_start, (HeapWord*)to_start);
  MemRegion to_mr  ((HeapWord*)to_start, (HeapWord*)from_start);
  MemRegion from_mr((HeapWord*)from_start, (HeapWord*)from_end);

PSMarkSweepDecorator 又是个啥,wocao

对young区的大小进行重新调整,可能是expand,也可能shrink:

bool PSYoungGen::resize_generation(size_t eden_size, size_t survivor_size) {
  const size_t alignment = virtual_space()->alignment();
  size_t orig_size = virtual_space()->committed_size();
  bool size_changed = false;

  // There used to be this guarantee there.
  // guarantee ((eden_size + 2*survivor_size)  <= _max_gen_size, "incorrect input arguments");
  // Code below forces this requirement.  In addition the desired eden
  // size and disired survivor sizes are desired goals and may
  // exceed the total generation size.

  assert(min_gen_size() <= orig_size && orig_size <= max_size(), "just checking");

  // Adjust new generation size
  const size_t eden_plus_survivors =
          align_size_up(eden_size + 2 * survivor_size, alignment);
  size_t desired_size = MAX2(MIN2(eden_plus_survivors, max_size()),

交换from和to,不过from_mark_sweep又是啥?

void PSYoungGen::swap_spaces() {
  MutableSpace* s    = from_space();
  _from_space        = to_space();
  _to_space          = s;

  // Now update the decorators.
  PSMarkSweepDecorator* md = from_mark_sweep();
  _from_mark_sweep           = to_mark_sweep();
  _to_mark_sweep             = md;

  assert(from_mark_sweep()->space() == from_space(), "Sanity");
  assert(to_mark_sweep()->space() == to_space(), "Sanity");
}

所谓mangle实际就是把未使用的内存区域填充为一个特定的值:

// Simply mangle the MemRegion mr.
void SpaceMangler::mangle_region(MemRegion mr) {
  assert(ZapUnusedHeapArea, "Mangling should not be in use");
#ifdef ASSERT
  if(TraceZapUnusedHeapArea) {
    gclog_or_tty->print("Mangling [0x%x to 0x%x)", mr.start(), mr.end());
  }
  Copy::fill_to_words(mr.start(), mr.word_size(), badHeapWord);
  if(TraceZapUnusedHeapArea) {
    gclog_or_tty->print_cr(" done");
  }
#endif
}

上述代码的Copy::fill_to_words其实调用了:

static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) {
#ifdef _LP64
  julong* to = (julong*) tohw;
  julong  v  = ((julong) value << 32) | value;
#else
  juint* to = (juint*) tohw;
  juint  v  = value;
#endif // _LP64

  while (count-- > 0) {
    *to++ = v;
  }

也就是按序,把内存每个变量赋值为指定value

const juint    badHeapWordVal   = 0xBAADBABE;  
const juint   max_juint   = (juint)-1;   // 0xFFFFFFFF largest juint

psVirtualspace.hpp

inline void PSVirtualSpace::set_reserved(ReservedSpace rs) {
  set_reserved(rs.base(), rs.base() + rs.size(), rs.special());
}

inline void PSVirtualSpace::set_committed(char* low_addr, char* high_addr) {
  _committed_low_addr = low_addr;
  _committed_high_addr = high_addr;
}

reserved

reserved memory 是指JVM 通过mmaped PROT_NONE 申请的虚拟地址空间,在页表中已经存在了记录(entries),其实就是reserved(预定)的大小

在堆内存下,就是xmx值,jvm申请的最大保留内存。

committed

committed memory 是JVM向操做系统实际分配的内存(malloc/mmap),mmaped PROT_READ | PROT_WRITE,相当于程序实际申请的可用内存。

在堆内存下,当xms没有扩容时就是xms值,最小堆内存,扩容后就是扩容后的值,heap committed memory,。

注意,committed申请的内存并不是说直接占用了物理内存,由于操作系统的内存管理是惰性的,对于已申请的内存虽然会分配地址空间,但并不会直接占用物理内存,真正使用的时候才会映射到实际的物理内存。

uncommited

reserved = committed + uncommitted

psVirtualspace.cpp

这个类应该是用来分配页表(虚拟空间的),下面的代码释放内存:


void PSVirtualSpace::release() {
  DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this));
  // This may not release memory it didn't reserve.
  // Use rs.release() to release the underlying memory instead.
  _reserved_low_addr = _reserved_high_addr = NULL;
  _committed_low_addr = _committed_high_addr = NULL;
  _special = false;
}

下面的expand_by,表示内存的扩容,其实是增加commited memory,shirink_by也是减少commited memory


bool PSVirtualSpace::expand_by(size_t bytes) {
  assert(is_aligned(bytes), "arg not aligned");
  DEBUG_ONLY(PSVirtualSpaceVerifier this_verifier(this));

  if (uncommitted_size() < bytes) {
    return false;
  }

  char* const base_addr = committed_high_addr();
  bool result = special() ||
         os::commit_memory(base_addr, bytes, alignment(), !ExecMem);
  if (result) {
    _committed_high_addr += bytes;
  }

  return result;
}

其中 os::commit_memory(base_addr, bytes, alignment(), !ExecMem); 用来向操作系统请求分配内存,uncommit_memory在shrink_by调用,用来通知操作系统释放内存

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值