一、摘要
在上篇文章中,我们详细介绍了 JVM 的内存布局。
今天这篇文章,并结合之前的介绍知识,一起了解一下 JVM 内存相关的调优参数。
二、内存设置相关的命令
所有内存溢出的问题,除了代码可能存在问题以外,更直观的问题是内存空间不足,如何通过参数来控制各区域的内存大小呢?
我们先来看一张图。
相关的常用控制参数介绍!
2.1、堆内存大小相关参数设置
1)-Xms
设置堆的最小空间大小,此值必须是 1024 的倍数且大于 1 MB。附加字母 k 或 k 表示千字节,m 或 m 表示兆字节,g 或 g 表示千兆字节,其它命令参数同理。比如-Xms1024m
,表示堆的最小内存为1024M
,默认值为物理内存的1/64
。
2)-Xmx
设置堆的最大空间大小,此值必须是 1024 的倍数且大于 2 MB。比如-Xmx2048m
,表示堆最大内存为2G
,默认值为物理内存的1/4
。
对于服务器部署,-Xms
和-Xmx
通常建议设置为相同的值,以避免堆的内存空间频繁扩缩。
3)-XX:+HeapDumpOnOutOfMemoryError
表示可以让虚拟机在出现内存溢出异常时 Dump 出当前的堆内存转储快照
2.2、年轻代内存大小相关参数设置
1)-XX:NewSize
设置年轻代的最小空间大小,比如-XX:NewSize=256m
,表示年轻代的最小内存为256M
。
GC 在这个区域比在其他区域执行的频率更高,如果年轻一代的设置太小,那么将进行大量的小频率 GCs。如果设置太大,那么会执行完整的GCs,这可能需要很长时间才能完成。Oracle 建议将年轻一代的大小保持在堆总大小的一半到四分之一之间。同时,该值需要小于-Xms
的值。
2)-XX:MaxNewSize
设置年轻代的最大空间大小,比如-XX:MaxNewSize=512m
,表示年轻代的最大内存为512M
。
3)-Xmn
设置年轻代堆的初始大小和最大大小,比如-Xmn128m
,表示年轻代的初始大小和最大大小为128M
。
这个参数是对-XX:newSize
、-XX:MaxnewSize
两个参数同时进行配置,虽然会很方便,但需要注意的是这个参数是在 JDK1.4 版本以后才加入的,低于此版本无法使用。
没有直接设置老年代的参数,但是可以设置堆空间大小和年轻代空间大小两个参数来间接控制,公式如下:
老年代空间大小 = 堆空间大小 - 年轻代空间大小
2.3、比例方式相关参数设置
1)-XX:NewRatio
设置年轻代和老年代大小之间的比例,默认值是-XX:NewRatio=2
,表示Young : Old = 1 : 2
。
2)-XX:SurvivorRatio
设置 Eden 空间大小和 Survivor 空间大小之间的比例,默认值是-XX:SurvivorRatio=8
,表示Eden : from : to = 8 : 1 : 1
。
3)-XX:MinHeapFreeRatio
设置 GC 事件后允许的最小可用堆空间百分比(0到100),如果可用堆空间低于此值,则堆将被扩展。默认情况下,此参数为-XX:MinHeapFreeRatio=40
,表示40%
。
4)-XX:MaxHeapFreeRatio
设置 GC 事件后允许的最大可用堆空间百分比(0到100)。如果可用堆空间高于此值,则堆将被缩小。默认情况下,此参数为-XX:MaxHeapFreeRatio=70
,表示70%
。
2.4、非堆区相关参数设置
1)-XX:PermSize
设置永久代的最小空间大小,比如-XX:PermSize=256m
,表示永久代的最小内存为256M
,默认值为物理内存的1/64
。
2)-XX:MaxPermSize
置永久代的最大空间大小,比如-XX:MaxPermSize=512m
,表示永久代的最大内存为512M
,默认值为物理内存的1/4
。
值得注意的是,-XX:PermSize
和-XX:MaxPermSize
这两个参数,在 JDK1.7 及以前的版本中有效,在 JDK1.8 中已经被弃用,被-XX:MetaspaceSize
和-XX:MaxMetaspaceSize
两个参数取代。
2.5、栈内存相关参数设置
1)-Xss
设置每个线程的栈大小,比如-Xss1024k
,表示每个线程的堆栈空间大小为1024KB
,通常不需要我们调整设置,默认值取决于平台:
- Linux/ARM (32-bit):320 KB
- Linux/i386 (32-bit):320 KB
- Linux/x64 (64-bit):1024 KB
- OS X (64-bit):1024 KB
- Oracle Solaris/i386 (32-bit):320 KB
- Oracle Solaris/x64 (64-bit):1024 KB
2)-Xoss
设置每个线程中的本地方法栈大小,比如-Xoss128k
,表示每个线程中的本地方法栈大小为128KB
,不过 HotSpot 并不区分虚拟机栈和本地方法栈,因此对于 HotSpot 来说这个参数是无效的。
2.6、堆外内存相关参数设置
1)-XX:MaxDirectMemorySize
此参数的含义是通过Direct ByteBuffer
方式分配的最大堆外内存大小。比如-XX:MaxDirectMemorySize=60m
,表示堆外最大内存不能超过60M
,如果没有设置,默认是 0,JVM 会自动申请内存的大小,最大大小受限于-Xmx
值。
三、内存溢出的几种场景
在上文中,我们介绍了 JVM 内存结构以及可能会发生的异常状况,下面我们一起来复现一下几种常见的内存溢出现象。
3.1、堆溢出
堆溢出测试类如下。
/**
* 虚拟机参数: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOMTest {
public static void main(String[] args) {
List<HeapOOMTest> list = new ArrayList<>();
while (true){
list.add(new HeapOOMTest());
}
}
}
在 IDEA 中设置 JVM 相关的参数。
运行后输出结果如下:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid21886.hprof ...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList