1. 程式人生 > >jvm原始碼閱讀筆記[7]-從jstat -gccause命令談到jvm中都有哪些GC cause

jvm原始碼閱讀筆記[7]-從jstat -gccause命令談到jvm中都有哪些GC cause

    
    大家都知道,當我們使用以下命令時,會打印出導致GC的原因

jstat -gccause pid 1000

    

    
    可以看到最後2列分別列出了上次GC的原因和當前GC的原因。有一個”Heap Inspection Initiated GC”是因為我呼叫了jmap -histo:live pid。那麼,今天我們就來看看,gccause顯示的都會有哪些GC原因呢
    https://github.com/dmlloyd/openjdk/blob/jdk8u/jdk8u/hotspot/src/share/vm/gc_interface/gcCause.hpp#L39 中有一個Cause的列舉類,列舉了都有哪些cause,用於在觸發GC的時候做原因的區分。總共有27條,刪掉原始碼中沒有找到呼叫的,還剩17條。
    

enum Cause {
    /* public */
    _java_lang_system_gc,
    _full_gc_alot,
    _scavenge_alot,
    _allocation_profiler,//無
    _jvmti_force_gc,
    _gc_locker,
    _heap_inspection,
    _heap_dump,
    _wb_young_gc,
    _update_allocation_context_stats_inc,//無
    _update_allocation_context_stats_full,//無
/* implementation independent, but reserved for GC use */ _no_gc,//無 _no_cause_specified,//無 _allocation_failure, /* implementation specific */ _tenured_generation_full,//無 _metadata_GC_threshold, _cms_generation_full,//無 _cms_initial_mark, _cms_final_remark, _cms_concurrent_mark,//background式
_old_generation_expanded_on_last_scavenge,//無 _old_generation_too_full_to_scavenge,//無 _adaptive_size_policy, _g1_inc_collection_pause, _g1_humongous_allocation, _last_ditch_collection, _last_gc_cause//無 };

    
    在呼叫各種GC時,需要傳入具體的引數GCCause,來表示這是一次由什麼原因觸發的GC。上面程式碼後面的註釋“無”表示沒有在具體的呼叫GC的程式碼中找到有傳入這樣的一個cause,可能在jdk8中已經去掉了。
    下面來具體寫一下每個有呼叫的cause都是什麼原因引起的。

_java_lang_system_gc

    該cause大家應該都知道,是通過程式碼顯示呼叫System.gc()觸發的。還有一種情況是,在visualvm等軟體上通過JMX監控時有點選觸發SystemGC的按鈕。

_jvmti_force_gc

    JVMTI(JVM Tool Interface)是 Java 虛擬機器所提供的 native 程式設計介面。正是由於 JVMTI 的強大功能,它是實現 Java 偵錯程式,以及其它 Java 執行態測試與分析工具的基礎。
    可以參考這篇文章https://www.ibm.com/developerworks/cn/java/j-lo-jpda2/
所以_jvmti_force_gc就是通過jvmti方式觸發的GC

_gc_locker

    當通過jni方式運算元組或者是字串的時候,為了避免GC過程移動陣列或字串的記憶體地址,jvm實現了一個GC_locker這樣的東西,用於表示有執行緒在jni臨界區內,阻止其他執行緒進行GC操作。當最後一個位於jni臨界區內的執行緒退出臨界區時,發起一次CGCause為_gc_locker的GC.

//退出臨界區後釋放該鎖,併發起一次由gclocker觸發的gc
void GC_locker::jni_unlock(JavaThread* thread) {
  assert(thread->in_last_critical(), "should be exiting critical region");
  MutexLocker mu(JNICritical_lock);
  _jni_lock_count--;
  decrement_debug_jni_lock_count();
  thread->exit_critical();
  /*
  減少計數器之後如果=0,則表示這個是最後一個退出jni的執行緒,
  則需要觸發一次有gclocker的GC
  */
  if (needs_gc() && !is_active_internal()) {

    _doing_gc = true;
    {
      // Must give up the lock while at a safepoint
      MutexUnlocker munlock(JNICritical_lock);
      if (PrintJNIGCStalls && PrintGCDetails) {
        ResourceMark rm; // JavaThread::name() allocates to convert to UTF8
        gclog_or_tty->print_cr("%.3f: Thread \"%s\" is performing GC after exiting critical section, %d locked",
            gclog_or_tty->time_stamp().seconds(), Thread::current()->name(), _jni_lock_count);
      }
      Universe::heap()->collect(GCCause::_gc_locker);
    }
    _doing_gc = false;
    _needs_gc = false;
    JNICritical_lock->notify_all();
  }
}

_heap_inspection

    這個型別主要是jamp -hisot:live命令時會觸發。或者若設定了PrintClassHistogramBeforeFullGC
或者PrintClassHistogramAfterFullGC時,則在fullgc之前或者之後也會觸發一次GCCause=_heap_inspection的GC。

_heap_dump

    看名字就知道,它用於dump堆時,比如:

jmap -dump:format=b,file=a.hprof pid

    或者設定了引數HeapDumpBeforeFullGC,HeapDumpAfterFullGC。這2個引數用於在fullgc前/後自動dump堆,便於分析fullgc前後的差異。

_wb_young_gc

_allocation_failure

    這個就是常見的記憶體分配失敗觸發的GC。比如在new 物件時。

_metadata_GC_threshold

    這個用於在metaspace區域分配時分配不下,從而觸發的GC

_cms_initial_mark,_cms_final_remark

    這2個就是對於設定的CMS回收器時,有一個background式的回收時的初始標記和最終標記階段

_cms_concurrent_mark

    表示觸發GC的是一次cms的background式GC。可以參考原始碼筆記1:如何觸發一次CMS回收中都有哪些原因會觸發background式的GC

_adaptive_size_policy

    這個在ps中動態調整堆以及各個區大小時用到。

_g1_inc_collection_pause

    這個是設定的G1回收器時,若分配不下觸發的GC的cause

_g1_humongous_allocation

    這個和上面的區別是,這個用於分配超大物件失敗時觸發GC。
    對於分配普通大小的物件和超大物件,是呼叫的不同的方法,所以也有不同的GCCause的區分。

if (!isHumongous(word_size)) {
      result = attempt_allocation(word_size, &gc_count_before, &gclocker_retry_count);
    } else {
      result = attempt_allocation_humongous(word_size, &gc_count_before, &gclocker_retry_count);
    }
    if (result != NULL) {
      return result;
    }

_last_ditch_collection

    這個也是用於在metaspace區域分配不下時,最後的一次回收。若GCCause=_metadata_GC_threshold的GC後,仍分配不下,則會最後觸發一次cause=_last_ditch_collection的回收。此次回收會清除軟引用。若GC完再分配不下,就OOM了。這也正符合了軟引用的定義:在OOM發生之前會進行回收。
    相關原始碼:

if (!MetadataAllocationFailALot) {
    _result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
    if (_result != NULL) {
      return;
    }
  }

  if (initiate_concurrent_GC()) {
    // For CMS and G1 expand since the collection is going to be concurrent.
    _result = _loader_data->metaspace_non_null()->expand_and_allocate(_size, _mdtype);
    if (_result != NULL) {
      return;
    }

    log_metaspace_alloc_failure_for_concurrent_GC();
  }

  /
  先不清除軟引用
  */
  heap->collect_as_vm_thread(GCCause::_metadata_GC_threshold);

  _result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
  if (_result != NULL) {
    return;
  }


  _result = _loader_data->metaspace_non_null()->expand_and_allocate(_size, _mdtype);
  if (_result != NULL) {
    return;
  }

  /* 
   如果擴容失敗,做最後一次回收,且會回收軟引用。
  */
   heap->collect_as_vm_thread(GCCause::_last_ditch_collection);
  _result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);
  if (_result != NULL) {
    return;
  }

  if (Verbose && PrintGCDetails) {
    gclog_or_tty->print_cr("\nAfter Metaspace GC failed to allocate size "
                           SIZE_FORMAT, _size);
  }

  if (GC_locker::is_active_and_needs_gc()) {
    set_gc_locked();
  }

    能找到的有觸發的也就這些了。知道這些各種原因,對於使用jstat -gccause 排查FGC問題時也是幫助很大。
    

相關推薦

jvm原始碼閱讀筆記[7]-jstat -gccause命令jvm哪些GC cause

         大家都知道,當我們使用以下命令時,會打印出導致GC的原因 jstat -gccause pid 1000               可以看到最後2列分別列出了上次GC的原因和當前GC的原因。有一個”Heap Inspec

jvm原始碼閱讀筆記[3]:記憶體分配到觸發GC的細節

         除了第一篇說到的,對於使用cms回收的應用,會有執行緒輪詢判斷老年代是否滿足GC的條件,若滿足,則會觸發一次cms老年代的回收。     針對年輕代,更常見的是,執行緒優先在eden區分配物件的時候,若eden區空間不足,則會觸發一次y

jvm原始碼閱讀筆記[5]:記憶體分配失敗觸發的GC究竟對記憶體做了什麼?

         在第3篇文章中,我們總結到,當分配記憶體失敗時,會通過VM觸發一次由分配失敗觸發的一次GC,也就是我們經常能在GC日誌裡面看到的“allocation failure” VM_GenCollectForAllocation op(s

jvm原始碼閱讀筆記[6]-雜談JIT對Exception做的優化

public class NpeThief { public void callManyNPEInLoop() { for (int i = 0; i < 10

hashMap原始碼閱讀筆記1.7

這幾天一直在看hashMap的原始碼,也借鑑了很多大佬的文章以便更好的理解,也從大佬文章中借鑑了很多的內容,如果侵權,請告知,我將立刻刪除。 hashMap繼承AbstractMap,實現了map介面。 public class HashMap<K,V> extends

iOS -- Effective Objective-C 閱讀筆記 (7)

成對 format 開發 數據 清晰 rip 相同 命名法 定義 1: 實現 description 方法 NSlog 在輸出自定義的類時, 只輸出了 類名 和 對象的內存地址. 要想輸出更為有用的信息也很簡單, 只需要覆寫 description 方法並將描述此對象的字符

原始碼閱讀筆記——Tablib

文章目錄 Tablib 用法示例 程式碼結構 程式碼結構與風格 細節 Reference Tablib Tablib是一個支援多格式資料轉換的庫,支援的格式包括XLSX、XLS、JSON、YA

原始碼閱讀筆記——HowDoI

文章目錄 HowDoI 用法示例 程式碼結構 主要依賴 程式碼結構與風格 細節 Reference HowDoI HowDoI是命令列應用,用以搜尋程式設計問題的答案,原始碼十分簡短,

jdk原始碼閱讀筆記-LinkedHashMap

  Map是Java collection framework 中重要的組成部分,特別是HashMap是在我們在日常的開發的過程中使用的最多的一個集合。但是遺憾的是,存放在HashMap中元素都是無序的,原因是我們在put或get資料的時候都是根據key的hash值來確定元素的位置。在具體的業務場景中,我們更

jdk原始碼閱讀筆記-ArrayList

一、ArrayList概述 首先我們來說一下ArrayList是什麼?它解決了什麼問題?ArrayList其實是一個數組,但是有區別於一般的陣列,它是一個可以動態改變大小的動態陣列。ArrayList的關鍵特性也是這個動態的特性了,ArrayList的設計初衷就是為了解決Java陣列長度不可變的

.NetCore原始碼閱讀筆記系列之HttpAbstractions(五) Authentication

說道認證&授權其實這塊才是核心,這款跟前面Security這塊有者緊密的聯絡,當然 HttpAbstractions 不光是認證、授權、還包含其他Http服務和中間價 接下來先就認證這塊結合前面的Security作一個補充說明 前面 AddCookie 、OpenIdConnect、Go

mxnet原始碼閱讀筆記之include

寫在前面 mxnet程式碼的規範性比Caffe2要好,看起來核心程式碼量也小很多,但由於對dmlc其它庫的依賴太強,程式碼的獨立性並不好。依賴的第三方庫包括: cub dlpack dmlc-core googletest mkldnn mshadow onnx-tensorrt openmp ps-lite

JAVA 10原始碼閱讀筆記之JEP-307(G1的並行Full GC

# 1. 背景 JEP-307解決了G1垃圾回收器的一個嚴重的問題,截止到Java 9,G1的Full GC採用的是單執行緒演算法,嚴重影響效能,無法利用到多核能力進行垃圾回收。JEP-307修復了此問題,發生Full GC時允許使用多個執行緒進行並行回收。 # 2. G1

base_local_planner原始碼閱讀筆記

ROS的navigation stack中區域性規劃器的介面plugin類為nav_core::BaseLocalPlanner,給出的包有4種: base_local_planner - 提供了 D

Disruptor原始碼閱讀筆記

Disruptor是什麼 關於 Disruptor,網路上有很多的解釋和說法。這裡簡單的概括下。Disruptor 是一個消費者生產者佇列框架,據官網介紹,可以提供非常強大的效能。Disruptor 與其說為我們帶來了一個框架,更多的是為我們帶來了一個獨特思路的程式設計實踐

spark原始碼閱讀筆記Dataset(二)DatasetActions、function、transformations

package Dataset import org.apache.spark.sql.functions._ import org.apache.spark.sql.{DataFrame, Dataset, SparkSession} /** * Cr

轉:Django 原始碼閱讀(一):概覽入口到請求到響應

轉載:Django 原始碼閱讀(一):概覽從入口到請求到響應————作者:hongweipeng 起步 在我研究完 django 的自動載入機制後,有了閱讀 django 原始碼的想法。那就看看吧,也不知道能堅持到什麼地方。我閱讀的版本也是我正在使用的 1.10.5 版本,算是比較新的了。

Flask 原始碼閱讀筆記 開篇

Flask 是一個 Python 實現的 Web 開發微框架, 有豐富的生態資源。本文從一段官方的示例程式碼通過一步步打斷點方式解釋 Flask 內部的執行機制,在一些關鍵概念會有相關解釋,這些前提概念對整體理解 Flask框架十分重要,本文基於flask 0

jdk原始碼閱讀筆記-LinkedList

  一、LinkedList概述   LinkedList的底層資料結構為雙向連結串列結構,與ArrayList相同的是LinkedList也可以儲存相同或null的元素。相對於ArrayList來說,LinkedList的插入與刪除的速度更快,時間複雜度為O(1),查詢的速度就相對比較慢了,因為每次遍歷的時

spark原始碼閱讀筆記Dataset(三)structField、structType、schame

StructType(fields: Seq[StructField]) 一個StructType物件,可以有多個StructField,同時也可以用名字(name)來提取,就想當於Map可以用key來提取value,但是他StructType提取的是整條欄位的資訊 在原始碼中structType是一個cas