Java HotSpot(TM) 64-Bit Server VM (25.202-b08) for linux-amd64 JRE (1.8.0_202-b08), built on Dec 15 2018 12:40:22 by "java_re" with gcc 7.3.0
Memory: 4k page, physical 98838152k(4043244k free), swap 16777212k(9148048k free)
CommandLine flags: -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSScavengeBeforeRemark -XX:CompressedClassSpaceSize=260046848 -XX:+DisableExplicitGC -XX:ErrorFile=/var/app/gc/hs_err_pid%p.log -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/app/gc -XX:InitialHeapSize=2818572288 -XX:MaxHeapSize=2818572288 -XX:MaxMetaspaceSize=268435456 -XX:MaxNewSize=1006632960 -XX:MaxTenuringThreshold=6 -XX:MetaspaceSize=268435456 -XX:NewSize=1006632960 -XX:OldPLABSize=16 -XX:+ParallelRefProcEnabled -XX:+PrintGC -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
2023-08-09T09:56:16.529+0000: 0.436: Total time for which application threads were stopped: 0.0002870 seconds, Stopping threads took: 0.0000777 seconds
2023-08-09T09:56:16.582+0000: 0.489: Total time for which application threads were stopped: 0.0001678 seconds, Stopping threads took: 0.0000354 seconds
2023-08-09T09:56:17.059+0000: 0.966: Total time for which application threads were stopped: 0.0003279 seconds, Stopping threads took: 0.0000319 seconds
2023-08-09T09:56:17.194+0000: 1.101: Total time for which application threads were stopped: 0.0003068 seconds, Stopping threads took: 0.0000202 seconds
2023-08-09T09:56:17.256+0000: 1.163: Total time for which application threads were stopped: 0.0003321 seconds, Stopping threads took: 0.0001093 seconds
2023-08-09T09:56:17.485+0000: 1.392: Total time for which application threads were stopped: 0.0005075 seconds, Stopping threads took: 0.0001826 seconds
2023-08-09T09:56:17.510+0000: 1.416: Total time for which application threads were stopped: 0.0023176 seconds, Stopping threads took: 0.0020402 seconds
2023-08-09T09:56:17.831+0000: 1.738: Total time for which application threads were stopped: 0.0004216 seconds, Stopping threads took: 0.0001025 seconds
2023-08-09T09:56:17.910+0000: 1.817: Total time for which application threads were stopped: 0.0003990 seconds, Stopping threads took: 0.0000672 seconds
2023-08-09T09:56:18.100+0000: 2.007: Total time for which application threads were stopped: 0.0001958 seconds, Stopping threads took: 0.0000334 seconds
2023-08-09T09:56:18.315+0000: 2.222: Total time for which application threads were stopped: 0.0004638 seconds, Stopping threads took: 0.0000256 seconds
2023-08-09T09:56:18.316+0000: 2.223: Total time for which application threads were stopped: 0.0002114 seconds, Stopping threads took: 0.0000163 seconds
2023-08-09T09:56:18.553+0000: 2.460: Total time for which application threads were stopped: 0.0004370 seconds, Stopping threads took: 0.0000176 seconds
2023-08-09T09:56:18.594+0000: 2.501: Total time for which application threads were stopped: 0.0037669 seconds, Stopping threads took: 0.0033805 seconds
2023-08-09T09:56:18.658+0000: 2.564: Total time for which application threads were stopped: 0.0004332 seconds, Stopping threads took: 0.0000330 seconds
{Heap before GC invocations=0 (full 0):
par new generation total 884736K, used 786432K [0x0000000748800000, 0x0000000784800000, 0x0000000784800000)
eden space 786432K, 100% used [0x0000000748800000, 0x0000000778800000, 0x0000000778800000)
from space 98304K, 0% used [0x0000000778800000, 0x0000000778800000, 0x000000077e800000)
to space 98304K, 0% used [0x000000077e800000, 0x000000077e800000, 0x0000000784800000)
concurrent mark-sweep generation total 1769472K, used 0K [0x0000000784800000, 0x00000007f0800000, 0x00000007f0800000)
Metaspace used 25279K, capacity 26130K, committed 26368K, reserved 1073152K
class space used 3298K, capacity 3497K, committed 3584K, reserved 1048576K
2023-08-09T09:56:18.786+0000: 2.693: [GC (Allocation Failure) 2023-08-09T09:56:18.786+0000: 2.693: [ParNew: 786432K->9099K(884736K), 0.0692137 secs] 786432K->9099K(2654208K), 0.0693093 secs] [Times: user=0.08 sys=0.00, real=0.07 secs]
Heap after GC invocations=1 (full 0):
par new generation total 884736K, used 9099K [0x0000000748800000, 0x0000000784800000, 0x0000000784800000)
eden space 786432K, 0% used [0x0000000748800000, 0x0000000748800000, 0x0000000778800000)
from space 98304K, 9% used [0x000000077e800000, 0x000000077f0e2c90, 0x0000000784800000)
to space 98304K, 0% used [0x0000000778800000, 0x0000000778800000, 0x000000077e800000)
concurrent mark-sweep generation total 1769472K, used 0K [0x0000000784800000, 0x00000007f0800000, 0x00000007f0800000)
Metaspace used 25279K, capacity 26130K, committed 26368K, reserved 1073152K
class space used 3298K, capacity 3497K, committed 3584K, reserved 1048576K
}
2023-08-09T09:56:18.856+0000: 2.762: Total time for which application threads were stopped: 0.0695623 seconds, Stopping threads took: 0.0000300 seconds
日志举例二
[GC (Allocation Failure) [DefNew: 4669K->330K(4928K), 0.0011597 secs] 9235K->4897K(15872K), 0.0011820 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 4746K->316K(4928K), 0.0011910 secs] 9313K->4966K(15872K), 0.0012126 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 4732K->237K(4928K), 0.0012076 secs] 9382K->4968K(15872K), 0.0012277 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (GCLocker Initiated GC) [DefNew: 4653K->262K(4928K), 0.0012791 secs] 9385K->4993K(15872K), 0.0012996 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 4674K->332K(4928K), 0.0010540 secs] 9406K->5093K(15872K), 0.0010742 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 4748K->185K(4928K), 0.0013724 secs] 9509K->5054K(15872K), 0.0013924 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Java HotSpot(TM) 64-Bit Server VM (25.202-b08) for linux-amd64 JRE (1.8.0_202-b08), built on Dec 15 2018 12:40:22 by "java_re" with gcc 7.3.0
Memory: 4k page, physical 98838152k(4043244k free), swap 16777212k(9148048k free)
CommandLine flags:
-XX:+CMSClassUnloadingEnabled
-XX:CMSInitiatingOccupancyFraction=70
-XX:+CMSScavengeBeforeRemark
-XX:CompressedClassSpaceSize=260046848
-XX:+DisableExplicitGC
-XX:ErrorFile=/var/app/gc/hs_err_pid%p.log
-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/app/gc
-XX:InitialHeapSize=2818572288
-XX:MaxHeapSize=2818572288
-XX:MaxMetaspaceSize=268435456
-XX:MaxNewSize=1006632960
-XX:MaxTenuringThreshold=6
-XX:MetaspaceSize=268435456
-XX:NewSize=1006632960
-XX:OldPLABSize=16
-XX:+ParallelRefProcEnabled
-XX:+PrintGC
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
这里分为三部分信息:版本和环境、内存、命令行参数:
第一行:版本和环境信息。构成:虚拟机的版本信息 for 操作系统信息,built on JDK的构建信息
第二行:内存信息。构成:内存页大小 page,physical 物理内存xxx(xxx 空闲free)swap 交换分区xxx(xxx 空闲free)
第三行:命令行参数信息。这里面的参数和启动参数之间是存在一些关系和差异的。有一些是和启动参数配置一样的,只是把(G/M/K)数值转化为了字节而已。需要重点关注的是以下两类:
意义一样名字有变化
-XX:InitialHeapSize 对应启动参数里的 -Xms
-XX:MaxHeapSize 对应启动参数里的 -Xmx
启动参数里没有设置的但是这里出现的
例如:-XX:+PrintGC等等
2023-08-09T09:56:16.529+0000: 0.436: Total time for which application threads were stopped: 0.0002870 seconds, Stopping threads took: 0.0000777 seconds
2023-08-09T09:56:16.529+0000: 0.436:GC发生的时间
Total time for which application threads were stopped: 0.0002870 seconds, Stopping threads took: 0.0000777 seconds:文件中会出现大量类似行日志,行数表示VM operation overhead数。当GC发生时,每个线程只有进入了SafePoint才算是真正挂起,也就是真正的停顿,这个日志的含义是整个GC过程中STW的时间,配置了 -XX:+PrintGCApplicationStoppedTime 这个参数才会打印这个信息。
特别地,如果是GC引发的STW,这条内容会紧挨着出现在GC log的下面。
{Heap before GC invocations=0 (full 0):
par new generation total 884736K, used 786432K [0x0000000748800000, 0x0000000784800000, 0x0000000784800000)
eden space 786432K, 100% used [0x0000000748800000, 0x0000000778800000, 0x0000000778800000)
from space 98304K, 0% used [0x0000000778800000, 0x0000000778800000, 0x000000077e800000)
to space 98304K, 0% used [0x000000077e800000, 0x000000077e800000, 0x0000000784800000)
concurrent mark-sweep generation total 1769472K, used 0K [0x0000000784800000, 0x00000007f0800000, 0x00000007f0800000)
Metaspace used 25279K, capacity 26130K, committed 26368K, reserved 1073152K
class space used 3298K, capacity 3497K, committed 3584K, reserved 1048576K
这是gc开始之前堆和元数据信息
该日志一般出现在GC操作之前,在GC操作之后会再次打印年轻代占用内存情况,可做对比。
Heap before GC invocations=0 (full 0):GC前的堆内存占用情况。invocations=0说明本次gc开始之前,已经经历过0次垃圾回收。其中full gc是0次。
par new generation total 884736K, used 786432K:使用parnew,表示年轻代堆内存总共884736K,已经使用786432K 。
eden space 786432K, 100% used[0x0000000748800000, 0x0000000778800000, 0x0000000778800000):年轻代Eden区域的内存大小,使用情况,对应区的内存区域起始内存标记位,已使用的内存地址标记位,空间结束位置的标记位
from space 98304K, 0% used:年轻代Survivor中FromSpace区域的内存大小,以及使用情况
to space 98304K, 0% used:年轻代Survivor中ToSpace区域的内存大小,以及使用情况
concurrent mark-sweep generation total 1769472K, used 0K:老年代所占内存情况,以及使用情况
Metaspace used 25279K, capacity 26130K, committed 26368K, reserved 1073152K:程序元空间内存占用情况(非JVM)
class space used 3298K, capacity 3497K, committed 3584K, reserved 1048576K:Metaspace 分为两个区域:non-class part 和 class part。class part被称为class spec。
=========================================== 日志举例一 ===========================================
2023-08-09T09:56:18.786+0000: 2.693: [GC (Allocation Failure) 2023-08-09T09:56:18.786+0000: 2.693: [ParNew: 786432K->9099K(884736K), 0.0692137 secs] 786432K->9099K(2654208K), 0.0693093 secs] [Times: user=0.08 sys=0.00, real=0.07 secs]
=========================================== 日志举例二 ===========================================
[GC (Allocation Failure) [DefNew: 4732K->237K(4928K), 0.0012076 secs] 9382K->4968K(15872K), 0.0012277 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (GCLocker Initiated GC) [DefNew: 4653K->262K(4928K), 0.0012791 secs] 9385K->4993K(15872K), 0.0012996 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
GC:GC的类型,用来区分Minor GC还是Full GC的标志。这里指一次小型GC(Minor GC)
ParNew:垃圾回收器的名称,Par表示Parallel,New表示年轻代,Parnew指并行收集年轻代垃圾。
786432K->9099K(884736K):回收情况,表示年轻代回收前->回收后(年轻代内存大小)
0.0692137 secs:耗费时间
786432K->9099K(2654208K):表示堆内存回收前大小->堆内存回收后大小(堆总内存大小)
[Times: user=0.08 sys=0.00, real=0.07 secs] :GC事件的持续时间,通过三个部分衡量(由于并不是所有的操作过程都能全部并行,所以在并行GC中,real约等于user+system/GC线程数):
DefNew,ParNew,Turnred:表示GC发生的区域,这里显示的区域名称与使用的GC收集器是密切相关的
Allocation Failure:GC产生的原因。表示向young generation(eden)给新对象申请空间,但是young generation(eden)剩余的合适空间不够所需的大小导致的minor gc
另外几种常见的GC原因简介:
GCLocker Initiated GC:使用JNI临界区的方式操作数组或者字符串时,为了防止GC过程中jarray或者jstring发生位移,而导致数组指针失效,需要保持它们在JVM Heap中的地址在JNI Critical过程中保持不变。于是JVM实现了GC_locker,用于JNI Critical内阻止其他GC的发生。
当GCLocker被激活且需要发生GC的时候(这里是否需要GC是各种GC发生时,调用GCLocker::check_active_before_gc()函数check并设置needs_gc = true的),就会阻塞其他线程进入JNI临界区;并且在最后一个位于JNI临界区的线程退出临界区时,发起一次CGCause为gc_locker的GC。
Ergonomics:负责自动的调解gc暂停时间和吞吐量之间的平衡。内存在进行分配的时候,会通过一些算法,预估是否会出现无法分配的问题。如果符合无法分配预估值,会提前进行一次gc。
Metedata GC Threshold:主要发生的条件是元空间,也就是Metadata的参数设置问题。
JDK8中,XX:MaxMetaspaceSize确实是没有上限的,最大容量与机器的内存有关;但是XX:MetaspaceSize是有一个默认值的:21M。
GC触发的原因是因为Metaspace大小达到了GC阈值。
G1 Evacuation Pause:空间使用达到阈值,如果是young gc则意味着年轻代已满,如果是mixed gc则大多数情况下是老年代空间占整个堆空间比例达到阈值(默认45%)。
G1 Humongous Allocation:表示巨型对象空间分配申请。每一个巨型对象的的内存分配都会触发一次gc尝试,如果当前已经处在并发标记周期阶段了,则不会主动发起gc,否则会主动发起gc。
有时候我们会看到某些GC日志还包括GC细节过程中的耗时情况
如下为新生代内存空间进行回收日志:
[Parallel Time: 12.9 ms, GC Workers: 8] // 开启了8个GC线程
[GC Worker Start (ms): Min: 3926.9, Avg: 3934.2, Max: 3939.7, Diff: 12.8] // 工作线程启动时间
[Ext Root Scanning (ms): Min: 0.0, Avg: 0.3, Max: 2.5, Diff: 2.5, Sum: 2.6] //扫描root的耗时
[Update RS (ms): Min: 0.0, Avg: 0.3, Max: 2.1, Diff: 2.1, Sum: 2.1] //更新RS的耗时
[Processed Buffers: Min: 0, Avg: 1.9, Max: 15, Diff: 15, Sum: 15]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.1] //扫描RS的耗时
[Code Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.6, Diff: 0.6, Sum: 0.6] //
[Object Copy (ms): Min: 0.0, Avg: 4.5, Max: 7.1, Diff: 7.1, Sum: 35.9] //对象拷贝耗时
[Termination (ms): Min: 0.0, Avg: 0.4, Max: 0.5, Diff: 0.5, Sum: 2.9]
[Termination Attempts: Min: 1, Avg: 74.2, Max: 133, Diff: 132, Sum: 594]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[GC Worker Total (ms): Min: 0.0, Avg: 5.5, Max: 12.8, Diff: 12.8, Sum: 44.3]
[GC Worker End (ms): Min: 3939.7, Avg: 3939.7, Max: 3939.8, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.3 ms]
[Other: 1.1 ms] //其它任务的耗时
[Choose CSet: 0.0 ms] //CSet选择Region的时间
[Ref Proc: 0.7 ms] //处理对象引用的时间
[Ref Enq: 0.0 ms] //引用入ReferenceQueues队列的时间
[Redirty Cards: 0.2 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms] //释放CSet时间
[Eden: 52.0M(52.0M)->0.0B(52.0M) Survivors: 8192.0K->8192.0K Heap: 74.2M(100.0M)->25.6M(100.0M)]
Eden 回收前用量(总容量)->回收后用量(总容量),Survivors区回收前用量-回收后用量, 堆内存回收前用量(总容量)->回收后用量(总容量)
概念
A point during program execution at which all GC roots are known and all heap object contents are consistent. From a global point of view, all threads must block at a safepoint before the GC can run. (Except thread running JNI code)
在SafePoint时,所有的 GC Roots 和堆对象内容将保持一致。从全局来看,所有的线程需要在该时刻阻塞,以便于执行 GC。
何种情况下进入SafePoint
在JVM中,存在一个单例的原始线程VM Thread
,它会产生创建所有其他JVM线程,同时也会被其他线程用于执行一些成本较高的VM Operions
。这些VM Operations
会保存在VMOperationQueue
中,通过自轮询方法loop()
进行处理。
而在VM Thread
退出及轮询处理VM Operations
的过程中,需要STW
来保证结果的准确性,就调用 SafepointSynchronize::begin() 进入SafePoint
。而处理完VM Operations
后,通过调用 SafepointSynchronize::end() 退出。
VM Thread退出时:JVM即将关闭,此时会进入SafePoint
,进行退出前的检查,并等待native thread执行完毕,最终关闭整个JVM。(此时只有begin,没有end,因为JVM已经要关闭了)
处理VM Operation时:在处理一些JVM行为时,会进入SafePoint
,这些JVM行为定义在vm_operations.hpp 中。
总体可以归结在以下几个行为类别中:
(1)Garbage collection pauses(垃圾回收)
(2)JIT相关,比如Code deoptimization, Flushing code cache
(3)Class redefinition (e.g. javaagent,AOP代码植入的产生的instrumentation)
(4)Biased lock revocation 取消偏向锁
(5)Various debug operation (e.g. thread dump or deadlock check) dump 线程
或者换一种方式表达:
(1)GC行为
(2)反优化行为
(3)部分JNI相关行为
(4)偏向锁相关行为
(5)调试行为(threaddump、heapdump、stacktrace等)
如何进入SafePoint
这是一个比较复杂的过程,JVM线程可能在处理不同的事务,处于不同的状态,为了能够让它们能够进入SafePoint
,JVM设计了不同的机制。
线程状态 | 机制 |
---|---|
解释执行中 | 修改解释器dispatch table,在下一条字节码执行前,强制检查SafePoint 条件 |
本地方法代码段执行中 | 当线程从本地方法调用返回时,必须检查SafePoint 条件 |
编译执行中 | JIT会在编译后的指令中插入检查点,检查点会进行SafePoint 条件判断 |
阻塞中 | 已处于阻塞中,使线程保持阻塞状态即可 |
状态转换中 | 此时会等待线程完成状态转换后阻塞其自身 |
Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。
STW是Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;STW多半是由于gc引起。
所有线程进入 SafePoint 等待的情况就是 Stop The World。比如,GC 时候一定需要所有线程同时进入 SafePoint,并停留在那里,等待 GC 处理完内存,再让所有线程继续执行。
偏向锁(biased locking)是一种优化机制,它使得一个线程在释放锁之后,vm仍旧在逻辑上让该线程"持有"一个对象的锁,它的优化前提点是该线程会在稍后重新获取该锁(这是一个常见的事件)。如果此时有其他线程争抢该锁,则必须撤消偏向锁持有者的偏向锁。
反优化(deoptimization)是一个将编译的(或者更优化的)栈桢转化为解释的(或者弱优化的)栈桢的过程。它也被解释为放弃依赖条件被打破(或者假定被打破)的nmethod的过程。反优化nmethod一般会被重新编译以便适配应用行为的变化。举个例子:编译器初始假定一个引用的值从不为null,并且使用"捕获内存访问"的方法进行测试。后续程序运行过程中,程序使用了null值,那么方法必须进行反优化和重编译,使用显示的test-and-branch方式发现此类null值。
它是一个特殊的持有编译后代码的堆,这些对象不会被gc搬移,但它们可能会包含服务于gc roots 的oops。
垃圾收集根(garbage collection root),它是一个从外部指向java对象堆的指针.举例:从类的静态字段或活化栈桢的本地对堆中对象的引用。
JIT编译器是一个应用运行时为应用(或类库)自生成代码的在线编译器。JIT即just in time.JIT编译器可以在java方法执行前非常快速地创建机器码。Hotspot编译器允许解释器执行java方法数千次以采样运行数据并热身,因为它可以在类加载初始化后观测到完整的类层级,热身周期使编译器有充足的依据做出优化决策。编译器也可以检视解释器收集的分支和类型的剖析信息。
java本地方法接口.它定义了一组api,用于java代码调用native c代码,以及它怎么调用java虚拟机代码。
对象指针(oop),即object pointer,一般来说是一个指向gc管理堆的指针(这是一个传统的协议,o代表ordinary).它的实现是一个本地机器地址而不是一个句柄。对象指针可以由编译器或解释器java代码直接组装,因为gc知道这些代码中的对象指针的存活情况和位置。(参见gc映射)对象指针可以直接用c/c++代码来组装,但在跨越安全点时相应的代码一定要把它保持在每个句柄之内。
虚拟机操作(VM Operations),虚拟机中可以被java线程请求的,但是一定要由虚拟机线程以串行方式执行的操作,这些操作通常是同步的,因此请求者将会block直到虚拟机线程完成旧有操作。很多这些操作通常需要虚拟机到达安全点,gc是一个简单的例子。