四、GC日志及参数

一、程序添加JVM参数的方式

  • 命令行添加:java [JVM参数] -jar [jar包名]

    • java -XX:+PrintGC -jar jvm-project.jar
      查看GC简要信息
  • idea添加(后续例子用此方式,idea版本2021.2.2、jdk1.8.0_152)

    • Run -> Edit Configurations -> modify options -> java -> Add VM options
      在这里插入图片描述
      在这里插入图片描述
    • 打印效果
      在这里插入图片描述

一、GC跟踪参数

  • 开启GC日志,打印简要GC信息

    • -verbose:gc(-XX:PrintGC):程序执行GC的时候打印GC信息
      • GC:执行一次Young GC(YGC、MinorGC);
      • Allocation Failure:出发GC的原因(分配内存失败);
      • 64289K->1008K:年轻代通过一次YGC,内存占用从64289K降为1008k;
      • 247296K:整堆大小为247296K,此处整堆大小是变化的是由于idea默认设置的堆最大值和最小值不一致(见最大/最小堆参数设置);
      • 0.0013379 secs:此次YGC耗时;在这里插入图片描述
  • 打印GC详细信息

    • -XX:+PrintGCDetails:程序结束时打印堆信息
      • GC/Full GC:GC类型;
      • Allocation Failure、Ergonomics:触发GC的原因;Ergonomics是由于虚拟通过算法估算出GC后年轻代所需空间大于当前剩余空间,所以提前进行一次Full GC;
      • PSYoungGen、ParOldGen、Metaspace:年轻代、老年代、元空间(jdk1.8之前为永久代);
      • 1054K->0K:回收前所占空间->回收后所占空间;
      • (2560K):当前区域(PSYoungGen、ParOldGen、Metaspace)可用总大小(年轻代to survivor区永远为空,不可用);
      • 0.0002245 secs:当前GC所花时间;
      • Times:用户时间、系统时间、实际时间;
      • heap:堆信息
      • total:当前区域总大小;
      • used :当前区域使用大小;
      • [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000):当前内存所在地址,最低边界,当前所在边界(使用到哪),最高边界;
      • eden:年轻代eden区;
      • form:年轻代form surivor区;
      • to:年轻代to surivor区;在这里插入图片描述
  • 以文件形式输出GC日志

    • -Xloggc:C:\temp\gc.log(路径自行调整)在这里插入图片描述
  • 每次GC之后打印堆信息

    • -XX:+PrintHeapAtGC
    • Heap before GC :GC前堆信息;
    • invocations=21 (full 7):调用GC次数(其中Full GC次数);
      在这里插入图片描述
  • 跟踪类加载

    • -XX:+TraceClassLoading;
    • Object为所有类的基类,最先加载;
      在这里插入图片描述

二、堆分配参数

  • 最大堆内存、最小堆内存

    • -Xmx20m -Xms12m
    • 如下代码中JVMObject中每次分配1M空间,JVMTest通过main方法实例化JVMObject,然后分别打印最大内存、空闲内存、总内存,GC日志如下图;
    • 内存总大小分别在第三次GC和第五次GC之后进行了提升,这是由于分配的初始内存为12m,进行三次GC之后,空间不足,JVM对堆内存进行了提升;
    • JVM会将堆内存尽量维持在最小值;
/**
 * @author xiaomu
 * @title: JVMObject
 * @description: JVM测试实体对象
 * @date 2022/8/1110:28
 */
public class JVMObject {

    private byte[] a = null;

    public JVMObject() {
        this.a = new byte[1 * 1024 * 1024];
    }
}
/**
 * @author xiaomu
 * @title: JVMTest
 * @description: JVM测试类
 * @date 2022/8/1014:15
 */
public class JVMTest {

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            JVMObject jvmObject = new JVMObject();
        }

        // 获取最大内存
        System.out.print("Xmx:");
        System.out.println(Runtime.getRuntime().maxMemory()/1024/1024 + "M");

        // 获取空闲内存
        System.out.print("free:");
        System.out.println(Runtime.getRuntime().freeMemory()/1024/1024 + "M");

        // 获取最小内存
        System.out.print("tot:");
        System.out.println(Runtime.getRuntime().totalMemory()/1024/1024 + "M");
    }
}

在这里插入图片描述

  • 分配新生代大小

    • -Xmn1m:给新生代分配5m内存;
    • 4608(新生代可用大小)+ 512(to survivor) = 5120 = 5m;
    • 如下代码中,每次分配3m内存,在main方法中循环分配两次;
    • jvm参数:-Xmx20m -Xms20m -Xmn5m -XX:+PrintGCDetails;
    • 未进行GC且from区占用0老年代占用40%:第一次分配3m直接存入Eden区,第二次分配3m时,年轻代内存不足,将大对象直接放入了老年代;(将 -Xmn设置改小时比如-Xmn1m出现了GC现象且年轻代占比升高,from区保持在90%以上,说明对象未直接进入老年代,原因未知,网上资料也未找到,待查)
/**
 * @author xiaomu
 * @title: JVMObject
 * @description: JVM测试实体对象
 * @date 2022/8/1110:28
 */
public class JVMObject {

    public JVMObject() {
        byte[] a = new byte[3 * 1024 * 1024];
    }
}

/**
 * @author xiaomu
 * @title: JVMTest
 * @description: JVM测试类
 * @date 2022/8/1014:15
 */
public class JVMTest {

    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            JVMObject jvmObject = new JVMObject();
        }
    }
}

在这里插入图片描述

  • 设置老年代与新生代比值

    • -XX:NewRatio=3:老年代 : 新生代 = 3 : 1(默认 老年代 :新生代 = 2 :1);
    • PSYoungGen:2560 + 512 = 3072 = 3m;ParOldGen:9216 = 9m;
    • -XX:MaxTenuringThreshold=15:设置年轻代进入老年代阈值(15);
      在这里插入图片描述
  • 设置两个survivor和Eden的比例

    • -XX:SurvivorRatio=8(默认值):两个survivor与Eden比为2:8,即一个Eden占总新生代的8/10;
    • 测试发现,垃圾回收器会优先保证单个survivor内存 >= 512k;如下图:新生代大小设为16m = 16384k,SurvivorRatio=14,GC日志中,Eden区占比为14336 / 16384 = 0.8750 = 14 / 16;改变SurvivorRatio=15,此时Eden区占比为15360 / 16384 = 0.9375 > 15 / 17 = 0.8824,继续增大SurvivorRatio的值之后,GC日志不再变化始终将survivor大小维持在512k;
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • jdk1.8默认垃圾回收器为UseParallelGC,网上说默认开启了自适应大小策略(-XX:+UseAdaptiveSizePolicy),在默认参数中并没有发现开启该参数,且实际测试中发现是否加该参数并不影响结果;
    • 测试发现from survivor和to survivor区并不总是相等;原因在于Minor GC之后会交换from和to区,此时会根据空间使用情况和GC周期时间开销计算出Eden和servivor的期望大小To_Size = MaxHeapSize - PSOldGen_Size - Eden_Size - From_Size;
/**
 * @author xiaomu
 * @title: JVMObject
 * @description: JVM测试实体对象
 * @date 2022/8/1110:28
 */
public class JVMObject {

    public JVMObject() {
        byte[] a = new byte[1 * 1024 * 1024];
    }
}

/**
 * @author xiaomu
 * @title: JVMTest
 * @description: JVM测试类
 * @date 2022/8/1014:15
 */
public class JVMTest {

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            JVMObject jvmObject = new JVMObject();
        }
    }
}

在这里插入图片描述
在这里插入图片描述

  • OOM时导出堆文件,并指定路径

    • -XX:+HeapDumpOnOutOfMemoryError:OOM时导出堆文件;
    • -XX:HeadDumpPath=c:\temp\oom.dump;
    • oom的复现困难,所以一般设置程序发生oom时将堆文件导出;
/**
 * @author xiaomu
 * @title: JVMTest
 * @description: JVM测试类
 * @date 2022/8/1014:15
 */
public class JVMTest {

    public static void main(String[] args) {
        Vector v=new Vector();
        for (int i = 0; i < 20; i++) {
            v.add(new byte[1 * 1024 * 1024]);
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • OOM时执行脚本

    • -XX:OnOutOfMemoryError=c:\temp\test.bat;
    • 用于发生oom时发送邮件通知相关人员;

三、元空间(永久区)分配参数

  • jdk1.8以前,元空间称为永久区,其内存在虚拟中,1.8之后改为元空间,内存改到本地,默认初始21807104,最大值不设置,直到将本地内存占满;(查看所有jvm默认参数命令:java -XX:+PrintFlagsFinal,查看经过覆盖的:java -XX:+PrintCommandLineFlags -version)
    在这里插入图片描述
  • -XX:MetaspaceSize=10m:设置元空间初始10m;
  • -XX:MaxMetaspaceSize=15m:设置元空间最大15m;
  • 元空间大小决定能存放多少类行;
  • Full GC会回收metaspace,当所需内存超过MaxMetaspaceSize时,报oom:Metaspace;
    • used:加载的类的空间量;
    • capacity:当前分配块的元数据的空间;
    • committed:空间块的数量;
    • reserved:元数据的空间保留(但不一定提交)的量;
      在这里插入图片描述

四、栈分配参数

  • -Xss=10m:设置栈大小为10m;
  • 栈大小决定了函数调用的深度,已经可容纳的局部变量;
  • 栈大小一般为几百k,由于线程独享,栈大小也同时一定程度上决定了程序可分配的线程大小;
/**
 * @author xiaomu
 * @title: JVMTest
 * @description: JVM测试类
 * @date 2022/8/1014:15
 */
public class JVMTest extends ClassLoader{

    private static int deep = 0;

    public static void main(String[] args) {
        try{
            testStackDeep();
        }catch(Throwable e){
            System.out.println("deep of calling = " + deep);
            e.printStackTrace();
        }
    }

    public static void testStackDeep(){
        deep++;
        testStackDeep();
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值