在本篇文章中,你将掌握最常用的 JVM 参数配置。
Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
与性能有关的最常见实践之一是根据应用程序要求初始化堆内存。如果我们需要指定最小和最大堆大小(推荐显示指定大小),以下参数可以帮助你实现:
- -Xms[unit]
- -Xmx[unit]
heap size 表示要初始化内存的具体大小。
unit 表示要初始化内存的单位。单位为**“ g”** (GB) 、“ m”(MB)、“ k”(KB)。
举个栗子 ,如果我们要为 JVM 分配最小 2 GB 和最大 5 GB 的堆内存大小,我们的参数应该这样来写:
-Xms2G -Xmx5G
根据Oracle 官方文档open in new window,在堆总可用内存配置完成之后,第二大影响因素是为 Young Generation 在堆内存所占的比例。默认情况下,YG 的最小大小为 1310 MB,最大大小为无限制。
一共有两种指定 新生代内存(Young Ceneration)大小的方法:
1.通过-XX:NewSize和-XX:MaxNewSize指定
举个栗子 ,如果我们要为 新生代分配 最小 256m 的内存,最大 1024m 的内存我们的参数应该这样来写:
2.通过-Xmn[unit]指定
举个栗子 ,如果我们要为 新生代分配 256m 的内存(NewSize 与 MaxNewSize 设为一致),我们的参数应该这样来写:
-Xmn256m
GC 调优策略中很重要的一条经验总结是这样说的:
将新对象预留在新生代,由于 Full GC 的成本远高于 Minor GC,因此尽可能将对象分配在新生代是明智的做法,实际项目中根据 GC
日志分析新生代空间大小分配是否合理,适当通过“-Xmn”命令调节新生代大小,最大限度降低新对象直接进入老年代的情况。
另外,你还可以通过 -XX:NewRatio= 来设置老年代与新生代内存的比值。
比如下面的参数就是设置老年代与新生代内存的比值为 1。也就是说老年代和新生代所占比值为 1:1,新生代占整个堆栈的 1/2。
-XX:NewRatio=1
从 Java 8 开始,如果我们没有指定 Metaspace 的大小,随着更多类的创建,虚拟机会耗尽所有可用的系统内存(永久代并不会出现这种情况)。
JDK 1.8 之前永久代还没被彻底移除的时候通常通过下面这些参数来调节方法区大小
相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。
JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是本地内存。
下面是一些常用参数:
修正(参见: issue#1947open in new window):
1、Metaspace 的初始容量并不是 -XX:MetaspaceSize 设置,无论 -XX:MetaspaceSize 配置什么值,对于 64 位 JVM 来说,Metaspace 的初始容量都是 21807104(约 20.8m)。
可以参考 Oracle 官方文档 Other Considerationsopen in new window 中提到的:
Specify a higher value for the option MetaspaceSize to avoid early
garbage collections induced for class metadata. The amount of class
metadata allocated for an application is application-dependent and
general guidelines do not exist for the selection of MetaspaceSize.
The default size of MetaspaceSize is platform-dependent and ranges
from 12 MB to about 20 MB.MetaspaceSize 的默认大小取决于平台,范围从 12 MB 到大约 20 MB。
另外,还可以看一下这个试验:JVM 参数 MetaspaceSize 的误解
2、Metaspace 由于使用不断扩容到-XX:MetaspaceSize参数指定的量,就会发生 FGC,且之后每次 Metaspace 扩容都会发生 Full GC。
也就是说,MetaspaceSize 表示 Metaspace 使用过程中触发 Full GC 的阈值,只对触发起作用。
垃圾搜集器内部是根据变量 _capacity_until_GC来判断 Metaspace 区域是否达到阈值的,初始化代码如下所示:
> void MetaspaceGC::initialize() { // Set the high-water mark to
> MaxMetapaceSize during VM initializaton since // we can't do a GC
> during initialization. _capacity_until_GC = MaxMetaspaceSize; }
相关阅读: issue 更正:MaxMetaspaceSize如果不指定大小的话,不会耗尽内存 #1204
为了提高应用程序的稳定性,选择正确的垃圾收集open in new window算法至关重要。
JVM 具有四种类型的 GC 实现:
可以使用以下参数声明这些实现:-XX:+UseSerialGC
-XX:+UseParallelGC
-XX:+UseParNewGC
-XX:+UseG1GC
有关垃圾回收实施的更多详细信息,请参见此处open in new window。
生产环境上,或者其他要测试 GC 问题的环境上,一定会配置上打印 GC 日志的参数,便于分析 GC 相关的问题。
# 必选
# 打印基本 GC 信息
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
# 打印对象分布
-XX:+PrintTenuringDistribution
# 打印堆数据
-XX:+PrintHeapAtGC
# 打印Reference处理信息
# 强引用/弱引用/软引用/虚引用/finalize 相关的方法
-XX:+PrintReferenceGC
# 打印STW时间
-XX:+PrintGCApplicationStoppedTime
# 可选
# 打印safepoint信息,进入 STW 阶段之前,需要要找到一个合适的 safepoint
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatisticsCount=1
# GC日志输出的文件路径
-Xloggc:/path/to/gc-%t.log
# 开启日志文件分割
-XX:+UseGCLogFileRotation
# 最多分割几个文件,超过之后从头文件开始写
-XX:NumberOfGCLogFiles=14
# 每个文件上限大小,超过就触发分割
-XX:GCLogFileSize=50M
对于大型应用程序来说,面对内存不足错误是非常常见的,这反过来会导致应用程序崩溃。这是一个非常关键的场景,很难通过复制来解决这个问题。
这就是为什么 JVM 提供了一些参数,这些参数将堆内存转储到一个物理文件中,以后可以用来查找泄漏:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./java_pid<pid>.hprof
-XX:OnOutOfMemoryError="< cmd args >;< cmd args >"
-XX:+UseGCOverheadLimit