JVM(OOM案例),以下是修改后的启动参
JVM(OOM案例),以下是修改后的启动参
调优从业务场景开始,没有业务场景的调优都是耍流氓
无监控,不调优
OOM案例1:堆溢出
在 JDK 9 及以上版本中,需要使用 -Xlog 参数来配置 GC 日志的输出格式。以下是修改后的启动参数:
-XX:+PrintGCDetails -XX:MetaspaceSize=64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap/heapdump.hprof -Xms200M -Xmx200M -Xlog:gc*,gc+age=trace,safepoint:file=log/gc-oomHeap.log:time,uptime,level,tags:filecount=10,filesize=100M
在 JDK 9 及以上版本中,需要使用 -Xlog 参数来配置 GC 日志的输出格式。以下是修改后的启动参数:
-XX:+PrintGCDetails -XX:MetaspaceSize=64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap/heapdump.hprof -Xms200M -Xmx200M -Xlog:gc*,gc+age=trace,safepoint:file=log/gc-oomHeap.log:time,uptime,level,tags:filecount=10,filesize=100M
参数说明:
- -Xlog:gc*:启用所有 GC 相关的日志。
- gc+age=trace:打印对象年龄信息。
- safepoint:打印安全点信息。
- file=log/gc-oomHeap.log:指定 GC 日志文件路径。
- time,uptime,level,tags:在日志中包含时间戳、JVM 启动时间、日志级别和标签。
- filecount=10,filesize=100M:日志文件轮转,最多保留 10 个文件,每个文件最大 100MB。
@RestController
@RequestMapping("/api/user")
public class SysUserController {
@Resource
private ISysUserService sysUserService;
/**
* JVM参数配置
*
* 参数配置: 初始-Xms30M -Xmx30M
* JDK1.8版本使用
* -XX:+PrintGCDetails -XX:MetaspaceSize=64m
* -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap/heapdump.hprof
* -XX:+PrintGCDateStamps -Xms200M -Xmx200M -Xloggc:log/gc-oomHeap.log
*/
@RequestMapping("/add")
public void add() {
List<SysUser> users = new ArrayList<>();
while (true) {
users.add(new SysUser());
}
}
}
访问add接口报错:java.lang.OutOfMemoryError: Java heap space,随后在heap文件夹中就会生成heapdump.hprof文件,在log文件夹中就会生成gc-oomHeap.log文件
使用GCeasy网站分析log文件
使用JDK自带的工具分析dump文件
点击红色字体线程查找它的具体代码位置
使用MAT工具分析dump文件
OOM案例2:元空间溢出
jdk8
-XX:+PrintGCDetails -XX:MetaspaceSize=60m -XX:MaxMetaspaceSize=60m
-Xss512K -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdumpMeta.hprof -XX:SurvivorRatio=8
-XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:+PrintGCDateStamps
-Xms60M -Xmx60M -Xloggc:log/gc-oomMeta.log
jdk11
-XX:MetaspaceSize=60m
-XX:MaxMetaspaceSize=60m
-Xss512K
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdumpMeta.hprof
-XX:SurvivorRatio=8
-Xms60M
-Xmx60M
-Xlog:gc*,gc+age=trace,safepoint:file=log/gc-oomMeta.log:time,uptime,level,tags:filecount=10,filesize=100M
-Xlog:class+load=info:file=log/class-load.log:time,uptime,level,tags:filecount=10,filesize=100M
-Xlog:class+unload=info:file=log/class-unload.log:time,uptime,level,tags:filecount=10,filesize=100M
jdk8
-XX:+PrintGCDetails -XX:MetaspaceSize=60m -XX:MaxMetaspaceSize=60m
-Xss512K -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdumpMeta.hprof -XX:SurvivorRatio=8
-XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:+PrintGCDateStamps
-Xms60M -Xmx60M -Xloggc:log/gc-oomMeta.log
jdk11
-XX:MetaspaceSize=60m
-XX:MaxMetaspaceSize=60m
-Xss512K
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdumpMeta.hprof
-XX:SurvivorRatio=8
-Xms60M
-Xmx60M
-Xlog:gc*,gc+age=trace,safepoint:file=log/gc-oomMeta.log:time,uptime,level,tags:filecount=10,filesize=100M
-Xlog:class+load=info:file=log/class-load.log:time,uptime,level,tags:filecount=10,filesize=100M
-Xlog:class+unload=info:file=log/class-unload.log:time,uptime,level,tags:filecount=10,filesize=100M
参数说明:
-Xlog:gc*:替换 -XX:+PrintGCDetails 和 -XX:+PrintGCDateStamps,用于记录所有 GC 相关的日志。
gc+age=trace:打印对象年龄信息。
safepoint:打印安全点信息。
file=log/gc-oomMeta.log:指定 GC 日志文件路径。
time,uptime,level,tags:在日志中包含时间戳、JVM 启动时间、日志级别和标签。
filecount=10,filesize=100M:日志文件轮转,最多保留 10 个文件,每个文件最大 100MB。
-Xlog:class+load:替换 -XX:+TraceClassLoading,用于记录类加载信息。
file=log/class-load.log:指定类加载日志文件路径。
-Xlog:class+unload:替换 -XX:+TraceClassUnloading,用于记录类卸载信息。
file=log/class-unload.log:指定类卸载日志文件路径。
其他参数:
-XX:MetaspaceSize=60m 和 -XX:MaxMetaspaceSize=60m:设置元空间大小。
-Xss512K:设置线程栈大小。
-XX:+HeapDumpOnOutOfMemoryError 和 -XX:HeapDumpPath=heap/heapdumpMeta.hprof:在 OOM 时生成堆转储文件。
-XX:SurvivorRatio=8:设置新生代中 Eden 区与 Survivor 区的比例。
-Xms60M 和 -Xmx60M:设置堆内存的初始大小和最大大小。
/**
* -XX:+PrintGCDetails -XX:MetaspaceSize=60m -XX:MaxMetaspaceSize=60m
* -Xss512K -XX:+HeapDumpOnOutOfMemoryError
* -XX:HeapDumpPath=heap/heapdumpMeta.hprof -XX:SurvivorRatio=8
* -XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:+PrintGCDateStamps
* -Xms60M -Xmx60M -Xloggc:log/gc-oomMeta.log
*/
@RequestMapping("/metaSpaceOom")
public void metaSpaceOom() {
ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
while (true) {
Enhancer enhance = new Enhancer();
enhance.setSuperclass(SysUser.class);
enhance.setUseCache(false);
enhance.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("我是加强类,输出print之前的加强方法");
return methodProxy.invokeSuper(o, objects);
});
SysUser sysUser = (SysUser) enhance.create();
}
}
jps命令
PS D:\Practice> jps
19024 Main
23360 Launcher
23536
10248 Jps
12312 Main
jstat 命令
PS D:\Practice> jstat -gc 12312 1000 10
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.0 0.0 3841.8 30720.0 10240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
这两个命令可以配合使用一下查看线程的JVM内存的使用变化
OOM案例3:GC overhead limit exceeded
java -Xmx10m -XX:+UseParallelGC -XX:+PrintGCDetails GCOverheadLimitExceededExample
public class GCOverheadLimitExceededExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
while (true) {
list.add(new String("GC Overhead Limit Exceeded"));
}
}
}
分析原因
-
GC overhead limit exceeded 是Java虚拟机在垃圾回收过程中花费了过多时间(默认超过98%的时间)但只能回收很少的内存(默认少于2%的内存)时抛出的错误。
-
在这个案例中,我们设置了堆内存的最大值为10MB(-Xmx10m),并且使用并行垃圾回收器(-XX:+UseParallelGC)。由于程序不断地向列表中添加字符串对象,导致堆内存很快被耗尽,垃圾回收器频繁尝试回收内存,但每次只能回收很少的内存,最终触发了GC overhead limit exceeded错误。
OOM案例4:线程溢出
java -Xss1m -Xmx100m ThreadOOMExample
public class ThreadOOMExample {
public static void main(String[] args) {
while (true) {
new Thread(() -> {
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
分析原因
- 线程溢出 通常是由于创建了过多的线程,导致线程栈内存耗尽。每个线程都需要一定的栈空间(通过-Xss参数设置),当线程数量过多时,栈内存的总需求会超过JVM的可用内存,从而抛出OutOfMemoryError: unable to create new native thread。
- 在这个案例中,我们设置了每个线程的栈大小为1MB(-Xss1m),并且堆内存为100MB(-Xmx100m)。程序不断地创建新线程,并且每个线程都休眠很长时间,导致线程数量迅速增加,最终耗尽内存。
用户点评