Java-第十四部分-JVM-运行时参数和GC日志

423 阅读8分钟

JVM全文

参数选项类型

  • 标准参数选项
  1. 比较稳定,以-开头
  2. java -help 查看
  3. server模式,大内存的应用程序,默认使用并行垃圾收集器;client模式,内存较小的桌面应用程序,只能使用serial串行垃圾回收器
  • -X参数选项
  1. 非标准化,功能稳定,-X开头
  2. java -X 查看
  3. -Xint 禁用JIT,只用解释器;-Xcomp所有字节码被编译成本地代码再执行;-Xmixed混合模式、默认模式,内部选择
  4. -Xms<size>=-XX:InitialHeapSize初始堆大小;-Xmx<size>=-XX:MaxHeapSize堆最大大小;-Xss<size>=-XX:ThreadStackSize线程栈大小
  • -XX参数选项
  1. 实验性的,用于开发和调试JVM
  2. Boolean类型,+/-区分
  3. 非Boolean类型,-XX:<option>=<value>
  4. -XX:+PrintFlagsFinal 输出所有参数的名称和默认值,默认不包括Diagnostic/Experimental诊断/实验性参数,配合-XX:+UnlockDiagnosticVMOptions/-XX:+UnlockExperimentalVMOptions使用

添加JVM参数

  • Idea image.png image.png
  • jar
java -XX:+PrintFlagsFinal -jar demo.jar

常用JVM参数选项

打印设置的XX选项

  • -XX:+PrintCommandLineFlags 程序运行前打印出用户手动设置或JVM自动设置的XX选项
  • -XX:+PrintFlagInitial 打印出所有XX选项的默认值
  • -XX:+PrintFlagsFinal 打印出XX选项在运行程序时生效的值
  • -XX:+PrintVMOptions 打印JVM参数

堆栈方法区内存大小设置

  • Xss128k 每个线程的栈大小,等价于-XX:ThreadStackSize=128k

  • -Xms<size>=-XX:InitialHeapSize初始堆大小,初始比最大小,报错
  • -Xmx<size>=-XX:MaxHeapSize堆最大大小
  • -Xss<size>=-XX:ThreadStackSize线程栈大小
  • -Xmn<size> 同时设置年轻代初始大小和最大大小,推荐设置为整个堆的3/8,等价于-XX:NewSize=<size> -XX:MaxNewSize=<size>
  • -XX:SurvivorRatio=8设置Eden和一个Survivor比例,显示定义了,以定义的为准
  • -XX:+UseAdaptiveSizePolicy 自适应内存分配的策略,默认实际上为6:1:1
  1. CMS垃圾回收器默认关闭,新生代比例默认就是8:1:1,实际上也是
  2. G1垃圾回收器默认是开启的,内存空间是灵活分配的 image.png image.png
  • -XX:NewRatio=4 设置老年代和新生代的比例
  • -XX:PretenureSizeThreadshold 设置大于此阈值的对象直接分配在老年代,单位字节;只对Serial、ParNew收集器有效
  • -XX:MaxTenuringThreshold=15 默认值为15,每次MinorGC后,对象年龄+1,大于设置的这个阈值后,进入老年代
  • -XX:PrintTenuringDistribution 每次MinorGC后,都打印当前使用的Survivor中对象的年龄分布
  • -XX:TargetSurvivorRatio 每次MinorGC后,Survivor区域期望的占比

方法区

  • 永久代
  1. -XX:PermSize=256m 设置初始值
  2. -XX:MaxPermSize=256m 最大值
  • 元空间
  1. -XX:MetaspaceSize 初始大小
  2. -XX:MaxMetaspaceSize 最大值
  3. -XX:+UseCompressedOops 压缩对象指针
  4. -XX:+UseCompressedClassPointers 压缩类指针
  5. -XX:+CompressedClassSpaceSize 设置存储类结构的数据(Klass)大小,默认1G
committed(Non-class Metaspace) + committed(Compressed Class Space) <= MaxMetaspaceSize
如果 MaxMetaspaceSize 设置为小于 CompressedClassSpaceSize,则后者会自动减小为
  • 直接内存

-XX:MaxDirectMemorySize 大小,默认与java堆一样

OutOfMemory相关

  • -XX:+HeapDumpOnoutOfMemoryError 出现OOM时,输出dump文件
  • -XX:+HeapDumpBeforeFullGC 出现FullGC之前,输出dump文件
  • -XX:HeapDumpPath=/Users/mzx/Desktop/java/jvm/test2.hprof 输出dump文件的路径,文件名,如果存在,会自动改文件名 image.png
  • -XX:OnOutOfMemoryError 指定一个可执行程序或者脚本的路径,发生OOM时,执行脚本

垃圾回收器相关

  • 垃圾回收器
  • Serial回收器 -XX:+UseSerialGC,同时指定,单核CPU
  • ParNew回收器
  1. -XX:+UseParNewGC指定垃圾收集器,jdk9及以后弃用
  2. -XX:ParallelGCThreads=2 设置线程数量,默认开启和CPU数量相同
  • Parallel回收器,JDK8默认
  1. -XX:UseParallelGC,互相激活,默认,跟老年代是一组搭配
  2. -XX:UseParallelOldGC
  3. -XX:ParallelGCThreads 设置年轻代线程数量,默认开启和CPU数量相同;CPU数量小于8,默认为CPU数量;CPU数量大于8,值为3+[5*count]/8
  4. -XX:MaxGCPauseMills,最大停顿时间,STW的时间,自动调整java堆的大小和分配;暂停时间越长->比例越大,影响下面的参数;暂时时间越短->越频繁->总时间越长->吞吐量下降(某个时间段)
  5. -XX:GCTimeRatio,垃圾回收时间占总时间的比例,= 1 / (N + 1),默认值N=99;
  6. -XX:+UseAdaptiveSizePolicy,自适应调节,年轻代大小,Eden和s1/s0的比例,晋升老年代的阈值自动调整
  • CMS回收器
  1. -XX:+UseConcMarkSweepGC 启用CMS,自动打开ParNew,采用ParNew+CMS+Serial Old
  2. -XX:CMSInitiatingOccupancyFraction=92,堆内存使用阈值,默认=-1,降低Full GC执行次数;如果内存增长很快,设置较小的值,给用户线程留出足够空间,避免频繁触发老年代串行收集器;内存增长很慢,设一个较大的值,降低CMS出发频率,减少老年代回收可以较明显地改善应用程序性能
  3. -XX:+UseCMSCompactAtFullCollection,执行完CMS后,进行压缩整理
  4. -XX:CMSFullGCsBeforeCompaction=0,执行多少次CMS Full GC后,进行压缩整理
  5. -XX:ParallelCMSThreads,设置CMS的线程数量,默认值为CPU个数,结果为(ParallelGCThreads+3)/4 image.png
  • G1回收器
  1. -XX:+UseG1GC,手动设置
  2. -XX:G1HeapRegionSize=16m 设置每个Region的大小,值是2的幂,范围是1~32MB,目标根据最小的Java对大小,划分出2048个区域,堆空间的大小为2G~64G,默认是对内存的1/2000
  3. -XX:MaxGCPauseMillis,设置期望达到的最大GC停顿时间指标,不一定能达到,默认200ms
  4. -XX:ParallelGCThreads,设置STW时GC线程数的值,最多8
  5. -XX:ConcGCThreads,设置并发标记的线程数,将n设置为并行垃圾回收线程数 占ParallelGCThread的1/4左右
  6. -XX:InitiatingHeapOccupancyPercent,设置触发并发GC周期的Java堆占用率阈值,超过此值,触发GC,默认为45
  7. -XX:G1NewSizePercent -XX:G1MaxNewSizePercent 新生代占整个堆内存的最小百分比(默认5%)、最大百分比(默认60%)
  8. -XX:G1ReservePercent=10 保留内存区域,防止to区溢出 image.png

GC日志相关

  • -XX:+PrintGC,输出GC日志,类似 -verbose:gc image.png
  • -XX:+PrintGCDetails,输出GC的详细日志,进程退出时输出当前内存各区域的分配情况 image.png
  • -XX:+PrintGCTimeStamps,不能独立使用,输出GC的时间戳(以基准时间的形式,JVM启动以来到现在的时间)0.389 image.png
  • -XX:+PrintGCDateStamps,不能独立使用,输出GC的时间戳(以日期的形式)2021-10-15T19:07:13.682+0800 image.png
  • -XX:+PrintHeapAtGC,进行GC的前后打印出堆的信息 image.png
  • -Xloggc:/Users/apple/Desktop/java/jvm/logs/gc.log 日志文件的输出,配合上面的使用
  • -XX:+PrintGCApplicationStoppedTime 打印GC时线程停顿时间 image.png
  • 其他参数 image.png

其他

image.png

通过代码获取参数

  • 堆内存
//虚拟机中堆初始内存总量 *64为系统内存大小
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024; 
//虚拟机中堆最大内存,java试图使用的最大堆内存 *4为系统内存大小
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
  • ManagementFactory
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage usage = memoryMXBean.getHeapMemoryUsage();
//Init Heap
System.out.println(usage.getInit() / 1024 / 1024);
//Max Heap
System.out.println(usage.getMax() / 1024 / 1024);
//Use Heap
System.out.println(usage.getUsed() / 1024 / 1024);
//Heap Memory Usage
System.out.println(memoryMXBean.getHeapMemoryUsage());
//Non-Heap Memory Usage
System.out.println(memoryMXBean.getNonHeapMemoryUsage());
//当前堆内存大小
System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024);
//空闲堆内存大小
System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024);
//最大可用堆内存
System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024);

GC日志

  • SerialGC image.png
  • CMS image.png

GC分类

  • 部分收集,PartialGC,不是完整收集整个java堆
  1. 新生代收集,MinorGC/YoungGC,只是新生代Eden/From/To的收集
  2. 老年代收集,MajorGC/OldGC, 只是老年代的收集;只有CMS会有单独收集老年代的行为;很多时候会和FullGC混淆使用;在进行老年代收集之前,通常会进行一次新生代收集
  3. 混合收集,MixedGC,收集整个新生代以及部分老年代的垃圾收集;只有G1有
  • 整堆收集,FullGC,收集java堆和方法区的垃圾堆
  1. 老年代、方法区空间不足
  2. 显示调用System.gc()
  3. MinorGC进入老年代的平均大小大于老年代可用内存
  4. 大对象直接进去老年代,而老年代可用空间不足

GC日志结构

image.png

  • GC开始到结束的时间
  1. user 进程执行用户态代码时间
  2. sys 进程在内核态消耗的cpu时间,在内核执行系统调用或等待系统事件所使用的时间
  3. real 程序从开始到结束的时钟时间
  4. real > user + sys IO负载非常重,CPU不够用
  • YGC
2021-12-14T14:32:59.957+0800: //日志打印时间
12.565:  //程序运行到现在经过的时间
[GC (Allocation Failure) //GC的原因,新生代没有足够的区域分配对象
[PSYoungGen: //使用的垃圾回收器 Parallel Scanvenge垃圾回收器
25600K->4084K(29696K)] //执行垃圾回收之前 -> 执行垃圾回收之后,括号中为新生代的总大小 9/10 减去一份to区
25600K->24443K(98304K), //java堆总大小的变化 新生代9/10 + 老年代,刚开始还没有使用老年代,所以25600k相等
0.0164968 secs] //整个GC花费的时间
[Times: user=0.02 sys=0.01, real=0.02 secs]
  • FullGC
2021-12-14T14:33:28.055+0800: 
40.663: 
[Full GC (Ergonomics) //系统自适应导致的GC (System)调用System.gc()触发的gc
[PSYoungGen: 25600K->2494K(29696K)] //新生代
[ParOldGen: 46608K->68493K(68608K)] //老年代
72208K->70988K(98304K), //总大小
[Metaspace: 3777K->3777K(1056768K)], //元空间
0.0369101 secs] 
[Times: user=0.07 sys=0.02, real=0.04 secs] 

日志工具

java -jar gcviewer-1.36.jar

案例

延迟满足

遵守时间的价值

保有好奇心

感谢康师傅