jvm源码阅读笔记[7]-从jstat -gccause命令谈到jvm中都有哪些GC cause
2017-09-24 17:44
471 查看
从零开始看源码,旨在从源码验证书上的结论,探索书上未知的细节。有疑问欢迎留言探讨
个人源码地址:https://github.com/FlashLightNing/openjdk-notes
还有一个openjdk6,7,8,9的地址:https://github.com/dmlloyd/openjdk
jvm源码阅读笔记[1]:如何触发一次CMS回收
jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解
jvm源码阅读笔记[3]:从内存分配到触发GC的细节
jvm源码阅读笔记[4]:从GC说到vm operation
jvm源码阅读笔记[5]:内存分配失败触发的GC究竟对内存做了什么?
jvm源码阅读笔记[6]:杂谈JIT中对Exception做的优化
大家都知道,当我们使用以下命令时,会打印出导致GC的原因
可以看到最后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条。
在调用各种GC时,需要传入具体的参数GCCause,来表示这是一次由什么原因触发的GC。上面代码后面的注释“无”表示没有在具体的调用GC的代码中找到有传入这样的一个cause,可能在jdk8中已经去掉了。
下面来具体写一下每个有调用的cause都是什么原因引起的。
可以参考这篇文章https://www.ibm.com/developerworks/cn/java/j-lo-jpda2/
所以_jvmti_force_gc就是通过jvmti方式触发的GC
或者PrintClassHistogramAfterFullGC时,则在fullgc之前或者之后也会触发一次GCCause=_heap_inspection的GC。
或者设置了参数HeapDumpBeforeFullGC,HeapDumpAfterFullGC。这2个参数用于在fullgc前/后自动dump堆,便于分析fullgc前后的差异。
对于分配普通大小的对象和超大对象,是调用的不同的方法,所以也有不同的GCCause的区分。
相关源码:
能找到的有触发的也就这些了。知道这些各种原因,对于使用jstat -gccause 排查FGC问题时也是帮助很大。
从零开始看源码,旨在从源码验证书上的结论,探索书上未知的细节。有疑问欢迎留言探讨
个人源码地址:https://github.com/FlashLightNing/openjdk-notes
还有一个openjdk6,7,8,9的地址:https://github.com/dmlloyd/openjdk
jvm源码阅读笔记[1]:如何触发一次CMS回收
jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解
jvm源码阅读笔记[3]:从内存分配到触发GC的细节
jvm源码阅读笔记[4]:从GC说到vm operation
jvm源码阅读笔记[5]:内存分配失败触发的GC究竟对内存做了什么?
jvm源码阅读笔记[6]:杂谈JIT中对Exception做的优化
个人源码地址:https://github.com/FlashLightNing/openjdk-notes
还有一个openjdk6,7,8,9的地址:https://github.com/dmlloyd/openjdk
jvm源码阅读笔记[1]:如何触发一次CMS回收
jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解
jvm源码阅读笔记[3]:从内存分配到触发GC的细节
jvm源码阅读笔记[4]:从GC说到vm operation
jvm源码阅读笔记[5]:内存分配失败触发的GC究竟对内存做了什么?
jvm源码阅读笔记[6]:杂谈JIT中对Exception做的优化
大家都知道,当我们使用以下命令时,会打印出导致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
很见,用于whitebox测试,见https://wiki.openjdk.java.net/display/HotSpot/The+WhiteBox+testing+API_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问题时也是帮助很大。
从零开始看源码,旨在从源码验证书上的结论,探索书上未知的细节。有疑问欢迎留言探讨
个人源码地址:https://github.com/FlashLightNing/openjdk-notes
还有一个openjdk6,7,8,9的地址:https://github.com/dmlloyd/openjdk
jvm源码阅读笔记[1]:如何触发一次CMS回收
jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解
jvm源码阅读笔记[3]:从内存分配到触发GC的细节
jvm源码阅读笔记[4]:从GC说到vm operation
jvm源码阅读笔记[5]:内存分配失败触发的GC究竟对内存做了什么?
jvm源码阅读笔记[6]:杂谈JIT中对Exception做的优化
相关文章推荐
- jvm源码阅读笔记[3]:从内存分配到触发GC的细节
- jvm源码阅读笔记[5]:内存分配失败触发的GC究竟对内存做了什么?
- jvm源码阅读笔记[4]:从GC说到vm operation
- Linux使用jstat命令查看jvm的GC情况
- Linux使用jstat命令查看jvm的GC情况
- Linux使用jstat命令查看jvm的GC情况
- Linux使用jstat命令查看jvm的GC情况
- jvm源码阅读笔记[6]-杂谈JIT中对Exception做的优化
- jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解
- jstat命令查看jvm的GC情况 (以Linux为例)
- Linux使用jstat命令查看jvm的GC情况
- jstat 命令,看jvm区域情况,GC情况
- Linux使用jstat命令查看jvm的GC情况
- Linux使用jstat命令查看jvm的GC情况
- Linux使用jstat命令查看jvm的GC情况
- Linux使用jstat命令查看jvm的GC情况
- Linux 使用jstat命令查看jvm的GC情况
- jstat命令查看jvm的GC情况 (以Linux为例)
- Linux使用jstat命令查看jvm的GC情况
- Linux使用jstat命令查看jvm的GC情况