JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容

时间:2022-10-05 14:36:26

测试代码如下:

  @Test
public void testPrintGcDetail(){
HashMap<String, List> gcMap = new HashMap<>(999999);
for(int i=0;i<999999;i++){
List<String> gcList = new LinkedList<String>();
gcMap.put(String.valueOf(i),gcList);
}
System.out.println("over");
}

JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容

VM options: -Xmx10m -Xms5m -XX:+PrintGCDetails   (注意空格)

-Xmx10m -Xms5m:最大分配堆空间为10m,最小分配堆空间为5m

-XX:+PrintGCDetails: 打印GC详细信息

测试运行结果:

JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容

JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容

结果:GC(Allocation Failure): minior GC 尝试分配失败,一共尝试了11次;

Full GC (Ergonomics):full GC 全局GC,前面几次正常gc回收,最后回收太频繁了,导致了不给回收了;

最后打印了详细的堆空间占用信息:

YoungGen 总共 2048k(2m)空间,ParOldGen 总共4096k(4m)空间   Metaspace 总共 5447k(5.5M)

YounGen 区域内 eden:from = 1:1 官方默认是 8:1(官方认为最优)

eden 区使用率 96% parOldGen 使用率 30%

调整Xms 到 10m ,使xmx和xms一样大,观察打印结果

JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容

对比发现,PSYounGen 区域总空间大小没有改变,而ParOldGen 分配了更多的空间至 7168k

而且实际运行中发现,第二次gc程序报错的时间比第一次要迟(第一次运行了0.873s   第二次运行了1.344s),说明xms的设置过小,导致gc一直在做。

官方给的解释:

Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded
Cause: The detail message "GC overhead limit exceeded" indicates that the garbage collector is running all the time and Java program is making very slow progress. After a garbage collection, if the Java process is spending more than approximately 98% of its time doing garbage collection and if it is recovering less than 2% of the heap and has been doing so far the last 5 (compile time constant) consecutive garbage collections, then a java.lang.OutOfMemoryError is thrown. This exception is typically thrown because the amount of live data barely fits into the Java heap having little free space for new allocations.
Action: Increase the heap size. The java.lang.OutOfMemoryError exception for GC Overhead limit exceeded can be turned off with the command line flag -XX:-UseGCOverheadLimit.

VM花费了98%的时间进行垃圾回收,而只得到2%可用的内存,这样是不被允许的,除非加上 -XX:-UseGCOverheadLimit

然后我就区加了这个参数,果然。。。成功地报了堆内存溢出的错误

JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容

增加

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=导出的路径   来查看 详细信息,再次运行代码,会生成hprof文件,需要用到mat工具来查询详细信息

JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容


最终调整: -Xmx200m -Xms100m -XX:+PrintGCDetails

JVM 调优测试 之 故意分配小的堆空间,观察gc回收打印的内容

从图上可以看,正常走完了程序,然后看明细数据:

PSYoungGen  55296k,eden 512000k,from 4096k      eden:from = 12.5

根据占用比计算,一共花费了 46173k+87447k=133620k(约130m) 堆空间,5385k元空间(jdk1.8以后的永久代不在堆内,而转移到直接内存中了)

总结:在设置堆空间时候,xms不要设置太小,不然会一直gc gc gc(即使你的xmx设置的足够大),导致程序出错。当然xmx也不能太小。