G1垃圾回收 - 3

2023-02-05  本文已影响0人  程序员札记

我们都知道当新生代剩下的空间不够分配会触发GC垃圾回收,新生代的GC是对部分内存进行垃圾回收,GC时间比较少,分区化的G1堆针对新生代的收集的内存也是不固定的。首先我们明白在进行YGC的时候会进行STW。然后会选择需要收集的CSet,针对新生代而言就是整个新生代分区。然后加入收集任务中,去并行处理引用。引用关系搜索完毕之后,就是进行对象引用回收,处理对象晋升,晋升失败的还原对象头,尝试扩展内存等。G1-YGC工作流程如下

do_collection_pause_at_safepoint

直接进入CollectedHeap.cpp#evacuate_collection_set方法一探其究。下图为并行清理CSet方法的工作流程

[图片上传中...(image-9d4305-1675644518441-3)]

  1. 使用G1RootProcessor类去执行根扫描,扫描直接强引用。主要是JVM根和Java根。使用G1ParCopyHelper把对象复制。

    • Java根

      • 类加载器

        深度遍历当前类的加载的所有存活的Klass对象,找到之后复制到Survivor区或者晋升老年代。

      • 线程栈

        处理Java线程栈和本地方法栈中找,通过StackFrameStream的next执行飞到Sender,从而得到调用者,进而其找到关联的活跃堆内对象,将其复制到Survivor区或者晋升老年代。

      知道了G1RootProcessor类会从上述的两个大方向上去找活跃对象,那么直接看代码,g1RootProcessor.cpp#evacuate_roots

      • void G1RootProcessor::process_java_roots(OopClosure* strong_roots,
        CLDClosure* thread_stack_clds,
        CLDClosure* strong_clds,
        CLDClosure* weak_clds,
        CodeBlobClosure* strong_code,
        G1GCPhaseTimes* phase_times,
        uint worker_i) {
        assert(thread_stack_clds == NULL || weak_clds == NULL, "There is overlap between those, only one may be set");
        // Iterating over the CLDG and the Threads are done early to allow us to
        // first process the strong CLDs and nmethods and then, after a barrier,
        // let the thread process the weak CLDs and nmethods.
        {
        G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::CLDGRoots, worker_i);
        if (!_process_strong_tasks->is_task_claimed(G1RP_PS_ClassLoaderDataGraph_oops_do)) {
        ClassLoaderDataGraph::roots_cld_do(strong_clds, weak_clds);
        }
        }
        
        {
        G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::ThreadRoots, worker_i);
        Threads::possibly_parallel_oops_do(strong_roots, thread_stack_clds, strong_code);
        }
        }
        
        void ClassLoaderDataGraph::roots_cld_do(CLDClosure* strong, CLDClosure* weak) {
        for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->_next) {
        CLDClosure* closure = cld->keep_alive() ? strong : weak;
        if (closure != NULL) {
        closure->do_cld(cld);
        }
        }
        }
        
        void ClassLoaderData::oops_do(OopClosure* f, KlassClosure* klass_closure, bool must_claim) {
        if (must_claim && !claim()) {
        return;
        }
        
        f->do_oop(&_class_loader);
        _dependencies.oops_do(f);
        _handles->oops_do(f);
        if (klass_closure != NULL) {
        classes_do(klass_closure);
        }
        }
        void ClassLoaderData::classes_do(KlassClosure* klass_closure) {
        for (Klass* k = _klasses; k != NULL; k = k->next_link()) {
        klass_closure->do_klass(k);
        assert(k != k->next_link(), "no loops!");
        }
        }
        

      最终发现调用的G1KlassScanClosure中的do_klass

      • class G1KlassScanClosure : public KlassClosure {
        G1ParCopyHelper* _closure;
        bool _process_only_dirty;
        int _count;
        public:
        G1KlassScanClosure(G1ParCopyHelper* closure, bool process_only_dirty)
        : _process_only_dirty(process_only_dirty), _closure(closure), _count(0) {}
        void do_klass(Klass* klass) {
        if (!_process_only_dirty || klass->has_modified_oops()) {
        klass->clear_modified_oops();
        _closure->set_scanned_klass(klass);
        klass->oops_do(_closure);
        _closure->set_scanned_klass(NULL);
        }
        _count++;
        }
        };
        

      主要执行klass->oops_do(_closure);,这个f为G1ParCopyHelper的对象,所以最终调用的g1CollectedHeap.cpp@G1ParCopyClosure#do_oop_workG1ParCopyHelperdo_oop最终调用do_oop_work来把活跃对象复制到新分区。

      针对线程的处理则是在thread.cpp#possibly_parallel_oops_doThreads::possibly_parallel_oops_do(strong_roots, thread_stack_clds, strong_code);实际调用JavaThread::oops_do遍历栈桢

      • void Thread::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) {
        active_handles()->oops_do(f);
        // Do oop for ThreadShadow
        f->do_oop((oop*)&_pending_exception);
        handle_area()->oops_do(f);
        }
        void JavaThread::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) {
        Thread::oops_do(f, cld_f, cf);
        assert( (!has_last_Java_frame() && java_call_counter() == 0) ||
        (has_last_Java_frame() && java_call_counter() > 0), "wrong java_sp info!");
        
        if (has_last_Java_frame()) {
        RememberProcessedThread rpt(this);
        if (_privileged_stack_top != NULL) {
        _privileged_stack_top->oops_do(f);
        }
        if (_array_for_gc != NULL) {
        for (int index = 0; index < _array_for_gc->length(); index++) {
        f->do_oop(_array_for_gc->adr_at(index));
        }
        }
        for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) {
        chunk->oops_do(f);
        }
        for(StackFrameStream fst(this); !fst.is_done(); fst.next()) {
        fst.current()->oops_do(f, cld_f, cf, fst.register_map());
        }
        }
        set_callee_target(NULL);
        assert(vframe_array_head() == NULL, "deopt in progress at a safepoint!");
        GrowableArray* list = deferred_locals();
        if (list != NULL) {
        for (int i = 0; i < list->length(); i++) {
        list->at(i)->oops_do(f);
        }
        }
        f->do_oop((oop*) &_threadObj);
        f->do_oop((oop*) &_vm_result);
        f->do_oop((oop*) &_exception_oop);
        f->do_oop((oop*) &_pending_async_exception);
        
        if (jvmti_thread_state() != NULL) {
        jvmti_thread_state()->oops_do(f);
        }
        }
        

      从JNI本地代码栈和JVM内部方法栈中找活跃对象,从java栈中找,遍历Monitor块,遍历jvmti(JVM Tool Interface)这里主要使用是JavaAgent。最后执行G1ParCopyHelperdo_oop最终调用do_oop_work来把活跃对象复制到新分区。

    • JVM根

      一些全局JVM对象,如Universe,JNIHandles,SystemDictionary,StringTable等等

      void G1RootProcessor::process_vm_roots(OopClosure* strong_roots,
                                             OopClosure* weak_roots,
                                             G1GCPhaseTimes* phase_times,
                                             uint worker_i) {
      {
          G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::UniverseRoots, worker_i);
          if (!_process_strong_tasks->is_task_claimed(G1RP_PS_Universe_oops_do)) {
            Universe::oops_do(strong_roots);
          }
        }
       ....
       void Universe::oops_do(OopClosure* f, bool do_all) {
      
        f->do_oop((oop*) &_int_mirror);
        f->do_oop((oop*) &_float_mirror);
        f->do_oop((oop*) &_double_mirror);
       ........
      }
      

      针对JVM根 同样也是调用的G1ParCopyHelperdo_oop只不过对JVM根而言则是各种全局对象。例如Univers

    g1CollectedHeap.cpp@G1ParCopyClosure#do_oop_work工作流程如下
    [图片上传中...(image-ee39c-1675644518439-2)]

    执行对象复制复制的操作在G1ParScanThreadState#copy_to_survivor_space方法中。具体处理如下
    [图片上传中...(image-eb176-1675644518439-1)]

  2. 处理RSet

  1. 对象复制
上一篇 下一篇

猜你喜欢

热点阅读