0
点赞
收藏
分享

微信扫一扫

不知道这些 JVM 参数,别说你是高级开发

飞空之羽 2022-12-04 阅读 140


前言

大家都知道,jvm在启动的时候,会执行默认的一些参数。一般情况下,这些设置的默认参数应对一些平常的项目也够用了。但是如果项目特别大了,需要增加一下堆内存的大小、或者是系统老是莫明的挂掉,想查看下gc日志来排查一下错误的原因,都需要咱们手动设置这些参数。

 

各个参数介绍

​-verbose:gc​

表示,启动jvm的时候,输出jvm里面的gc信息。格式如下:

[ Full GC 178K -> 99K ( 1984K ), 0.0253877 secs]

解读 :Full GC 就表示执行了一次Full GC的操作,178K 和99K 就表示执行GC前内存容量和执行GC后的内存容量。1984K就表示内存总容量。后面那个是执行本次GC所消耗的时间,单位是秒。
 

​-XX:+PrintGC​

这个打印的GC信息跟上个一样,就不做介绍了。
 

​-XX:+PrintGCDetails​

打印GC的详细信息。格式如下:

–Heap
– def new generation total 13824K, used 11223K [0x27e80000, 0x28d80000, 0x28d80000)
– eden space 12288K, 91% used [0x27e80000, 0x28975f20, 0x28a80000)
– from space 1536K, 0% used [0x28a80000, 0x28a80000, 0x28c00000)
– to space 1536K, 0% used [0x28c00000, 0x28c00000, 0x28d80000)
– tenured generation total 5120K, used 0K [0x28d80000, 0x29280000, 0x34680000)
– the space 5120K, 0% used [0x28d80000, 0x28d80000, 0x28d80200, 0x29280000)
– compacting perm gen total 12288K, used 142K [0x34680000, 0x35280000, 0x38680000)
– the space 12288K, 1% used [0x34680000, 0x346a3a90, 0x346a3c00, 0x35280000)
– ro space 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000)
– rw space 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000

解读:new generation 就是堆内存里面的新生代。total的意思就是一共的,所以后面跟的就是新生代一共的内存大小。used也就是使用了多少内存大小。0x开头的那三个分别代表的是 底边界,当前边界,高边界。也就是新生代这片内存的起始点,当前使用到的地方和最大的内存地点。

eden space 这个通常被翻译成伊甸园区,是在新生代里面的,一些创建的对象都会先被放进这里。后面那个12288K就表示伊甸园区一共的内存大小,91% used,很明显,表示已经使用了百分之多少。后面的那个0x跟上一行的解释一样。

from space 和to space 是幸存者的两个区。也是属于新生代的。他两个区的大小必须是一样的。因为新生代的GC采用的是复制算法,每次只会用到一个幸存区,当一个幸存区满了的时候,把还是活的对象复制到另个幸存区,上个直接清空。这样做就不会产生内存碎片了。

tenured generation 就表示老年代。

compacting perm 表示永久代。由于这两个的格式跟前面我介绍的那个几乎一样,我就不必介绍了。
 

​-XX:+PrintGCTimeStamps​

输出GC的时间戳(以基准时间的形式)。格式如下:

289.556 : [GC [ PSYoungGen : 314113K -> 15937K ( 300928K )] 405513K -> 107901K ( 407680K ), 0.0178568 secs] [ Times : user= 0.06 sys= 0.00 , real= 0.01 secs]
293.271 : [GC [ PSYoungGen : 300865K -> 6577K ( 310720K )] 392829K -> 108873K ( 417472K ), 0.0176464 secs] [ Times : user= 0.06 sys= 0.00 , real= 0.01 secs]

解读:289.556表示从jvm启动到发生垃圾回收所经历的的时间。GC表示这是新生代GC(Minor GC)。PSYoungGen(PS是指Parallel Scavenge)表示新生代使用的是多线程垃圾回收器Parallel Scavenge。314113K->15937K(300928K)]这个跟上面那个GC格式一样,只不过,这个是表示的是新生代,幸存者区。后面那个是整个堆的大小,GC前和GC后的情况。Times这个显而易见,代表GC的所消耗的时间,用户垃圾回收的时间和系统消耗的时间和最终真实的消耗时间。
 

​-XX:+PrintGCDateStamps​

输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
 

​-Xloggc:logs/gc.log​

这个就表示,指定输出gc.log的文件位置。(我这里写的log/gc.log就表示在当前log的目录里,把GC日志写到叫gc.log的文件里。)
 

​-XX:+PrintHeapAtGC​

表示在每次进行 GC 的前后打印出堆的信息(这个打印的基本格式跟上面第二条的基本类似,我也就不比多说了。)
 

​-XX:ErrorFile​

当 JVM 发生致命错误导致崩溃时,会生成一个 ​​hs_err_pid<PID>.log​​​ 这样的文件, 默认情况下, 该文件是生成在工作目录下的:​-XX:ErrorFile=./hs_err_pid<PID>.log​​
 

​-XX:+TraceClassLoading​

监控类的加载。格式如下:

•[Loaded java.lang.Object from shared objects file]
•[Loaded java.io.Serializable from shared objects file]
•[Loaded java.lang.Comparable from shared objects file]
•[Loaded java.lang.CharSequence from shared objects file]
•[Loaded java.lang.String from shared objects file]
•[Loaded java.lang.reflect.GenericDeclaration from shared objects file]
•[Loaded java.lang.reflect.Type from shared objects file]

使用这个参数就能很清楚的看到那些类被加载的情况了。
 

​-XX:+PrintClassHistogram​

跟踪参数。这个按下Ctrl+Break(Pause)后,就会打印一下信息:

num instances   bytes     class name
----------------------------------------------
1: 890617 470266000 [B
2: 890643 21375432 java.util.HashMap$Node
3: 890608 14249728 java.lang.Long
4: 13 8389712 [Ljava.util.HashMap$Node;
5: 2062 371680 [C
6: 463 41904 java.lang.Class

– 分别显示:序号、实例数量、总大小、类型。
这里面那个类型,B和C的其实就是byte和char类型。
 

​-Xmx -Xms​

这个就表示设置堆内存的最大值和最小值。这个设置了最大值和最小值后,jvm启动后,并不会直接让堆内存就扩大到指定的最大数值。而是会先开辟指定的最小堆内存,如果经过数次GC后,还不能,满足程序的运行,才会逐渐的扩容堆的大小,但也不是直接扩大到最大内存。

  1. -Xms : 初始堆大小​​-XX:InitialHeapSize​​的缩写
  2. -Xmx: 最大堆大小​​-XX:MaxHeapSize​​​的缩写
     

​-Xmn​

设置新生代的内存大小。(如果需要进一步细化,初始化大小用-XX:NewSize,最大大小用-XX:MaxNewSize)
 

​-XX:NewRatio​

新生代和老年代的比例。比如:1:4,就是新生代占五分之一。
 

​-XX:SurvivorRatio​

设置两个Survivor区和eden区的比例。比如:2:8 ,就是一个Survivor区占十分之一。
 

​-XX:+HeapDumpPath​

表示指定 dump 文件存储路径, 结合 -XX:+HeapDumpOnOutOfMemoryError 使用。 ​​-XX:HeapDumpPath=./dump.hprof​​  

​-XX:+HeapDumpOnOutOfMemoryError​

发生 OOM 时,导出堆的信息到文件。
 

​-XX:OnOutOfMemoryError​

当系统产生OOM时,执行一个指定的脚本,这个脚本可以是任意功能的。比如生成当前线程的dump文件,或者是发送邮件和重启系统。​​-XX:OnOutOfMemoryError="kill -9 %p"​​  

​-XX:PermSize -XX:MaxPermSize​

JDK1.7及之前版本:设置永久代(方法区)的内存大小和最大值。永久代内存用光也会导致OOM的发生。
 

​-XX: MetaspaceSize -XX: MaxMetaspaceSize​

JDK1.8及之后版本: 调整(元空间)方法区的大小。(MetaspaceSize默认20M)

 

从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存大小有关。当然你也可以通过以下的几个参数对Metaspace进行控制:

  • -XX:MetaspaceSize=N: 这个参数是初始化的Metaspace大小,该值越大触发Metaspace GC的时机就越晚。随着GC的到来,虚拟机会根据实际情况调控Metaspace的大小,可能增加上线也可能降低。在默认情况下,这个值大小根据不同的平台在12M到20M浮动。使用java -XX:+PrintFlagsInitial命令查看本机的初始化参数,-XX:Metaspacesize为21810376B(大约20.8M)。
  • -XX:MaxMetaspaceSize=N: 这个参数用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序。在本机上该参数的默认值为4294967295B(大约4096MB)。
  • -XX:MinMetaspaceFreeRatio=N: 当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存。
  • -XX:MaxMetasaceFreeRatio=N: 当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%。
  • -XX:MaxMetaspaceExpansion=N: Metaspace增长时的最大幅度。在本机上该参数的默认值为5452592B(大约为5MB)。
  • -XX:MinMetaspaceExpansion=N: Metaspace增长时的最小幅度。在本机上该参数的默认值为340784B(大约330KB为)。

​说明​​: 在Java官方的HotSpot 虚拟机中,Java8版本以后,是用元空间来实现的方法区;在Java8之前的版本,则是用永久代实现的方法区;方法区是JVM规范的抽象定义,其具体的实现是通过永久代(JDK1.7及之前)和元空间(JDK1.8及之后)实现的

 

​-Xss​

设置每个线程的堆栈的大小。栈都是每个线程独有一个,所有一般都是几百k的大小。​​等同于-XX:ThreadStackSize​

默认JDK1.4中是256K,JDK1.5+中是1M。 在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右

# 获取当前JVM ThreadStackSize参数的默认值
java -XX:+PrintFlagsFinal -version | grep ThreadStackSize

 

​-XX:+PrintGCApplicationStoppedTime​

打印垃圾回收期间程序暂停的时间
输出形式: Total time for which application threads were stopped: 0.0468229 seconds
 

​-XX:MinHeapFreeRatio​

设置堆空间最小空闲比例。 当堆空间的空闲内存小于这个数值时, JVM 便会扩展堆空间。
 

​-XX:MaxHeapFreeRatio​

设置堆空间的最大空闲比例。 当堆空间的空闲内存大于这个数值时, 便会压缩堆空间, 得到一个较小的堆。
 

​-XX:NewSize​

设置新生代大小。
 

​-XX:NewRatio​

设置老年代与新生代的比例, 它等于老年代大小除以新生代大小。
 

​-XX:SurvivorRatio​

新生代中 Eden 区和 Survivor 区的比例。
 

​-XX:MaxPermSize​

设置最大的持久区大小。
 

​-XX:PermSize​

设置永久区的初始值。
 

​-XX:TargetSurvivorRatio​

设置 Survivor 区的可使用率。 当 Survivor 区的空间使用率达到这个数值时, 它将对象送入老年代。
 

​-XX:+DisableExplicitGC​

这个参数,会导致System.gc()调用变成一个空调用,没有任何作用

System.gc()默认会触发一次Full Gc,如果在代码中不小心调用了System.gc()会导致JVM间歇性的暂停

为什么不推荐使用-XX:+DisableExplicitGC?

有些NIO框架比如Netty框架经常会使用DirectByteBuffer来分配堆外内存,在分配之前会显式的调用System.gc()。如果开启了DisableExplicitGC这个参数,会导致System.gc()调用变成一个空调用,没有任何作用,反而会导致Netty框架无法申请到足够的堆外内存,从而产生java.lang.OutOfMemoryError: Direct buffer memory.

 

jmap -heap PID

# 启动参数
-Xms512M -Xmx512M -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintClassHistogram


jmap -heap 4828

Attaching to process ID 4828, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.171-b11

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
# 空闲堆空间的最小百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 40。如果HeapFreeRatio < MinHeapFreeRatio,则需要进行堆扩容,扩容的时机应该在每次垃圾回收之后。
MinHeapFreeRatio = 0
# 空闲堆空间的最大百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 70。如果HeapFreeRatio > MaxHeapFreeRatio,则需要进行堆缩容,缩容的时机应该在每次垃圾回收之后。
MaxHeapFreeRatio = 100
# JVM 堆空间允许的最大值. jvm参数 -Xms512m
MaxHeapSize = 536870912 (512.0MB)
# JVM 新生代堆空间的默认值. jvm参数 -Xmn128m
NewSize = 178782208 (170.5MB)
# JVM 新生代堆空间允许的最大值
MaxNewSize = 178782208 (170.5MB)
# JVM 老年代堆空间的默认值. 老年代空间 = 堆内存大小 - 年轻代大小
OldSize = 358088704 (341.5MB)
# 新生代(2个Survivor区和Eden区 )与老年代(不包括永久区)的堆空间比值。 表示 老年代:新生代=2:1, 即: 新生代占堆总大小的的1/3(512/3=170.66MB)
NewRatio = 2
# 两个Survivor区和Eden区的堆空间比值为 8,表示 S0 : S1 :Eden = 1:1:8
# Eden:Survivor=8:1. 新生代=Eden + From Survivor + To Survivor
# 即: Eden占8/10, From Survivor=To Survivor=1/10
SurvivorRatio = 8
# JVM 元空间的默认值 jvm参数 -XX:MaxMetaspaceSize
MetaspaceSize = 21807104 (20.796875MB)
# 类指针压缩空间大小, 默认为1G
CompressedClassSpaceSize = 1073741824 (1024.0MB)
# JVM 元空间允许的最大值
MaxMetaspaceSize = 17592186044415 MB
# 在使用 G1 垃圾回收算法时,JVM 会将 Heap 空间分隔为若干个 Region,该参数用来指定每个 Region 空间的大小
# G1区块的大小, 取值为1M至32M. 其取值是要根据最小Heap大小划分出2048个区块
G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
PS Young Generation
# 新生代-伊甸区内存分布
Eden Space:
# Eden区总容量
capacity = 134742016 (128.5MB)
# Eden区已使用
used = 40132832 (38.273651123046875MB)
# Eden区剩余容量
free = 94609184 (90.22634887695312MB)
# Eden区使用比率
29.784942508207685% used
# 新生代-From Survivor
From Space:
capacity = 22020096 (21.0MB)
used = 21206680 (20.224266052246094MB)
free = 813416 (0.7757339477539062MB)
96.3060288202195% used
# 新生代-To Survivor
To Space:
capacity = 20447232 (19.5MB)
used = 0 (0.0MB)
free = 20447232 (19.5MB)
0.0% used
# 老年代
PS Old Generation
capacity = 358088704 (341.5MB)
used = 20568088 (19.615257263183594MB)
free = 337520616 (321.8847427368164MB)
5.743852785705299% used

26215 interned Strings occupying 3125824 bytes.

​​JVM:jmap heap 堆参数分析MinHeapFreeRatio、MaxHeapFreeRatio、MaxHeapSize、NewSize、MaxNewSize​​

 

查看JVM使用的什么垃圾收集器

java -XX:+PrintCommandLineFlags -version

-XX:InitialHeapSize=132165184 -XX:MaxHeapSize=2114642944 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

​​-XX:+UseParallelGC​​: 虚拟机运行在 Server 模式下的默认值, 打开此开关后, 使用 Parallel Scavenge + Serial Old(PS MarkSweep) 的收集器组合进行内存回收

Parallel Scavenge收集器架构中本身有PS MarkSweep收集器来进行老年代收集,并非直接使用了Serial Old收集器,但是这个PS MarkSweep收集器与Serial Old的实现非常接近,所以在官方的许多资料中都是直接以Serial Old代替PS MarkSweep进行讲解,这里笔者也采用这种方式。

 

for(GarbageCollectorMXBean garbageCollectorMXBean : ManagementFactory.getGarbageCollectorMXBeans()) {
System.out.println(garbageCollectorMXBean.getName());
}

/** 输出
PS Scavenge
PS MarkSweep
*/

各个jdk默认的垃圾回收器

  • JDK1.7 默认垃圾收集器Parallel Scavenge(新生代)+ Serial Old(PS MarkSweep)(老年代)
  • JDK1.8 默认垃圾收集器Parallel Scavenge(新生代)+ Serial Old(PS MarkSweep)(老年代)
  • JDK1.9 默认垃圾收集器G1

​​JDK1.8默认使用的垃圾收集器组合​​

# 可查看默认设置收集器类型
java -XX:+PrintCommandLineFlags -version
# 打印GC日志, 根据新生代、老年代名称判断
java -XX:+PrintGCDetails -version

 

JVM参数设置

# %p变量表示当前jvm的pid,用%t表示jvm的启动时间戳. eg: -Xloggc:./logs/gc-%t.log
-Xms1024M -Xmx1024M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -Xloggc:./gc/app_`date +%Y%m%d`.log -XX:HeapDumpPath=./gc/app-dump_`date +%Y%m%d`.hprof -XX:ErrorFile=./gc/app-error_`date +%Y%m%d`.log -Duser.timezone=GMT+08 -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

java <options> -jar XXX.jar --spring.profiles.active=prod


##################################################
# 参考
##################################################
# JDK7
JAVA_MEM_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "

JAVA_DEBUG_OPTS=" -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof -XX:OnOutOfMemoryError=/scripts/restart-myapp.sh "



# JDK8
JAVA_MEM_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:MetaspaceSize=256m -Xss1024m -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "

JAVA_DEBUG_OPTS=" -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof -XX:OnOutOfMemoryError=/scripts/restart-myapp.sh "

堆内存分配

  • JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;
  • JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。
  • 默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
  • 空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。

因此服务器一般设置-Xms、-Xmx 相等以避免在每次GC 后调整堆的大小。

说明:如果-Xmx 不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM,不是Throwable的,无法用try…catch捕捉。

 

打印 JVM 参数

# 打印 JVM 参数初始值(打印出所有XX选项的默认值),初始值可能被修改掉
java -XX:+PrintFlagsInitial

# 打印 JVM 参数最终值(打印出XX选项在运行程序时生效的值)
java -XX:+PrintFlagsFinal 2> nul
java -XX:+PrintFlagsFinal -version |grep CMSInitiatingOccupancyFraction

# 打印被修改过的 JVM 参数
java -XX:+PrintCommandLineFlags 2> nul

在 java 代码里面打印

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
private void printJvm() {
MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();
System.out.println("堆内存信息: " + memorymbean.getHeapMemoryUsage());
System.out.println("方法区内存信息: " + memorymbean.getNonHeapMemoryUsage());

List<String> inputArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
System.out.println("\n####################运行时设置的JVM参数####################");
System.out.println(inputArgs);

System.out.println("\n####################运行时内存情况#######################");
long totle = Runtime.getRuntime().totalMemory();
System.out.println("总的内存量 [" + totle + "]");
long free = Runtime.getRuntime().freeMemory();
System.out.println("空闲的内存量 [" + free + "]");
long max = Runtime.getRuntime().maxMemory();
System.out.println("最大的内存量 [" + max + "]");
}

 

Reference

  • ​​不知道这些 JVM 参数,别说你是高级开发​​
  • ​​根据GC日志判断使用的GC(垃圾收集器)类型​​
  • ​​JVM 垃圾回收算法&垃圾收集器​​
  • ​​JVM 调优 - 系统问题排查​​
  • ​​JVM调优参数整理​​


举报

相关推荐

0 条评论