JVM调优学习之旅-(5)初识gc⽇志ParNew+CMS
beyond是什么意思
模拟GC
JVM参数
-XX:NewSize=5242880-XX:MaxNewSize=5242880-XX:SurvivorRatio=8
-XX:InitialHeapSize=10485760-XX:MaxHeapSize=10485760
-XX:PretenureSizeThreshold=10485760
-XX:+UParNewGC -XX:+UConcMarkSweepGC
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log
参数说明
-XX:NewSize 初始新⽣代⼤⼩ 5M
-XX:MaxNewSize 最⼤新⽣代⼤⼩ 5M
-
XX:SurvivorRatio Eden区与s1 s2 区的⽐例
-XX:InitialHeapSize 初始堆⼤⼩ 10M
-XX:MaxHeapSize 最⼤堆⼤⼩ 10M
-XX:+UParNewGC -XX:+UConcMarkSweepGC 新⽣代parnew ⽼年代 cms
-XX:PretenureSizeThreshold=10485760 指定了⼤对象阈值是10MB
p s是什么意思
-XX:+PrintGCDetils:打印详细的gc⽇志
-XX:+PrintGCTimeStamps:这个参数可以打印出来每次GC发⽣的时间
-Xloggc:gc.log:这个参数可以设置将gc⽇志写⼊⼀个磁盘⽂件
⽰例代码
public class Demo1 {
public static void main(String[] args){
byte[] array1 =new byte[1024*1024];
array1 =new byte[1024*1024];
array1 =new byte[1024*1024];
array1 = null;
byte[] array2 =new byte[2*1024*1024];
}
}
gc⽇志
Java HotSpot(TM) 64-Bit Server VM (25.231-b11) for windows-amd64 JRE (1.8.0_231-b11), built on Oct 5 2019 03:11:30 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 33359028k(19834688k free), swap 35456180k(14483660k free)
CommandLine flags: -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=5242880 -XX:NewSize=5242880 -XX:OldPLABSize=1 6 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UCompresdClassP ointers -XX:+UCompresdOops -XX:+UConcMarkSweepGC -XX:-ULargePagesIndividualAllocation -XX:+UParNewGC
climate change
2011江苏英语高考0.103: [GC (Allocation Failure) 0.103: [ParNew: 3684K->512K(4608K), 0.0018922 cs] 3684K->1647K(9728K), 0.0020309 cs] [Times: ur=0.00 sys=0. 00, real=0.00 cs]
Heap
par new generation total 4608K, ud 3746K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
eden space 4096K, 78% ud [0x00000000ff600000, 0x00000000ff928990, 0x00000000ffa00000)
from space 512K, 100% ud [0x00000000ffa80000, 0x00000000ffb00000, 0x00000000ffb00000)
to space 512K, 0% ud [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
concurrent mark-sweep generation total 5120K, ud 1135K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
Metaspace ud 3143K, capacity 4496K, committed 4864K, rerved 1056768K
class space ud 343K, capacity 388K, committed 512K, rerved 1048576K
前⾯都是⼀⽬了然 我们从第六⾏开始看
0.103: [GC (Allocation Failure) 0.103: [ParNew: 3684K->512K(4608K), 0.0018922 cs] 3684K->1647K(9728K), 0.0020309 cs] [Times: ur=0.00 sys=0. 00, real=0.00 cs]
这是⼀次GC概要说明:
GC (Allocation Failure) 由于分配失败产⽣⼀次gc 系统运⾏到0.103秒时ParNew 产⽣⼀次年轻代gc
年轻代空间4.5M (Eden区是4MB,两个Survivor中只有⼀个是可以放存活对象的,另外⼀个是必须⼀致保持空闲的)
3684K 说明已经使⽤了多⼤空间但是gc 过后存活下来512k
3684K->1647K(9728K) 这是整个堆gc后整体情况 gc前占⽤ 3684k gc后还占⽤ 1647k
Times: ur=0.00 sys=0.00, real=0.00 cs 由于单位是秒所以忽略为0 了
Heap
par new generation total 4608K, ud 3746K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
eden space 4096K, 78% ud [0x00000000ff600000, 0x00000000ff928990, 0x00000000ffa00000)
supermarket怎么读from space 512K, 100% ud [0x00000000ffa80000, 0x00000000ffb00000, 0x00000000ffb00000)
to space 512K, 0% ud [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
concurrent mark-sweep generation total 5120K, ud 1135K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
Metaspace ud 3143K, capacity 4496K, committed 4864K, rerved 1056768K
class space ud 343K, capacity 388K, committed 512K, rerved 1048576K
这是内存结束后的情况
新⽣代总⼤⼩4608k⽬前还在使⽤3746k 年轻代⽬前占⽤78% ⾥⾯包含了2M数组 还有未知对象 s1也占⽤了⼀个
⽼年代也存在1135k对象 。
具体还存在哪些对象后续⽤⼯具看清楚,先了解个⼤概
Metaspace ud 3143K, capacity 4496K, committed 4864K, rerved 1056768K
class space ud 343K, capacity 388K, committed 512K, rerved 1048576K
这⾥牵涉到了操作系统的虚拟内存的概念!
财务统计
⾸先 Jdk8开始把类的元数据 放到本地内存(native heap),称之为MetaSpace,理论上本地内存剩余多少,MetaSpace就有多⼤。 ⽽class space 是元空间内部的。不过设置了Metaspace⼤⼩后还是会有溢出情况。
触发动态年龄判断
JVM参数其他参数看前⼀篇解释
-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520
-XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=10485760
-XX:+UParNewGC -XX:+UConcMarkSweepGC
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc1.log
-XX:MaxTenuringThreshold=15 年龄15
新⽣代我们通过“-XX:NewSize”设置为10MB了 然后其中Eden区是8MB,每个Survivor区是1MB,Java堆总⼤⼩是20MB,⽼年代是10MB,⼤对象必须超过10MB才会直接进⼊⽼年 代
但是我们通过“-XX:MaxTenuringThreshold=15”设置了,只要对象年龄达到15岁才会直接进⼊⽼年代。 ⼀切准备就绪,先看看我们当前的内存分配情况
代码
array1 =new byte[2*1024*1024];
array1 = null;
byte[] array2 =new byte[128*1024];
byte[] array3 =new byte[2*1024*1024];
}
}
当main ⽅法执⾏到第五⾏的时候 给array3分配时新⽣代肯定放不下,前⾯已经放了2m +2m+2m+128k 此时 eden区可分配剩余1m左右
gc⽇志
Java HotSpot(TM) 64-Bit Server VM (25.231-b11) for windows-amd64 JRE (1.8.0_231-b11), built on Oct 5 2019 03:11:30 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 33359028k(19075240k free), swap 35456180k(13369076k free)
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSi ze=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRat io=8 -XX:+UCompresdClassPointers -XX:+UCompresdOops -XX:+UConcMarkSweepGC -XX:-ULargePagesIndividualAllocation -XX:+UPar NewGC
lzg0.100: [GC (Allocation Failure) 0.100: [ParNew: 8130K->642K(9216K), 0.0008885 cs] 8130K->642K(19456K), 0.0010790 cs] [Times: ur=0.00 sys=0. 00, real=0.00 cs]
Heap
par new generation total 9216K, ud 3141K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 30% ud [0x00000000fec00000, 0x00000000fee70c60, 0x00000000ff400000)
from space 1024K, 62% ud [0x00000000ff500000, 0x00000000ff5a08c8, 0x00000000ff600000)
to space 1024K, 0% ud [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
concurrent mark-sweep generation total 10240K, ud 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace ud 3230K, capacity 4496K, committed 4864K, rerved 1056768K
class space ud 350K, capacity 388K, committed 512K, rerved 1048576K
分析
0.100: [GC (Allocation Failure) 0.100: [ParNew: 8130K->642K(9216K), 0.0008885 cs] 8130K->642K(19456K), 0.0010790 cs] [Times: ur=0.00 sys=0. 00, real=0.00 cs]emperor是什么意思
可以看出gc前就已经占⽤了8130k 垃圾回收后还有642k存在
par new generation total 9216K, ud 3141K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 30% ud [0x00000000fec00000, 0x00000000fee70c60, 0x00000000ff400000)
from space 1024K, 62% ud [0x00000000ff500000, 0x00000000ff5a08c8, 0x00000000ff600000)
to space 1024K, 0% ud [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
我们 正常剩余存在的对象应该就是2m+128k左右
由于在128k产⽣后 分配内存失败引起gc 所以 那⼀部分去了 s1 也就是⽇志中的from 后⾯分配的2048k 去了 eden 区。
此时array2的对象 应该是1岁
更改代码
array1 =new byte[2*1024*1024];
array1 = null;
byte[] array2 =new byte[128*1024];
byte[] array3 =new byte[2*1024*1024];
array3=new byte[2*1024*1024];
array3=new byte[2*1024*1024];
array3=new byte[128*1024];
array3=null;
byte[] array4 =new byte[2*1024*1024];
}
}
此时的内存分布图 还没执⾏最后⼀⾏
上⾯代码再次执⾏gc⽇志
0.090: [GC (Allocation Failure) 0.090: [ParNew: 8130K->637K(9216K), 0.0009362 cs] 8130K->637K(19456K), 0.0011031 cs] [Times: ur=0.00 sys=0. 00, real=0.00 cs]
0.092: [GC (Allocation Failure) 0.092: [ParNew: 7150K->366K(9216K), 0.0033473 cs] 7150K->990K(19456K), 0.0033991 cs] [Times: ur=0.00 sys=0. 00, real=0.00 cs]
Heap
par new generation total 9216K, ud 2552K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 26% ud [0x00000000fec00000, 0x00000000fee225d0, 0x00000000ff400000)
from space 1024K, 35% ud [0x00000000ff400000, 0x00000000ff45bb38, 0x00000000ff500000)
to space 1024K, 0% ud [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
concurrent mark-sweep generation total 10240K, ud 623K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace ud 3230K, capacity 4496K, committed 4864K, rerved 1056768K
anti是什么意思class space ud 350K, capacity 388K, committed 512K, rerved 1048576K
从这⾥可以看出 cms 已经有600多k的⽼年代了 这600多k其实就是第⼀次gc的时候进⼊s区的对象,
CMS管理的⽼年代,此时使⽤空间刚好是600多k,证明此时Survivor⾥的对象触发了动态年龄判定规则,虽然没有达到15岁,但是全部进⼊⽼年代了。
接着其实此时会发现Survivor区域中的对象都是存活的,⽽且总⼤⼩超过s区50%了,之前对象年龄都是1岁 此时根据动态年龄判定规则:年龄1+年龄2+年龄n的对象总⼤⼩超过了Survivor区域的50%,年龄n及以上的对象进⼊⽼ 年代。 当然这⾥的对象都是年龄1的,所以⽼年龄进⼊⽼年代了。
再次改变代码
public class Demo2 {
public static void main(String[] args){
byte[] array1 =new byte[2*1024*1024];
array1=new byte[2*1024*1024];
array1=new byte[2*1024*1024];
byte[] array2 =new byte[128*1024];
array2=null;
byte[] array3=new byte[2*1024*1024];
}
}
gc⽇志
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSi ze=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRat io=8 -XX:+UCompresdClassPointers -XX:+UCompresdOops -XX:+UConcMarkSweepGC -XX:-ULargePagesIndividualAllocation -XX:+UPar NewGC
0.119: [GC (Allocation Failure) 0.119: [ParNew: 8130K->659K(9216K), 0.0020862 cs] 8130K->2709K(19456K), 0.0022548 cs] [Times: ur=0.00 sys= 0.00, real=0.00 cs]
Heappls
par new generation total 9216K, ud 3158K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 30% ud [0x00000000fec00000, 0x00000000fee70c60, 0x00000000ff400000)
from space 1024K, 64% ud [0x00000000ff500000, 0x00000000ff5a4cc0, 0x00000000ff600000)
to space 1024K, 0% ud [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
concurrent mark-sweep generation total 10240K, ud 2050K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace ud 3230K, capacity 4496K, committed 4864K, rerved 1056768K
class space ud 350K, capacity 388K, committed 512K, rerved 1048576K
这⾥出现⼀个特点 Young GC过后存活对象放不下Survivor区域,从⽽部分对象会进⼊⽼年代
在gc 的时候会发现 有659k未知对象和2M的数组要放进 Survivor 区 放不下 会有部分对象进⼊⽼年代。
fullGc之⽼年代放不下
JVM参数其他参数看前⼀篇解释
-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520
-XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15
-XX:PretenureSizeThreshold=3145728
-XX:+UParNewGC -XX:+UConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc2.log
上⾯注意⼀点 ⼤对象给的 3M
代码
public class Demo3 {
public static void main(String[] args){
byte[] array1 =new byte[4*1024*1024];
array1=null;
byte[] array2 =new byte[2*1024*1024];
byte[] array3 =new byte[2*1024*1024];
byte[] array4 =new byte[2*1024*1024];
byte[] array5 =new byte[128*1024];
byte[] array6 =new byte[2*1024*1024];
}
}
gc ⽇志
0.130: [GC (Allocation Failure) 0.130: [ParNew (promotion failed): 8130K->8803K(9216K), 0.0034184 cs]0.134: [CMS: 8194K->6774K(10240K), 0.00305 27 cs] 12226K->6774K(19456K), [Metaspace: 3223K->3223K(1056768K)], 0.0070386 cs] [Times: ur=0.00 sys=0.00, real=0.01 cs]
Heap
par new generation total 9216K, ud 2422K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 29% ud [0x00000000fec00000, 0x00000000fee5d898, 0x00000000ff400000)
from space 1024K, 0% ud [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
to space 1024K, 0% ud [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
concurrent mark-sweep generation total 10240K, ud 6774K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace ud 3230K, capacity 4496K, committed 4864K, rerved 1056768K
class space ud 350K, capacity 388K, committed 512K, rerved 1048576K
最后⼀句代码还没执⾏时 gc前
分析