目录
写在前面
1、使用jmap了解系统运行时的内存区域
2、使用jmap了解系统运行时的对象分布
3、使用jmap生成堆内存转储快照
4、使用jhat在浏览器中分析堆转出快照
5、案例实战
5.1、模拟代码的JVM参数设置
5.2、示例程序
5.3、通过jstat观察程序的运行状态
5.4、对JVM性能进行优化
写在前面
jmap和jhat可以帮助我们观察线上JVM中的对象分布,了解到你的系统平时运行过程中,到底哪些对象占据了主角位置,他们占据了多少内存空间。
1、使用jmap了解系统运行时的内存区域
2、使用jmap了解系统运行时的对象分布
他会按照各种对象占用内存空间的大小降序排列,把占用内存最多的对象放在最上面。
3、使用jmap生成堆内存转储快照
jmap -dump:live,format=b,file=dump.hprof PID
4、使用jhat在浏览器中分析堆转出快照
jhat dump.hprof -port 7000
5、案例实战
5.1、模拟代码的JVM参数设置
接着我们会用一段程序来模拟出那种频繁Full GC的一个场景,此时JVM参数如下所示:
-XX:NewSize=104857600 -XX:MaxNewSize=104857600 -XX:InitialHeapSize=209715200
-XX:MaxHeapSize=209715200 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15
-XX:PretenureSizeThreshold=20971520 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log
“-XX:PretenureSizeThreshold”,把大对象阈值修改为了20MB,避免我们程序里分配的大对象直接进入老年代。
5.2、示例程序
public class Demo {
public static void main(String[] args) throws Exception {
Thread.sleep(30000);
while(true) {
loadData();
}
}
public static void loadData() throws InterruptedException {
byte[] data = null;
for (int i = 0; i < 4; i++) {
data = new byte[10 * 1024 * 1024];
}
data = null;
byte[] data1 = new byte[10 * 1024 * 1024];
byte[] data2 = new byte[10 * 1024 * 1024];
byte[] data3 = new byte[10 * 1024 * 1024];
data3 = new byte[10 * 1024 * 1024];
Thread.sleep(1000);
}
}
大概意思其实就是,每秒钟都会执行一次loadData()方法,他会分配4个10MB的数组,但是都立马成为垃圾,但是会有data1和data2 两个10MB的数组是被变量引用必须存活的,此时Eden区已经占用了六七十MB空间了,接着是data3变量依次指向了两个10MB的数 组,这是为了在1s内触发Young GC的。
5.3、通过jstat观察程序的运行状态
程序运行起来之后,突然在一秒内就发生了一次Young GC,这是为什么呢?
然后我们明显看到在OU中多出来了30MB左右的对象,因此可以确定,在这次Young GC的时候,有30MB的对象存活了,此时因为Survivor区域放不下,所以直接进入老年代了。
很明显每秒会发生一次Young GC,都会导致20MB~30MB左右的对象进入老年代,因为每次Young GC都会存活下来这么多对象,但是Survivor区域是放不下的,所以都会直接进入老年代。
对于YoungGC和FullGC的耗时?
18次Young GC,结果耗费了170毫秒,平均下来一次Young GC要5毫秒左右。但是9次Full GC才耗费10毫秒,平均下来一次Full GC才耗费1毫秒。这是为什么呢?
5.4、对JVM性能进行优化
-XX:NewSize=209715200 -XX:MaxNewSize=209715200 -XX:InitialHeapSize=31457280
-XX:MaxHeapSize=314572800 -XX:SurvivorRatio=2 -XX:MaxTenuringThreshold=15
-XX:PretenureSizeThreshold=20971520 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log
在上述截图里,大家可以清晰看到,每秒的Young gC过后,都会有20MB左右的存活对象进入Survivor,但是每个Survivor区都是50MB的大小,因此可以轻松容纳,而且一般不会过50%的动态年龄判定的阈值。