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参数说明:
[*]-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.logjdk11
-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 123121000 10
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.0 26624.0 10898.0 38400.0 37090.8 5120.0 4571.6 6 0.024 0 0.000 0.024
0.0 4096.00.0 3841.8 30720.010240.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)。程序不断地创建新线程,并且每个线程都休眠很长时间,导致线程数量迅速增加,最终耗尽内存。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]