jvm(13) full gc 源码 jdk17

full gc是g1正常的gc cycle回收无力的情况下进行的,相对来说比较简单
主要入口代码在 g1CollectedHeap.cpp
do_full_collection

bool G1CollectedHeap::do_full_collection(bool explicit_gc,
                                         bool clear_all_soft_refs,
                                         bool do_maximum_compaction) {
  assert_at_safepoint_on_vm_thread();
  //检查jni lock
  if (GCLocker::check_active_before_gc()) {
    // Full GC was not completed.
    return false;
  }
  //判断系统是否需要回收所有软连接,主要看空间紧急程度
  const bool do_clear_all_soft_refs = clear_all_soft_refs ||
      soft_ref_policy()->should_clear_all_soft_refs();

  G1FullCollector collector(this, explicit_gc, do_clear_all_soft_refs, do_maximum_compaction);
  GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true);
  //准备阶段,主要是打印日志,停止cycle,停止并发标记,关闭活跃区域等
  collector.prepare_collection();
  collector.collect();
  //清理标记状态,记录full gc结束日志等
  collector.complete_collection();

  // Full collection was successfully completed.
  return true;
}

full gc分为四个阶段,对应四个task,看对应的work方法即可
G1FullGCMarkTask,G1FullGCPrepareTask,G1FullGCAdjustTask,G1FullGCCompactTask

void G1FullCollector::collect() {
  //标记,<<深入理解jvm虚拟机>>书上没看到这种full gc标记过程
  phase1_mark_live_objects();
  verify_after_marking();

  // Don't add any more derived pointers during later phases
  //栈上的派生类指针表进行关闭
  deactivate_derived_pointers();
  //压缩准备,判断region空间是否需要压缩,存入一个队列compaction queue
  phase2_prepare_compaction();
  //指针调整,新region地址更新
  phase3_adjust_pointers();
  //压缩region,处理第二步的东西
  phase4_do_compaction();
}

phase1_mark_live_objects

使用到了gc线程来进行的标记,就是初始化g1heap出现的_worker,线程中再使用G1FullGCMarker的线程

void G1FullCollector::phase1_mark_live_objects() {
  // Recursively traverse all live objects and mark them.
  GCTraceTime(Info, gc, phases) info("Phase 1: Mark live objects", scope()->timer());

  {
    // Do the actual marking.
    //标记,不是并发的,在prepare阶段concurrent_cycle_abort关了
    G1FullGCMarkTask marking_task(this);
    //实际调用就是task的work方法
    run_task(&marking_task);
  }

  { //引用处理器ReferenceProcessor,一种是并发的_ref_processor_cm还有就是这里的_ref_processor_stw
    //stw的开启会关闭cm的,并且开启单单线程,full gc中发现任何引用都会记录进来
    uint old_active_mt_degree = reference_processor()->num_queues();
    reference_processor()->set_active_mt_degree(workers());
    GCTraceTime(Debug, gc, phases) debug("Phase 1: Reference Processing", scope()->timer());
    // Process reference objects found during marking.
    //标记过程中出现的引用处理
    ReferenceProcessorPhaseTimes pt(scope()->timer(), reference_processor()->max_num_queues());
    G1FullGCRefProcProxyTask task(*this, reference_processor()->max_num_queues());
    //处理引用,按照软强弱虚的顺序处理
    const ReferenceProcessorStats& stats = reference_processor()->process_discovered_references(task, pt);
    scope()->tracer()->report_gc_reference_stats(stats);
    pt.print_all_references();
    assert(marker(0)->oop_stack()->is_empty(), "Should be no oops on the stack");

    reference_processor()->set_active_mt_degree(old_active_mt_degree);
  }

  // Weak oops cleanup.
  // 对象只有一个弱引用的话就要被回收
  // 这里处理会开多个线程,将弱引用地址所指的对象循环查看,不是空的且不是存活的对象就释放引用
  {
    GCTraceTime(Debug, gc, phases) debug("Phase 1: Weak Processing", scope()->timer());
    WeakProcessor::weak_oops_do(_heap->workers(), &_is_alive, &do_nothing_cl, 1);
  }

  // Class unloading and cleanup.
  // classloader卸载class,要从符号表移除
  if (ClassUnloading) {
    GCTraceTime(Debug, gc, phases) debug("Phase 1: Class Unloading and Cleanup", scope()->timer());
    // Unload classes and purge the SystemDictionary.
    bool purged_class = SystemDictionary::do_unloading(scope()->timer());
    _heap->complete_cleaning(&_is_alive, purged_class);
  }

  scope()->tracer()->report_object_count_after_gc(&_is_alive);
}

多线程处理标记,GcRoots,注意这里不是说传统意义的多线程来处理,而是我们需要对每个线程进行处理

void G1FullGCMarkTask::work(uint worker_id) {
  Ticks start = Ticks::now();
  ResourceMark rm;
  G1FullGCMarker* marker = collector()->marker(worker_id);
  MarkingCodeBlobClosure code_closure(marker->mark_closure(), !CodeBlobToOopClosure::FixRelocations);
  //ClassUnloading默认为true
  if (ClassUnloading) {
  //处理gcRoots
    _root_processor.process_strong_roots(marker->mark_closure(),
                                         marker->cld_closure(),
                                         &code_closure);
  } else {
    _root_processor.process_all_roots(marker->mark_closure(),
                                      marker->cld_closure(),
                                      &code_closure);
  }

  // Mark stack is populated, now process and drain it.
  //之前遍历roots生成的stack进行抽取处理
  //通过mark word将存活的对象记录到_mark_stats_cache
  marker->complete_marking(collector()->oop_queue_set(), collector()->array_queue_set(), &_terminator);
  marker->flush_mark_stats_cache();

  // This is the point where the entire marking should have completed.
  assert(marker->oop_stack()->is_empty(), "Marking should have completed");
  assert(marker->objarray_stack()->is_empty(), "Array marking should have completed");
  log_task("Marking task", worker_id, start);
}

process_strong_roots标记gcRoots

void G1RootProcessor::process_strong_roots(OopClosure* oops,
                                           CLDClosure* clds,
                                           CodeBlobClosure* blobs) {
  StrongRootsClosures closures(oops, clds, blobs);
  //1.清除本地方法nmethod线程栈外的对象
  //2.重新映射对象
  //3.标记存活对象
  process_java_roots(&closures, NULL, 0);
  process_vm_roots(&closures, NULL, 0);

  // CodeCache is already processed in java roots
  // refProcessor is not needed since we are inside a safe point
  _process_strong_tasks.all_tasks_claimed(G1RP_PS_CodeCache_oops_do,
                                          G1RP_PS_refProcessor_oops_do);
}

先看StrongRootsClosures closures(oops, clds, blobs);
这里三个参数的类型为G1MarkAndPushClosure,CLDToOopClosure,MarkingCodeBlobClosure
对应获取方法strong_oops(),strong_clds(),strong_codeblobs()
对应的root为java引用,类加载器对象,方法对象
实际运行的就是这些closure

complete_marking 处理前一步从强引用,类加载器,方法区获得的gcRoots对象

void G1FullGCMarker::complete_marking(OopQueueSet* oop_stacks,
                                      ObjArrayTaskQueueSet* array_stacks,
                                      TaskTerminator* terminator) {
  do {
    //处理标记
    drain_stack();
    ObjArrayTask steal_array;
    //从其他worker线程取任务继续处理
    if (array_stacks->steal(_worker_id, steal_array)) {
      follow_array_chunk(objArrayOop(steal_array.obj()), steal_array.index());
    } else {
      oop steal_oop;
      if (oop_stacks->steal(_worker_id, steal_oop)) {
        follow_object(steal_oop);
      }
    }
  } while (!is_empty() || !terminator->offer_termination());
}
//处理
void G1FullGCMarker::drain_stack() {
  do {
    oop obj;
    //从之前gcroots标记的_oop_stack获取对象到obj
    while (pop_object(obj)) {
      assert(_bitmap->is_marked(obj), "must be marked");
      //标记成员,也就是可达对象,mark_push后再while循环,不可达的对象最后会被回收
      follow_object(obj);
    }
    // Process ObjArrays one at a time to avoid marking stack bloat.
    ObjArrayTask task;
    if (pop_objarray(task)) {
      follow_array_chunk(objArrayOop(task.obj()), task.index());
    }
  } while (!is_empty());
}

phase2_prepare_compaction

直接看G1FullGCPrepareTask核心代码

bool G1FullGCPrepareTask::G1CalculatePointersClosure::do_heap_region(HeapRegion* hr) {
  bool force_not_compacted = false;
  if (should_compact(hr)) {
  //首先通过hrm region管理器查看对应region是否已经pinned(归档或者是大对象),再检查region压缩的临界值
    assert(!hr->is_humongous(), "moving humongous objects not supported.");
    //到达临界值没有锁定的region放入队列
    prepare_for_compaction(hr);
  } else {
    // There is no need to iterate and forward objects in pinned regions ie.
    // prepare them for compaction. The adjust pointers phase will skip
    // work for them.
    assert(hr->containing_set() == nullptr, "already cleared by PrepareRegionsClosure");
    if (hr->is_humongous()) {
      oop obj = cast_to_oop(hr->humongous_start_region()->bottom());
      //大对象检查是否标记,不在标记的进行回收且放入回收队列
      if (!_bitmap->is_marked(obj)) {
        free_pinned_region<true>(hr);
      }
    } else if (hr->is_open_archive()) {
      bool is_empty = _collector->live_words(hr->hrm_index()) == 0;
      if (is_empty) {
        free_pinned_region<false>(hr);
      }
    } else if (hr->is_closed_archive()) {
      // nothing to do with closed archive region
    } else {
      //MarkSweepDeadRatio默认5
      assert(MarkSweepDeadRatio > 0,
             "only skip compaction for other regions when MarkSweepDeadRatio > 0");

      // Too many live objects; skip compacting it.
      _collector->update_from_compacting_to_skip_compacting(hr->hrm_index());
      //region如果是年轻代的,需要创建Block Offset Table
      if (hr->is_young()) {
        // G1 updates the BOT for old region contents incrementally, but young regions
        // lack BOT information for performance reasons.
        // Recreate BOT information of high live ratio young regions here to keep expected
        // performance during scanning their card tables in the collection pauses later.
        hr->update_bot();
      }
      log_trace(gc, phases)("Phase 2: skip compaction region index: %u, live words: " SIZE_FORMAT,
                            hr->hrm_index(), _collector->live_words(hr->hrm_index()));
    }
  }

  // Reset data structures not valid after Full GC.
  //清除rem_set与cardtable信息
  reset_region_metadata(hr);

  return false;
}

如果准备阶段发现!task.has_freed_regions()
regions不够用,就需要用到serial的gc,调用prepare_serial_compaction

phase3_adjust_pointers

G1FullGCAdjustTask

void G1FullGCAdjustTask::work(uint worker_id) {
  Ticks start = Ticks::now();
  ResourceMark rm;

  // Adjust preserved marks first since they are not balanced.
  G1FullGCMarker* marker = collector()->marker(worker_id);\
  //调整stack中的对象地址,前面的region已经回收了
  marker->preserved_stack()->adjust_during_full_gc();

  // Adjust the weak roots.
  if (!Atomic::cmpxchg(&_references_done, false, true)) {
    G1CollectedHeap::heap()->ref_processor_stw()->weak_oops_do(&_adjust);
  }

  AlwaysTrueClosure always_alive;
  _weak_proc_task.work(worker_id, &always_alive, &_adjust);

  CLDToOopClosure adjust_cld(&_adjust, ClassLoaderData::_claim_strong);
  CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations);
  //回到了第一个标记阶段
  _root_processor.process_all_roots(&_adjust, &adjust_cld, &adjust_code);

  // Now adjust pointers region by region
  //逐个region进行处理
  G1AdjustRegionClosure blk(collector(), worker_id);
  G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&blk, &_hrclaimer, worker_id);
  log_task("Adjust task", worker_id, start);
}

phase4_do_compaction

void G1FullGCCompactTask::work(uint worker_id) {
  Ticks start = Ticks::now();
  GrowableArray<HeapRegion*>* compaction_queue = collector()->compaction_point(worker_id)->regions();
  for (GrowableArrayIterator<HeapRegion*> it = compaction_queue->begin();
       it != compaction_queue->end();
       ++it) {
    //将标记对象取出后,移动到目标地址,随后清除next_mark_bitmap
    compact_region(*it);
  }
  //重新设置region是否需要压缩
  G1ResetSkipCompactingClosure hc(collector());
  G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&hc, &_claimer, worker_id);
  log_task("Compaction task", worker_id, start);
}

压缩就是复制,每一个对象存了新地址的

size_t G1FullGCCompactTask::G1CompactRegionClosure::apply(oop obj) {
  size_t size = obj->size();
  HeapWord* destination = cast_from_oop<HeapWord*>(obj->forwardee());
  if (destination == NULL) {
    // Object not moving
    return size;
  }

  // copy object and reinit its mark
  HeapWord* obj_addr = cast_from_oop<HeapWord*>(obj);
  assert(obj_addr != destination, "everything in this pass should be moving");
  Copy::aligned_conjoint_words(obj_addr, destination, size);
//初始化就是更新对象mark word
  cast_to_oop(destination)->init_mark();
  assert(cast_to_oop(destination)->klass() != NULL, "should have a class");

  return size;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352