HotSpot note (paet-2)

part 2

這個(gè)comment希望能分析一下GenCollectedHeap::do_collection這個(gè)函數(shù)的具體執(zhí)行流程,根據(jù)函數(shù)名字可以猜測(cè)該函數(shù)實(shí)現(xiàn)的功能就是做垃圾回收,下面是它的方法聲明(聲明和定義是有區(qū)別的,聲明僅僅是告訴別人有這樣一個(gè)函數(shù),而定義則是說這個(gè)函數(shù)具體實(shí)現(xiàn)了什么功能):

  // Helper function for two callbacks below.
  // Considers collection of the first max_level+1 generations.
  void do_collection(bool           full,
                     bool           clear_all_soft_refs,
                     size_t         size,
                     bool           is_tlab,
                     GenerationType max_generation);

參數(shù)full代表是否是FullGC,clear_all_soft_refs參數(shù)表示是否要回收sort reference,size參數(shù)需要多說明一點(diǎn),在一些情況下,GC發(fā)生是因?yàn)榘l(fā)送了"Allocate Fail",這個(gè)size就代表了申請(qǐng)分配的內(nèi)存大?。籭s_tlab表示是否使用 TLAB(線程分配Buffer,可以避免多線程在堆上并發(fā)申請(qǐng)內(nèi)存),max_generation參數(shù)表示最大的回收代,只有兩種類型,YoungGen或者OldGen;下面來仔細(xì)分析一下這個(gè)函數(shù)。

  • (1)、首先是做一些基本的校驗(yàn),比如是否在safe_point,是否是GC線程訪問該函數(shù),以及是否已經(jīng)有其他的線程觸發(fā)了GC,這些條件都需要滿足才能執(zhí)行接下來的代碼。

<img width="946" alt="2018-11-11 10 04 16" src="https://user-images.githubusercontent.com/16225796/48308185-65793780-e599-11e8-83d6-607af6a98722.png">

  • (2)、接下來需要做一些GC策略的生成,主要是判斷是否回收soft reference對(duì)象,是否收集Young或者Old區(qū)域等。complete表示是否收集整個(gè)堆,old_collects_young表示是否在收集老年代的同時(shí)收集新生代,也就是是否有必要收集新生代,JVM參數(shù)ScavengeBeforeFullGC控制是否在FullGC前做一次YoungGC,如果設(shè)置了該參數(shù),那在收集old區(qū)的時(shí)候就沒有必要再回收young區(qū)了;do_young_collection表示是否需要對(duì)young區(qū)域進(jìn)行垃圾收集,判斷標(biāo)準(zhǔn)就是young區(qū)域確實(shí)需要回收了,也就是進(jìn)行YoungGC,

<img width="1223" alt="2018-11-11 10 07 02" src="https://user-images.githubusercontent.com/16225796/48308258-6f039f00-e59b-11e8-8bac-435316592a08.png">

  • (3)、現(xiàn)在,知道該回收哪些區(qū)域了,那么接下來就去回收需要回收的區(qū)域,如果do_young_collection是true的,那么就執(zhí)行YoungGC,collect_generation函數(shù)是具體的執(zhí)行某個(gè)區(qū)域垃圾回收的入口,待會(huì)再來分析這個(gè)函數(shù)的具體流程;接著也判讀oldGen是否需要回收,如果需要的話也進(jìn)行回收。

<img width="1081" alt="2018-11-11 10 22 01" src="https://user-images.githubusercontent.com/16225796/48308286-33b5a000-e59c-11e8-914f-f54bad42683a.png">

<img width="1291" alt="2018-11-11 10 25 21" src="https://user-images.githubusercontent.com/16225796/48308297-868f5780-e59c-11e8-957c-822f4a25e7d6.png">

  • (4)、垃圾收集完成之后,需要計(jì)算各個(gè)分代的大小因?yàn)镚C之后堆可能會(huì)擴(kuò)展,所以需要重新計(jì)算一下各個(gè)分代的大小,重新計(jì)算大小通過調(diào)用函數(shù)compute_new_size實(shí)現(xiàn),該函數(shù)需要調(diào)整各個(gè)分代的各種指針,使得堆擴(kuò)展后各個(gè)分代依然可以正常工作。

下面,來分析上面幾個(gè)步驟中出現(xiàn)的一些關(guān)鍵函數(shù),首先是should_collect函數(shù),該函數(shù)用于判斷某一個(gè)分代是否需要做垃圾回收下面來看看該方法的細(xì)節(jié)

  // Returns "true" iff collect() should subsequently be called on this
  // this generation. See comment below.
  // This is a generic implementation which can be overridden.
  //
  // Note: in the current (1.4) implementation, when genCollectedHeap's
  // incremental_collection_will_fail flag is set, all allocations are
  // slow path (the only fast-path place to allocate is DefNew, which
  // will be full if the flag is set).
  // Thus, older generations which collect younger generations should
  // test this flag and collect if it is set.
  virtual bool should_collect(bool   full,
                              size_t word_size,
                              bool   is_tlab) {
    return (full || should_allocate(word_size, is_tlab));
  }

如果是FullGC,那么無(wú)論哪個(gè)分代都應(yīng)該被回收,如果不是FullGC,那么就使用should_allocate函數(shù)繼續(xù)判斷是否需要在該分代進(jìn)行收集,比如對(duì)于DefNew(Serial GC下新生代)分代來說,其具體實(shí)現(xiàn)就如下:

  // Allocation support
  virtual bool should_allocate(size_t word_size, bool is_tlab) {
    assert(UseTLAB || !is_tlab, "Should not allocate tlab");

    size_t overflow_limit    = (size_t)1 << (BitsPerSize_t - LogHeapWordSize);

    const bool non_zero      = word_size > 0;
    const bool overflows     = word_size >= overflow_limit;
    const bool check_too_big = _pretenure_size_threshold_words > 0;
    const bool not_too_big   = word_size < _pretenure_size_threshold_words;
    const bool size_ok       = is_tlab || !check_too_big || not_too_big;

    bool result = !overflows &&
                  non_zero   &&
                  size_ok;

    return result;
  }

接著一個(gè)重要的函數(shù)就是collect_generation,這個(gè)函數(shù)將回收給定的分代中的垃圾,主要看下面的這段代碼片段:

// Do collection work
  {
    // Note on ref discovery: For what appear to be historical reasons,
    // GCH enables and disabled (by enqueing) refs discovery.
    // In the future this should be moved into the generation's
    // collect method so that ref discovery and enqueueing concerns
    // are local to a generation. The collect method could return
    // an appropriate indication in the case that notification on
    // the ref lock was needed. This will make the treatment of
    // weak refs more uniform (and indeed remove such concerns
    // from GCH). XXX

    HandleMark hm;  // Discard invalid handles created during gc
    save_marks();   // save marks for all gens
    // We want to discover references, but not process them yet.
    // This mode is disabled in process_discovered_references if the
    // generation does some collection work, or in
    // enqueue_discovered_references if the generation returns
    // without doing any work.
    ReferenceProcessor* rp = gen->ref_processor();
    // If the discovery of ("weak") refs in this generation is
    // atomic wrt other collectors in this configuration, we
    // are guaranteed to have empty discovered ref lists.
    if (rp->discovery_is_atomic()) {
      rp->enable_discovery();
      rp->setup_policy(clear_soft_refs);
    } else {
      // collect() below will enable discovery as appropriate
    }
    gen->collect(full, clear_soft_refs, size, is_tlab);
    if (!rp->enqueuing_is_done()) {
      rp->enqueue_discovered_references();
    } else {
      rp->set_enqueuing_is_done(false);
    }
    rp->verify_no_references_recorded();
  }

接著看gen->collect函數(shù)調(diào)用,這里面就是做具體的垃圾收集工作,比如下面分析在DefNew分代中的gen->collect實(shí)現(xiàn)。

  • (1)、DefNew是Serial GC下的新生代,首先它要判斷是否有必要讓老年代來做這次GC,使用collection_attempt_is_safe函數(shù)來做這個(gè)判斷,也就是判斷出收集該區(qū)域是否是安全的,所謂安全的,就是DefNew分代收集了之后,old 區(qū)域是否可以完整的將這次Minor GC之后晉升的對(duì)象安置起來,如果不能的話,那DefNew就舉得自己做GC是不安全的,應(yīng)該讓老年代來做GC,這也是最合適的選擇,老年代會(huì)做一次規(guī)模宏大的GC,并且做一些內(nèi)存規(guī)整的工作,避免新生代中晉升上來的大對(duì)象無(wú)法找到連續(xù)的空間放置,當(dāng)然,老年代GC實(shí)現(xiàn)上幾乎都包含"整理"的階段,這也是為什么老年代發(fā)生GC耗時(shí)是新生代GC的10倍的原因之一,新生代使用copying算法,是一種非??焖俚氖占惴ǎ?dāng)然也得益于新生代中的對(duì)象壽命都比較短,不像老年代中的對(duì)象壽命較長(zhǎng),當(dāng)然,這也是分代的意義所在;

<img width="1083" alt="2018-11-11 11 06 01" src="https://user-images.githubusercontent.com/16225796/48308556-ef79ce00-e5a2-11e8-9a81-e3cd4839f8c7.png">

collection_attempt_is_safe函數(shù)的實(shí)現(xiàn)如下:

bool DefNewGeneration::collection_attempt_is_safe() {
  if (!to()->is_empty()) {
    log_trace(gc)(":: to is not empty ::");
    return false;
  }
  if (_old_gen == NULL) {
    GenCollectedHeap* gch = GenCollectedHeap::heap();
    _old_gen = gch->old_gen();
  }
  return _old_gen->promotion_attempt_is_safe(used());
}

正常來說,用區(qū)域內(nèi)兩個(gè)survivor中有一個(gè)區(qū)域總是空閑的,但是在某些情況下也會(huì)發(fā)生意外,使得兩個(gè)survivor都不為空,這種情況是有可能發(fā)生的,首先DefNew在進(jìn)行YoungGC之后,會(huì)將Eden + From中存活的對(duì)象拷貝到To中去,并且將一些符合晉升要求的對(duì)象拷貝到old區(qū)域中去,然后調(diào)換兩個(gè)survivor的角色,所以按理來說其中某個(gè)survivor區(qū)域總是空的,但是這是在YoungGC順利完成的情況,在發(fā)生"promotion failed"的時(shí)候就不會(huì)去清理From和To,這一點(diǎn)在后續(xù)會(huì)再次說明;但是肯定的是,如果To區(qū)域不為空,那么就說明前一次YoungGC并不是很順利,此時(shí)DefNew就舉得沒必要再冒險(xiǎn)去做一次可能沒啥用處的Minor GC,因?yàn)橛锌赡躆inor GC之后需要出發(fā)一次Full GC來解決某些難題,所以DefNew基于自己的歷史GC告訴Old去做一些較為徹底的GC工作時(shí)必要的;如果沒有發(fā)生"promotion fail"這種不愉快的事情,那么接下來就讓old區(qū)自己判斷是否允許本次Minor GC的發(fā)生,也就是_old_gen->promotion_attempt_is_safe的調(diào)用,下面來看看該函數(shù)的具體實(shí)現(xiàn);

bool TenuredGeneration::promotion_attempt_is_safe(size_t max_promotion_in_bytes) const {
  size_t available = max_contiguous_available();
  size_t av_promo  = (size_t)gc_stats()->avg_promoted()->padded_average();
  bool   res = (available >= av_promo) || (available >= max_promotion_in_bytes);

  log_trace(gc)("Tenured: promo attempt is%s safe: available(" SIZE_FORMAT ") %s av_promo(" SIZE_FORMAT "), max_promo(" SIZE_FORMAT ")",
    res? "":" not", available, res? ">=":"<", av_promo, max_promotion_in_bytes);

  return res;
}

老年代也會(huì)看歷史數(shù)據(jù),如果發(fā)現(xiàn)老年代的最大連續(xù)空間大小大于新生代歷史晉升的平均大小或者新生代中存活的對(duì)象,那么老年代就認(rèn)為本次Minor GC是安全的,沒必要做一次Full GC;當(dāng)然這是有一些冒險(xiǎn)的成分的,如果某一次minorGC發(fā)生之后符合晉升條件的對(duì)象大小遠(yuǎn)遠(yuǎn)大小評(píng)價(jià)晉升大小,而且這個(gè)時(shí)候老年代連續(xù)空間小于這些符合晉升的對(duì)象大小的時(shí)候,悲劇就發(fā)生了,也就是上面說到的"promotion fail",這個(gè)時(shí)候就要做一次Full GC。

  • (2)、接著關(guān)鍵的一個(gè)步驟就是進(jìn)行對(duì)象存活判斷,并且將存活的對(duì)象轉(zhuǎn)移到正確的位置,比如To區(qū)域或者old區(qū)域;

<img width="875" alt="2018-11-11 11 53 16" src="https://user-images.githubusercontent.com/16225796/48308898-8a28db80-e5a8-11e8-887e-1d16e41998fd.png">

FastEvacuateFollowersClosure是一個(gè)遞歸的過程,Closure后綴代表 它是一個(gè)回調(diào)操作,所謂遞歸,就是在判斷對(duì)象存活并且copying的工作是遞歸進(jìn)行的,首先找到root objects,然后根據(jù)root objects去標(biāo)記存活的對(duì)象,并且將它們轉(zhuǎn)移到合適的區(qū)域中去;gch->young_process_roots做的工作就是將root objects轉(zhuǎn)移到其他空間去的函數(shù):

void GenCollectedHeap::young_process_roots(StrongRootsScope* scope,
                                           OopsInGenClosure* root_closure,
                                           OopsInGenClosure* old_gen_closure,
                                           CLDClosure* cld_closure) {
  MarkingCodeBlobClosure mark_code_closure(root_closure, CodeBlobToOopClosure::FixRelocations);

  process_roots(scope, SO_ScavengeCodeCache, root_closure, root_closure,
                cld_closure, cld_closure, &mark_code_closure);
  process_string_table_roots(scope, root_closure);

  if (!_process_strong_tasks->is_task_claimed(GCH_PS_younger_gens)) {
    root_closure->reset_generation();
  }

  // When collection is parallel, all threads get to cooperate to do
  // old generation scanning.
  old_gen_closure->set_generation(_old_gen);
  rem_set()->younger_refs_iterate(_old_gen, old_gen_closure, scope->n_threads());
  old_gen_closure->reset_generation();

  _process_strong_tasks->all_tasks_completed(scope->n_threads());
}

這里面關(guān)鍵的函數(shù)是process_roots,該函數(shù)會(huì)對(duì)設(shè)置的各種Closure進(jìn)行回調(diào),比如FastScanClosure,具體的回調(diào)工作將在Closure的do_oop_work進(jìn)行:

// NOTE! Any changes made here should also be made
// in ScanClosure::do_oop_work()
template <class T> inline void FastScanClosure::do_oop_work(T* p) {
  T heap_oop = oopDesc::load_heap_oop(p);
  // Should we copy the obj?
  if (!oopDesc::is_null(heap_oop)) {
    oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
    if ((HeapWord*)obj < _boundary) {
      assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?");
      oop new_obj = obj->is_forwarded() ? obj->forwardee()
                                        : _g->copy_to_survivor_space(obj);
      oopDesc::encode_store_heap_oop_not_null(p, new_obj);
      if (is_scanning_a_klass()) {
        do_klass_barrier();
      } else if (_gc_barrier) {
        // Now call parent closure
        do_barrier(p);
      }
    }
  }
}

如果對(duì)象已經(jīng)被復(fù)制過了,那么就不用再?gòu)?fù)制一次了,否則調(diào)用copy_to_survivor_space將該對(duì)象復(fù)制到to區(qū)域中去,下面是copy_to_survivor_space函數(shù)的具體實(shí)現(xiàn):

oop DefNewGeneration::copy_to_survivor_space(oop old) {
  assert(is_in_reserved(old) && !old->is_forwarded(),
         "shouldn't be scavenging this oop");
  size_t s = old->size();
  oop obj = NULL;

  // Try allocating obj in to-space (unless too old)
  if (old->age() < tenuring_threshold()) {
    obj = (oop) to()->allocate_aligned(s);
  }

  // Otherwise try allocating obj tenured
  if (obj == NULL) {
    obj = _old_gen->promote(old, s);
    if (obj == NULL) {
      handle_promotion_failure(old);
      return old;
    }
  } else {
    // Prefetch beyond obj
    const intx interval = PrefetchCopyIntervalInBytes;
    Prefetch::write(obj, interval);

    // Copy obj
    Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)obj, s);

    // Increment age if obj still in new generation
    obj->incr_age();
    age_table()->add(obj, s);
  }

  // Done, insert forward pointer to obj in this header
  old->forward_to(obj);

  return obj;
}

這個(gè)函數(shù)的流程大概是這樣的:首先判斷對(duì)象是否達(dá)到了晉升到老年代的年齡閾值,如果到了,那么就要將對(duì)象拷貝到老年代中去,否則就要將對(duì)象拷貝到to區(qū)域中去,這里面也包括一個(gè)細(xì)節(jié),如果對(duì)象沒有達(dá)到晉升老年代的年齡閾值,但是無(wú)法拷貝到To區(qū)域中去,那么也試圖將對(duì)象晉升到老年代,也就是將對(duì)象提前晉升,晉升是有風(fēng)險(xiǎn)的,可能晉升失敗,那么就要通過調(diào)用handle_promotion_failure來處理晉升失敗的情況,如果對(duì)象成功拷貝到了To區(qū)域中來,那么就要將對(duì)象的年齡更新一下,最后,需要需要標(biāo)記對(duì)象已經(jīng)被轉(zhuǎn)移,如果可能,那么就把老的對(duì)象清空吧;下面來先來看看promote函數(shù),該函數(shù)用于將對(duì)象晉升到老年代:

// Ignores "ref" and calls allocate().
oop Generation::promote(oop obj, size_t obj_size) {
  assert(obj_size == (size_t)obj->size(), "bad obj_size passed in");

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

  HeapWord* result = allocate(obj_size, false);
  if (result != NULL) {
    Copy::aligned_disjoint_words((HeapWord*)obj, result, obj_size);
    return oop(result);
  } else {
    GenCollectedHeap* gch = GenCollectedHeap::heap();
    return gch->handle_failed_promotion(this, obj, obj_size);
  }
}

這個(gè)函數(shù)較為簡(jiǎn)單,首先通過allocate函數(shù)試圖在老年代申請(qǐng)一塊可以容納對(duì)象的內(nèi)存,如果成功了,那么就將對(duì)象復(fù)制到里面去,否則通過handle_failed_promotion函數(shù)來處理晉升失敗的情況,晉升失敗的前提下,handle_failed_promotion在handle_promotion_failure前執(zhí)行,看起來都是處理晉升失敗的情況,下面先看看handle_failed_promotion:

oop GenCollectedHeap::handle_failed_promotion(Generation* old_gen,
                                              oop obj,
                                              size_t obj_size) {
  guarantee(old_gen == _old_gen, "We only get here with an old generation");
  assert(obj_size == (size_t)obj->size(), "bad obj_size passed in");
  HeapWord* result = NULL;

  result = old_gen->expand_and_allocate(obj_size, false);

  if (result != NULL) {
    Copy::aligned_disjoint_words((HeapWord*)obj, result, obj_size);
  }
  return oop(result);
}

可以看到,oldGen將試圖去擴(kuò)展自己的堆空間來讓更多的新生代對(duì)象可以成功晉升,但是很多情況下,堆空間被設(shè)置為不可擴(kuò)展,這種情況下這個(gè)方法也就做了無(wú)用功,接著會(huì)調(diào)用handle_promotion_failure,調(diào)用handle_promotion_failure代表老年代也就明確告訴新生代無(wú)法將本次晉升的這個(gè)對(duì)象放置到老年代,來看看handle_promotion_failure會(huì)有什么對(duì)策:

void DefNewGeneration::handle_promotion_failure(oop old) {
  log_debug(gc, promotion)("Promotion failure size = %d) ", old->size());

  _promotion_failed = true;
  _promotion_failed_info.register_copy_failure(old->size());
  _preserved_marks_set.get()->push_if_necessary(old, old->mark());
  // forward to self
  old->forward_to(old);

  _promo_failure_scan_stack.push(old);

  if (!_promo_failure_drain_in_progress) {
    // prevent recursion in copy_to_survivor_space()
    _promo_failure_drain_in_progress = true;
    drain_promo_failure_scan_stack();
    _promo_failure_drain_in_progress = false;
  }
}

看起來DefNew還是比較樂觀的,既然老年代容納不了你,那么這個(gè)晉升的對(duì)象就還呆在新生代吧,說不定下次老年代發(fā)生GC就可以成功把它拷貝過去呢。這個(gè)時(shí)候_promotion_failed也被標(biāo)記物為了true,這個(gè)標(biāo)記之后會(huì)有用,發(fā)生"promotion fail"之后From區(qū)域可能存在一些對(duì)象沒有成功晉升到老年代,但是又不是垃圾,這個(gè)時(shí)候From和To區(qū)域都不為空了,這是個(gè)難題。

接著,是時(shí)候執(zhí)行遞歸標(biāo)記&復(fù)制的過程了,也就是evacuate_followers.do_void(),這個(gè)過程是非常復(fù)雜的,下面來稍微看看這個(gè)函數(shù):

void DefNewGeneration::FastEvacuateFollowersClosure::do_void() {
  do {
    _gch->oop_since_save_marks_iterate(GenCollectedHeap::YoungGen, _scan_cur_or_nonheap, _scan_older);
  } while (!_gch->no_allocs_since_save_marks());
  guarantee(_young_gen->promo_failure_scan_is_complete(), "Failed to finish scan");
}

不斷使用oop_since_save_marks_iterate來做遞歸遍歷的工作,結(jié)束條件是通過no_allocs_since_save_marks來決定的,下面是no_allocs_since_save_marks函數(shù)的具體實(shí)現(xiàn):

bool GenCollectedHeap::no_allocs_since_save_marks() {
  return _young_gen->no_allocs_since_save_marks() &&
         _old_gen->no_allocs_since_save_marks();
}

看名字應(yīng)該是說沒有分配發(fā)生了,比如看看DefNew的no_allocs_since_save_marks函數(shù)實(shí)現(xiàn):

bool DefNewGeneration::no_allocs_since_save_marks() {
  assert(eden()->saved_mark_at_top(), "Violated spec - alloc in eden");
  assert(from()->saved_mark_at_top(), "Violated spec - alloc in from");
  return to()->saved_mark_at_top();
}

top()指向To區(qū)域空閑空間的起點(diǎn),上面已經(jīng)說過的一個(gè)過程是將root objects先標(biāo)記并且拷貝到To區(qū)域或者老年代,這個(gè)時(shí)候To區(qū)域內(nèi)已經(jīng)存在的對(duì)象是存活的,需要遞歸遍歷這些對(duì)象引用的對(duì)象,然后也進(jìn)行拷貝工作,saved_mark_at_top就是判斷是否還在有對(duì)象唄拷貝到To區(qū)域中來,如果還有對(duì)象拷貝進(jìn)來,那么就說明GC還沒有完成,繼續(xù)循環(huán)執(zhí)行oop_since_save_marks_iterate,否則就可以停止了;下面來看看oop_since_save_marks_iterate函數(shù)的實(shí)現(xiàn):

#define ContigSpace_OOP_SINCE_SAVE_MARKS_DEFN(OopClosureType, nv_suffix)  \
                                                                          \
void ContiguousSpace::                                                    \
oop_since_save_marks_iterate##nv_suffix(OopClosureType* blk) {            \
  HeapWord* t;                                                            \
  HeapWord* p = saved_mark_word();                                        \
  assert(p != NULL, "expected saved mark");                               \
                                                                          \
  const intx interval = PrefetchScanIntervalInBytes;                      \
  do {                                                                    \
    t = top();                                                            \
    while (p < t) {                                                       \
      Prefetch::write(p, interval);                                       \
      debug_only(HeapWord* prev = p);                                     \
      oop m = oop(p);                                                     \
      p += m->oop_iterate_size(blk);                                      \
    }                                                                     \
  } while (t < top());                                                    \
                                                                          \
  set_saved_mark_word(p);                                                 \
}

ALL_SINCE_SAVE_MARKS_CLOSURES(ContigSpace_OOP_SINCE_SAVE_MARKS_DEFN)

在深入下去的部分就比較復(fù)雜了,不再做分析,但是需要注意的一點(diǎn)是,DefNew在將存活對(duì)象復(fù)制到To區(qū)域的時(shí)候,Eden + From區(qū)域的對(duì)象是否存活不僅僅會(huì)看是否被To區(qū)域的對(duì)象引用,還會(huì)看老年代是否存在跨代引用新生代的對(duì)象的情況,這種情況也需要將存活的對(duì)象轉(zhuǎn)到To或者老年代。

  • (3)、接下來需要對(duì)GC過程中發(fā)現(xiàn)的引用進(jìn)行一些處理,比如是否回收soft reference,以及堆weak reference的回收等工作;

<img width="975" alt="2018-11-11 1 03 35" src="https://user-images.githubusercontent.com/16225796/48309323-74b8af00-e5b2-11e8-90d3-14fc28213502.png">

  • (4)、到此GC工作大概已經(jīng)完成了,接下來需要做一些收尾工作,如果發(fā)現(xiàn)在Minor GC的過程中發(fā)生了"promotion fail",那么就要做特殊的處理,younger_refs_iterate會(huì)將那些晉升失敗的對(duì)象恢復(fù)回來,否則下一次發(fā)生Minor GC的時(shí)候會(huì)誤以為這些對(duì)象已經(jīng)被復(fù)制過了,但是他們確實(shí)沒有被轉(zhuǎn)移成功,這樣的話,這些對(duì)象可能一直留在新生代,無(wú)論經(jīng)歷多少次GC都無(wú)法發(fā)生轉(zhuǎn)移;

<img width="943" alt="2018-11-11 1 05 45" src="https://user-images.githubusercontent.com/16225796/48309362-7cc51e80-e5b3-11e8-9a8c-828a06f700c3.png">

<img width="912" alt="2018-11-11 1 05 59" src="https://user-images.githubusercontent.com/16225796/48309367-851d5980-e5b3-11e8-97bf-2f09a09e2b15.png">

無(wú)論如何,新生代發(fā)生了GC,經(jīng)過這次GC,需要轉(zhuǎn)換From和To兩個(gè)survivor的角色,swap_spaces函數(shù)實(shí)現(xiàn)了這個(gè)功能:

void DefNewGeneration::swap_spaces() {
  ContiguousSpace* s = from();
  _from_space        = to();
  _to_space          = s;
  eden()->set_next_compaction_space(from());
  // The to-space is normally empty before a compaction so need
  // not be considered.  The exception is during promotion
  // failure handling when to-space can contain live objects.
  from()->set_next_compaction_space(NULL);

  if (UsePerfData) {
    CSpaceCounters* c = _from_counters;
    _from_counters = _to_counters;
    _to_counters = c;
  }
}

這個(gè)函數(shù)較為簡(jiǎn)單,只是swap了一下From和To;再說一句,如果沒有發(fā)生"Promotion Fail",那么在Minor GC之后,需要將From和Eden清空,因?yàn)闆]有發(fā)生晉升失敗事件,就說明所以在新生代(Eden + From)存活的對(duì)象都安全的轉(zhuǎn)移到了To或者老年代,所以可以清空,但是發(fā)生晉升失敗意味著有部分存活的對(duì)象依然還留在原地等待,所以不能clear掉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容