JVM 内存调优
JVM 内存调优学习记录
堆内存中分配两个区: 新生代, 老年代
新生代(PSYoungGen): 创建对象,先存放在新生代
新生代包含: eden、from、to
老年代(ParOldGen):如果对象频繁的使用,对象放入到老年代
对象进入老年代后,如果不再频繁使用了,不会回到新生代
当内存空间紧张时会触发 Full GC 对老年代空间进行垃圾回收,针对老年代空间进行GC操作.
JVM 参数调优
- 堆内存初始值与堆内存最大值一定要保持一致(原因: 减少垃圾回收机制次数)
参数配置
参数 | 描述 |
---|---|
-XX:+PrintGC | 每次触发GC的时候打印相关日志 |
-XX:+UseSerialGC | 串行回收 |
-XX:+PrintGCDetails | 更详细的GC日志 |
-Xms | 堆初始值 |
-Xmx | 堆最大可用值 |
-Xmn | 新生代堆最大可用值 |
-XX:SurvivorRatio | 用来设置新生代中eden空间和from/to空间的比例. |
-XX:NewRatio | 设置新生代和老年代内存比例 |
-Xss | 设置每个线程的堆栈大小 |
栈内存溢出示例
描述
栈溢出主要是因为递归调用到达了零界点 ,导致系统内存移除.
解决方法
# 添加vm arg 参数
# 调整深度
-Xss 10m
- 栈内存溢出代码示例
public class Main {
private static int count = 0;
public static void getCount() {
try {
count++;
getCount();
} catch (Throwable e) {
System.err.println("递归次数: " + count);
e.printStackTrace();
}
}
public static void main(String[] args) {
getCount();
}
}
错误信息
递归次数: 17794
java.lang.StackOverflowError
...(以下错误信息省略)
- 设置 JVM 参数: -Xss10m
错误信息
通过修改 JVM 参数 -Xss 后, 递归调用的次数明显增多.
递归次数: 614518
java.lang.StackOverflowError
...(以下错误信息省略)
堆内存参数配置
最小堆内存5M 最大堆内存. 最终 GC 两次
-XX:+PrintGCDetails 打印详细的 GC 信息
-Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
测试一
- 设置堆的初始值为 5m, 最大值为 20m -Xms5m -Xmx20m.
# JVM 参数配置
-Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
- 示例代码
public static void main(String[] args) throws InterruptedException {
byte[] me = new byte[1 * 1024 *1024];
System.out.println("分配了 1M 内存");
byte[] byte02 = new byte[4 * 1024 *1024];
System.out.println("分配了 4M 内存");
}
- 执行结果
设置堆的初始值为 5m, 最大值为 20m. 此时垃圾回收机制回收了两次
-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
Connected to the target VM, address: '127.0.0.1:58563', transport: 'socket'
[GC (Allocation Failure) [DefNew: 1141K->192K(1856K), 0.0023176 secs] 1141K->716K(5952K), 0.0023745 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
分配了 1M 内存
[GC (Allocation Failure) [DefNew: 1247K->0K(1856K), 0.0016138 secs][Tenured: 1740K->1740K(4096K), 0.0021735 secs] 1771K->1740K(5952K), [Metaspace: 2908K->2908K(1056768K)], 0.0038473 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
分配了 4M 内存
[GC (Allocation Failure) [DefNew: 32K->0K(1920K), 0.0003316 secs][Tenured: 5836K->5836K(8196K), 0.0034514 secs] 5869K->5836K(10116K), [Metaspace: 2908K->2908K(1056768K)], 0.0039188 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Disconnected from the target VM, address: '127.0.0.1:58563', transport: 'socket'
分配了 4M 内存
Heap
def new generation total 4480K, used 81K [0x00000000fec00000, 0x00000000ff0d0000, 0x00000000ff2a0000)
eden space 4032K, 2% used [0x00000000fec00000, 0x00000000fec14468, 0x00000000feff0000)
from space 448K, 0% used [0x00000000feff0000, 0x00000000feff0000, 0x00000000ff060000)
to space 448K, 0% used [0x00000000ff060000, 0x00000000ff060000, 0x00000000ff0d0000)
tenured generation total 13696K, used 10956K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000)
the space 13696K, 80% used [0x00000000ff2a0000, 0x00000000ffd533d0, 0x00000000ffd53400, 0x0000000100000000)
Metaspace used 2915K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 310K, capacity 386K, committed 512K, reserved 1048576K
测试二
- 设置堆的初始值为 5m, 最大值为 20m -Xms5m -Xmx20m.
# JVM 参数配置
-Xms20m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
- 示例代码
public static void main(String[] args) throws InterruptedException {
byte[] me = new byte[1 * 1024 *1024];
System.out.println("分配了 1M 内存");
byte[] byte02 = new byte[4 * 1024 *1024];
System.out.println("分配了 4M 内存");
}
- 执行结果
设置堆的初始值为 20m, 最大值为 20m. 此时垃圾回收机制回收了一次
# 初始值与最大值一致, 最终 GC 一次
-XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
Connected to the target VM, address: '127.0.0.1:58637', transport: 'socket'
分配了 1M 内存
[GC (Allocation Failure) [DefNew: 2460K->640K(6144K), 0.0031363 secs] 2460K->1740K(19840K), 0.0032275 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
分配了 4M 内存
Disconnected from the target VM, address: '127.0.0.1:58637', transport: 'socket'
Heap
def new generation total 6144K, used 4883K [0x00000000fec00000, 0x00000000ff2a0000, 0x00000000ff2a0000)
eden space 5504K, 77% used [0x00000000fec00000, 0x00000000ff024cb0, 0x00000000ff160000)
from space 640K, 100% used [0x00000000ff200000, 0x00000000ff2a0000, 0x00000000ff2a0000)
to space 640K, 0% used [0x00000000ff160000, 0x00000000ff160000, 0x00000000ff200000)
tenured generation total 13696K, used 1100K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000)
the space 13696K, 8% used [0x00000000ff2a0000, 0x00000000ff3b3320, 0x00000000ff3b3400, 0x0000000100000000)
Metaspace used 2919K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 310K, capacity 386K, committed 512K, reserved 1048576K
结论
堆内存初始值与堆内存最大值保持一致可减少垃圾回收机制次数
新生代空间分配
- JVM 配置
# -Xmn1m 设置新生代内存为1m
# -XX:SurvivorRatio=2 设置 eden 区和 from/to 空间的比例 (from/to 为 eden区的二分之一)
-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
- 示例代码
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// 创建 1 M 空间
byte = new byte[1 * 1024 * 1024];
}
}
- GC 信息
##################### 运行堆内存信息 ###############################
eden 为 512K
from|to 为 256K 占 eden 区的二分一
###################################################################
Heap
def new generation total 768K, used 281K [0x00000000fec00000, 0x00000000fed00000, 0x00000000fed00000)
eden space 512K, 5% used [0x00000000fec00000, 0x00000000fec06720, 0x00000000fec80000)
from space 256K, 99% used [0x00000000fec80000, 0x00000000fecbfff8, 0x00000000fecc0000)
to space 256K, 0% used [0x00000000fecc0000, 0x00000000fecc0000, 0x00000000fed00000)
tenured generation total 19456K, used 10693K [0x00000000fed00000, 0x0000000100000000, 0x0000000100000000)
the space 19456K, 54% used [0x00000000fed00000, 0x00000000ff771428, 0x00000000ff771600, 0x0000000100000000)
Metaspace used 2908K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 309K, capacity 386K, committed 512K, reserved 1048576K
设置新生代和老年代参数
- JVM 配置
# XX:NewRatio=2 设置新生代和老年代的比例(XX:NewRatio=2: 老年代空间是新生代空间的两倍)
-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2
- 示例代码
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// 创建 1 M 空间
byte = new byte[1 * 1024 * 1024];
}
}
- GC 信息
##################### 运行堆内存信息 ###############################
eden + from + to = 6784K
the space 13696K
新生代区 * 2 约等于老年代
###################################################################
Heap
def new generation total 5120K, used 3169K [0x00000000fec00000, 0x00000000ff2a0000, 0x00000000ff2a0000)
eden space 3456K, 62% used [0x00000000fec00000, 0x00000000fee18458, 0x00000000fef60000)
from space 1664K, 61% used [0x00000000ff100000, 0x00000000ff200010, 0x00000000ff2a0000)
to space 1664K, 0% used [0x00000000fef60000, 0x00000000fef60000, 0x00000000ff100000)
tenured generation total 13696K, used 715K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000)
the space 13696K, 5% used [0x00000000ff2a0000, 0x00000000ff352e08, 0x00000000ff353000, 0x0000000100000000)
Metaspace used 2907K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 309K, capacity 386K, committed 512K, reserved 1048576K
堆内存溢出
堆内存溢出的主要体现为老年代和新生代的使用空间不足.无法分配足够的内存空间.
解决方案:
# 修改堆的初始值和最大值.
-Xms512m -Xmx512m
堆内存溢出示例
- JVM 配置
假设将 JVM 的初始内存和最大内存设置为 5
-Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails
- 对堆内存溢出示例代码
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
System.out.println("i:" + i);
// 申请 10M 的空间
list.add(new byte[1 * 1024 * 1024]);
}
System.out.println("添加完成...");
}
- 结果
Full GC 表示触发了老年代的 GC 操作
代码执行后提示内存溢出错误.java.lang.OutOfMemoryError: Java heap space
原因: JVM 的最大内存只有 5m, 但是程序创建了10m空间.JVM无法创建10m内存,内存使用不足,最后导致内存溢出.
Connected to the target VM, address: '127.0.0.1:58799', transport: 'socket'
[GC (Allocation Failure) [PSYoungGen: 1018K->488K(1536K)] 1018K->736K(5632K), 0.0012770 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
i:0
i:1
i:2
i:3
[GC (Allocation Failure) [PSYoungGen: 562K->504K(1536K)] 3883K->3848K(5632K), 0.0009257 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 504K->480K(1536K)] 3848K->3916K(5632K), 0.0005384 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 480K->0K(1536K)] [ParOldGen: 3436K->3787K(4096K)] 3916K->3787K(5632K), [Metaspace: 2912K->2912K(1056768K)], 0.0082153 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 3787K->3787K(5632K), 0.0004224 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 3787K->3763K(4096K)] 3787K->3763K(5632K), [Metaspace: 2912K->2912K(1056768K)], 0.0083167 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid15116.hprof ...
Heap dump file created [4601798 bytes in 0.015 secs]
Disconnected from the target VM, address: '127.0.0.1:58799', transport: 'socket'
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.xcc.jvm.Demo04.main(Demo04.java:22)
Heap
PSYoungGen total 1536K, used 30K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
eden space 1024K, 2% used [0x00000000ffe00000,0x00000000ffe078f0,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 4096K, used 3763K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)
object space 4096K, 91% used [0x00000000ffa00000,0x00000000ffdacee8,0x00000000ffe00000)
Metaspace used 2943K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 312K, capacity 386K, committed 512K, reserved 1048576K