前言
大家都知道,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后,还不能,满足程序的运行,才会逐渐的扩容堆的大小,但也不是直接扩大到最大内存。
- -Xms : 初始堆大小
-XX:InitialHeapSize
的缩写 - -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调优参数整理