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(二)Dataset中Actions、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