VisualVM工具Visual GC界面各参数详解

简介

网上也没有看到一个比较完整的介绍VisualVM工具Visual GC界面各种参数的文章,就想着写一个。
Visual GC界面除了查询GC活动之外,还可以查看堆、元空间区域的内存使用情况,并且能看到各区域内存使用的趋势变化。这个界面一共分为三块区域,SpacesGraphsHistogram,都可供勾选是否显示。
本文所说的最大可用内存是指所在区域整个最大允许使用的(包含已经被对象占用的内存),不是指的当前内存减掉已经被对象占用的内存,当前可用内存同理。比如给堆配置了最大10G,然后里面已经有5个G被占用了,那这个最大可用内存还是10G。

  1. Spaces
    Spaces区域用来展示老年代、新生代(伊甸园区、S0区、S1区)、元空间的内存使用情况,主要包含虚线单元格实线单元格,这些区域的内存被占用部分则会使用颜色来填充。虚线单元格代表了最大可用内存,实线代表了当前可用内存,颜色填充的代表的是已被使用的内存
    一般情况下,没有配置-Xms-Xmx-Xmn以及其他一些jvm参数的话,老年代等区域的最大可用内存在项目启动的时候就确定了,但是当前可用内存是会变化的,如果当前可用内存被占满(这段话不考虑GC的情况),那当前可用内存就会变大,但是不会超过最大可用内存,变大了实线单元格就会增多。
  2. Graphs
    Graphs区域用来展示一些统计图表,主要有GC活动,各区域的内存使用趋势等,具体可以看下面第一张图;
  3. Histogram
    Histogram区域是柱状图,主要用来展示S(幸存者)区各对象年龄的分布情况。java8如果不配置jvm参数,默认是不支持显示的,当你配置了使用G1GC的时候,就可以显示了。

随便在一个目录新建一个T.java文件

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class T {
    public static final List<byte[]> list = new ArrayList<>();

    public static void main(String[] args) {
        test();
    }

    private static void test() {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("please enter:");
            if (scanner.hasNext()) {
                String input = scanner.nextLine();
                if (input.equalsIgnoreCase("exit")) {
                    System.out.println("exit...");
                    break;
                }
                try {
                    String[] s = input.split("\\*");
                    final int count = Integer.parseInt(s[0]);
                    final int sizeM = Integer.parseInt(s[1]);
                    if (sizeM > 2047) {
                        System.out.println("The maximum value cannot exceed 2047");
                        continue;
                    }
                    for (int i = 0; i < count; i++) {
                        list.add(new byte[1024 * 1024 * sizeM]);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        scanner.close();
    }
}

之所以判断sizeM > 2047可以看new byte[]数组的时候发生NegativeArraySizeException异常

Spaces和Graphs区域参数介绍:

启动脚本:
使用java命令启动,初始堆大小5G,最大堆大小10G,最大元空间大小32m,类路径当前目录,运行T.class文件

java -Xms5G -Xmx10G -XX:MaxMetaspaceSize=32m -cp . T

在默认情况下,新生代占整个堆空间的1/3,老年代占整个堆空间的2/3。这里最大10G,也就是说老年代大小为10*(2/3)=6.666666 显示0.667G,新生代大小10-0.667=3.333。可以发现伊甸园区显示的最大是3.332GEdenS0S1的加起来内存超过了3.33G,这跟我们知道的8:1:1不一致,而且大于了新生代总的内存,针对这个也做了一下验证,可以看后面。
参数介绍图
在这里插入图片描述
多做几次操作之后,S0S1当前可用内存会不一致。一般来说都是一样的
在这里插入图片描述
多加入一些对象到Eden区,然后等待晋升到Old老年代,可以发现,当Old满了之后,Eden区还有空间,继续塞入对象后报了OOM堆溢出错误。
在这里插入图片描述
为了验证新生代内存占比的问题,改一下参数启动,这次加入-Xmn5g给新生代配置5GEdenS0S1的比例配置为8:1:1。正常来说,Eden区应该是最大占用5*0.8=4GS0S1最大各占用0.5G也就是512M,两个幸存者区最大都是没有问题的。Eden区最大比实际能给的多了1G
为了验证,一直往Eden区加入对象,经过多次塞入对象后实际使用内存达到了4G,和当前可用内存一致后,没有进行扩容操作,反而后续对象都直接加入到了Old老年代。所以说,对于新生代来说,Eden最大可用内存显示的是整个新生代的内存大小,并不是Eden区自身允许使用的最大内存。

java -Xms5G -Xmx10G -XX:MaxMetaspaceSize=32m -Xmn5g -XX:SurvivorRatio=8 -cp . T

在这里插入图片描述
Graphs区域几个内存区域的图里面,还可以单击右键,勾选Show Reserved Space(显示保留空间),勾选之后,y轴就会从当前可用内存变成最大可用内存
在这里插入图片描述

Histogram区域参数介绍

如果用上面的启动脚本启动项目,会显示不支持当前JVM(Not supported for this VJM.),所以需要改一下启动脚本。这里相比前面多了-XX:+UseG1GC,代表使用G1垃圾收集器,配置了这个参数之后,有些参数是会失效的,具体可以搜一下或者自己验证一下。然后还加上了一个-XX:MaxTenuringThreshold=10代表最大晋升阈值为10,默认是15。如果设置超过15的值会报错无法启动的。

java -Xms5G -Xmx10G -XX:+UseG1GC -XX:MaxTenuringThreshold=10 -cp . T

使用G1垃圾收集器的GC界面截图:
在这里插入图片描述
参数介绍图
在这里插入图片描述
上面的参数除了Desired Survivor Size外都挺容易理解的。想深入了解这个参数可以参考其他人的文章:关于gc日志中Desired Survivor的疑问和对象晋升老年代的小结。也可以自己配置一个-XX:TargetSurvivorRatio(默认50,也就是50%)来测试一下。
下面是文心一言对-XX:TargetSurvivorRatio的解释:

定义:-XX:TargetSurvivorRatio 是一个JVM参数,用于设置Survivor区的目标使用率。
用途:该参数帮助JVM在每次Minor GC(年轻代垃圾收集)后,根据Survivor区的实际使用情况和目标使用率来调整对象的晋升策略。如果Survivor区中的对象大小超过了根据目标使用率计算出的期望大小,JVM可能会降低Tenuring Threshold(晋升阈值),以便将更多对象提前晋升到老年代,从而释放Survivor区的空间。

posted @ 2024-12-25 11:00  西瓜当冬瓜  阅读(19)  评论(0)    收藏  举报  来源