美文网首页
System.gc源码分析

System.gc源码分析

作者: allanYan | 来源:发表于2017-12-18 20:59 被阅读0次

    前言

    最近项目中遇到定期(一个小时)发生Full gc的情况,经过排查发现是sun.misc.GC的Daemon线程会定时调用Sysgem.gc,根源是项目中用到了JMX导致的;
    最终的方案选择的是启用ExplicitGCInvokesConcurrent和合理调大sun.rmi.dgc.client.gcInterval参数;

    那么为什么启用ExplicitGCInvokesConcurrent会减缓Full GC的停顿时间呢?下面的文章将进行探讨。

    System.gc简介

    在Java层面,System.gc调用的是Runtime.getRuntime().gc():

    
    JNIEXPORT void JNICALL
    Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)
    {
        JVM_GC();
    }
    

    在JVM层面,Runtime.getRuntime().gc()调用的是JVM_GC(见jvm.cpp):

    JVM_ENTRY_NO_ENV(void, JVM_GC(void))
      JVMWrapper("JVM_GC");
      if (!DisableExplicitGC) {
        Universe::heap()->collect(GCCause::_java_lang_system_gc);
      }
    JVM_END
    

    可见,如果打开了DisableExplicitGC开关,System.gc实际上什么都不做;

    collect方法分析

    Universe::heap()->collect的具体实现在GenCollectedHeap::collect方法:

    void GenCollectedHeap::collect(GCCause::Cause cause) {
      if (should_do_concurrent_full_gc(cause)) {
    #if INCLUDE_ALL_GCS
        // mostly concurrent full collection
        collect_mostly_concurrent(cause);
    #else  // INCLUDE_ALL_GCS
        ShouldNotReachHere();
    #endif // INCLUDE_ALL_GCS
      } else {
    #ifdef ASSERT
        if (cause == GCCause::_scavenge_alot) {
          // minor collection only
          collect(cause, 0);
        } else {
          // Stop-the-world full collection
          collect(cause, n_gens() - 1);
        }
    #else
        // Stop-the-world full collection
        collect(cause, n_gens() - 1);
    #endif
      }
    }
    
    bool GenCollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
      return UseConcMarkSweepGC &&
             ((cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) ||
              (cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent));
    }
    

    说明:

    1. 如果是采用CMS垃圾收集器,且满足如下的任何一个条件,则走并行收集的collect_mostly_concurrent流程:
    • GC原因是System.gc且开启了ExplicitGCInvokesConcurrent选项;
    • GC原因是GCLocker且开启了GCLockerInvokesConcurrent选项;
    1. 否则走stop the world的collect收集流程;

    collect_mostly_concurrent

    void GenCollectedHeap::collect_mostly_concurrent(GCCause::Cause cause) {
      assert(!Heap_lock->owned_by_self(), "Should not own Heap_lock");
    
      MutexLocker ml(Heap_lock);
      // Read the GC counts while holding the Heap_lock
      unsigned int full_gc_count_before = total_full_collections();
      unsigned int gc_count_before      = total_collections();
      {
        MutexUnlocker mu(Heap_lock);
        VM_GenCollectFullConcurrent op(gc_count_before, full_gc_count_before, cause);
        VMThread::execute(&op);
      }
    }
    

    collect_mostly_concurrent需要先获取堆锁,然后通过VMThread执行VM_GenCollectFullConcurrent;

    collect

    
    void GenCollectedHeap::collect_locked(GCCause::Cause cause, int max_level) {
      // Read the GC count while holding the Heap_lock
      unsigned int gc_count_before      = total_collections();
      unsigned int full_gc_count_before = total_full_collections();
      {
        MutexUnlocker mu(Heap_lock);  // give up heap lock, execute gets it back
        VM_GenCollectFull op(gc_count_before, full_gc_count_before,
                             cause, max_level);
        VMThread::execute(&op);
      }
    }
    

    collect最终调用的是collect_locked方法,通过VMThread执行VM_GenCollectFull

    总结:对于System.gc,开启ExplicitGCInvokesConcurrent之后走的是VMThread的VM_GenCollectFullConcurrent,否则走的是VMThread的VM_GenCollectFull;由于VMThread会stop the world,因此这两种分支都会暂停业务线程

    VM_GenCollectFull

    class VM_GenCollectFull: public VM_GC_Operation {
     private:
      int _max_level;
     public:
      VM_GenCollectFull(unsigned int gc_count_before,
                        unsigned int full_gc_count_before,
                        GCCause::Cause gc_cause,
                          int max_level)
        : VM_GC_Operation(gc_count_before, gc_cause, full_gc_count_before, true /* full */),
          _max_level(max_level) { }
      ~VM_GenCollectFull() {}
      virtual VMOp_Type type() const { return VMOp_GenCollectFull; }
      virtual void doit();
    };
    

    VM_GenCollectFull的执行逻辑是放在doit方法:

    void VM_GenCollectFull::doit() {
      SvcGCMarker sgcm(SvcGCMarker::FULL);
    
      GenCollectedHeap* gch = GenCollectedHeap::heap();
      GCCauseSetter gccs(gch, _gc_cause);
      gch->do_full_collection(gch->must_clear_all_soft_refs(), _max_level);
    }
    

    直接调用GenCollectedHeap::do_full_collection方法实现的;

    VM_GenCollectFullConcurrent

    VM_GenCollectFullConcurrent的doit方法实现如下:

    void VM_GenCollectFullConcurrent::doit() {
      assert(Thread::current()->is_VM_thread(), "Should be VM thread");
      assert(GCLockerInvokesConcurrent || ExplicitGCInvokesConcurrent, "Unexpected");
    
      GenCollectedHeap* gch = GenCollectedHeap::heap();
      if (_gc_count_before == gch->total_collections()) {
       //虽然是full gc,但只收集yong区
        GCCauseSetter gccs(gch, _gc_cause);
        gch->do_full_collection(gch->must_clear_all_soft_refs(),0 );
      } 
     
      MutexLockerEx x(FullGCCount_lock, Mutex::_no_safepoint_check_flag);
      assert(_full_gc_count_before <= gch->total_full_collections(), "Error");
      if (gch->total_full_collections() == _full_gc_count_before) {
     
        CMSCollector::disable_icms();
        _disabled_icms = true;
        CMSCollector::start_icms();
        // 通过CMS线程执行CMS Full GC
        CMSCollector::request_full_gc(_full_gc_count_before, _gc_cause);
      } else {
        assert(_full_gc_count_before < gch->total_full_collections(), "Error");
        FullGCCount_lock->notify_all();  // Inform the Java thread its work is done
      }
    }
    
    
    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,CMS线程默认2秒钟检查一次_full_gc_requested标志位,如果发现为true,开始执行CMS垃圾收集
        _full_gc_requested = true;
        _full_gc_cause = cause;
        CGC_lock->notify();   // nudge CMS thread
      } else {
        assert(gc_count > full_gc_count, "Error: causal loop");
      }
    }
    

    可以看到要么通过VMThread执行Full GC,但只收集yong区域;要么通过CMSThread执行full gc(CMSCollector::collect_in_background),只有初始标记和再次标记阶段stop the world;相对于通过VMThread执行的full gc,业务停顿时间有较大的改善;

    相关文章

      网友评论

          本文标题:System.gc源码分析

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