自己搭建了一个小博客,该文章与博客文章同步。
一般情况下,出现OOM主要有一下三种原因。
- 一次性申请对象的太多。更改申请对象数量。
- 内存资源耗尽未释放。找到未释放的对象进行释放。
- 本身资源不够。jmap -heap 查看堆信息。
分几种情况解决:
系统已经挂了的情况:
-XX:+HeapDumpOnOutOfMemoryError
这个jvm启动参数含义:当堆内存空间溢出时输出堆的内存快照。
配合参数:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/home/tomcat/logs/…
触发条件:java.lang.OutOfMemoryError: Java heap space,也就是说当发生OutOfMemoryError错误时,才能触发-XX:HeapDumpOnOutOfMemoryError 输出到-XX:HeapDumpPath指定位置。
关于fullgc:Systerm.gc() 以及fullgc 不会触发-XX:HeapDumpOnOutOfMemoryError
系统运行中还未OOM
导出dump文件:jmap -dump:format=b,file=java_pidxxxx.hprof 14660
Arthas
结合jvisualvm 进行调试
查看最多跟业务有关对象->找到GCRoot ->查看线程栈
我们以一个场景为例:
准备三个java文件:
OOMUser
package com.aquarius.wizard.jdkapi.oom;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author zhaoyijie
* @since 2024/6/22 09:17
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OOMUser {
private Integer id;
private String name;
}
OOMService
package com.aquarius.wizard.jdkapi.oom;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author zhaoyijie
* @since 2024/6/22 09:19
*/
public class OOMService {
public void getUserList() {
List<OOMUser> list = new ArrayList<OOMUser>();
int i = 0;
while (true) {
list.add(new OOMUser(i++, UUID.randomUUID().toString()));
}
}
}
OOMDemo
package com.aquarius.wizard.jdkapi.oom;
/**
* @author zhaoyijie
* @since 2024/6/22 08:49
*/
public class OOMDemo {
public static void main(String[] args) {
//java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/zhaoyijie/IdeaProjects/java-study/Jdk-api-demo/logs/ -cp Jdk-api-demo-1.0.jar com.aquarius.wizard.jdkapi.oom.OOMDemo
OOMService oomService = new OOMService();
oomService.getUserList();
}
}
zhaoyijie@zhaoyijiedeMacBook-Pro target % java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/zhaoyijie/IdeaProjects/java-study/Jdk-api-demo/logs/ -cp Jdk-api-demo-1.0.jar com.aquarius.wizard.jdkapi.oom.OOMDemo
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to /Users/zhaoyijie/IdeaProjects/java-study/Jdk-api-demo/logs/java_pid71318.hprof ...
Heap dump file created [11439235 bytes in 0.059 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.UUID.randomUUID(UUID.java:144)
at com.aquarius.wizard.jdkapi.oom.OOMService.getUserList(OOMService.java:17)
at com.aquarius.wizard.jdkapi.oom.OOMDemo.main(OOMDemo.java:12)
zhaoyijie@zhaoyijiedeMacBook-Pro target % cd ..
zhaoyijie@zhaoyijiedeMacBook-Pro Jdk-api-demo % tree
.
├── logs
│ └── java_pid71318.hprof
├── pom.xml
zhaoyijie@zhaoyijiedeMacBook-Pro target % jvisualvm
导出路径是/Users/zhaoyijie/IdeaProjects/java-study/Jdk-api-demo/logs/
你必须确保有这个路径,否则导出不成功,程序不会自动创建文件夹
找到最占内存的实例,双击实例
这里表示是OOMDemo的第12行,也就是oomService.getUserList();这个方法导致了OOM,这个方法写的是个死循环。
其他工具:
1.jmap -heap 查看是否内存分配过小
2.jmap -histo 查看是否有明显的对象分配过多且没有释放情况
3.jmap -dump 导出 JVM 当前内存快照,使用 JDK 自带或 MAT 等工具分析快照
jmap -histo:live 71854 | head -10
zhaoyijie@zhaoyijiedeMacBook-Pro ~ % jps
71131 RemoteMavenServer36
70218 Main
71855 Jps
71854 OOMDemo
zhaoyijie@zhaoyijiedeMacBook-Pro ~ % jmap -histo:live 71854 | head -10
num #instances #bytes class name
----------------------------------------------
1: 51019 4488144 [C
2: 50999 1223976 java.lang.String
3: 49520 1188480 com.aquarius.wizard.jdkapi.oom.OOMUser
4: 49648 794368 java.lang.Integer
5: 571 313888 [Ljava.lang.Object;
6: 557 63992 java.lang.Class
7: 439 29280 [I
jstat -gcutil 75841 1000 10
zhaoyijie@zhaoyijiedeMacBook-Pro ~ % jps
75841 OOMDemo
75588 Main
75844 Jps
73694 jar
zhaoyijie@zhaoyijiedeMacBook-Pro ~ % jstat -gcutil 75841 1000 10
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
33.71 0.00 0.00 92.95 70.58 74.10 22 7.029 5 9.073 16.102
33.71 0.00 0.00 92.95 70.58 74.10 22 7.029 5 9.073 16.102
0.00 0.00 6.00 70.55 70.58 74.10 22 7.029 5 14.942 21.971
0.00 0.00 100.00 70.55 70.58 74.10 23 7.029 5 14.942 21.971
16.87 16.86 100.00 72.39 70.58 74.10 24 7.471 5 14.942 22.413
16.87 16.87 100.00 76.21 70.58 74.10 25 8.006 5 14.942 22.948
16.87 16.87 100.00 79.29 70.58 74.10 26 8.728 5 14.942 23.670
17.83 0.00 38.00 88.79 70.58 74.10 28 9.677 5 14.942 24.620
0.00 18.82 0.00 91.90 70.58 74.10 29 10.345 6 14.942 25.287
0.00 18.82 0.00 91.90 70.58 74.10 29 10.345 6 14.942 25.287
zhaoyijie@zhaoyijiedeMacBook-Pro ~ % jstack 75841
可以看到老年代的内存达到了百分之90以上,有跌倒百分之70,但是后面又回升到了百分之90。fullGC执行了6次,老年代存在大量不回收的对象。
jstack不方便查看,推荐Arthas
https://arthas.aliyun.com/
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
未使用的时候需要停止
zhaoyijie@zhaoyijiedeMacBook-Pro software % java -jar arthas-boot.jar
[INFO] JAVA_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre
[INFO] arthas-boot version: 3.7.2
[INFO] Process 75588 already using port 3658
[INFO] Process 75588 already using port 8563
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 75588 com.intellij.idea.Main
[2]: 77952 wechatImageMonitor.jar
1
[INFO] arthas home: /Users/zhaoyijie/.arthas/lib/3.7.2/arthas
[INFO] The target process already listen port 3658, skip attach.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.7.2
main_class
pid 75588
time 2024-06-23 09:08:36
[arthas@75588]$ stop
Resetting all enhanced classes ...
Affect(class count: 0 , method count: 0) cost in 1 ms, listenerId: 0
Arthas Server is going to shutdown...
[arthas@75588]$ session (5948c830-32cc-43d5-a878-fd9bbca42a87) is closed because server is going to shutdown.
zhaoyijie@zhaoyijiedeMacBook-Pro software %
否则会抛出异常
zhaoyijie@zhaoyijiedeMacBook-Pro software % java -jar arthas-boot.jar
[INFO] JAVA_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre
[INFO] arthas-boot version: 3.7.2
[INFO] Process 75588 already using port 3658
[INFO] Process 75588 already using port 8563
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 75588 com.intellij.idea.Main
[2]: 73694 wechatImageMonitor.jar
2
[ERROR] The telnet port 3658 is used by process 75588 instead of target process 73694, you will connect to an unexpected process.
[ERROR] 1. Try to restart arthas-boot, select process 75588, shutdown it first with running the 'stop' command.
[ERROR] 2. Or try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 3658 -c "stop"
[ERROR] 3. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
使用dashboard命令
[arthas@77952]$ dashboard
Memory used total max usage GC
heap 153M 219M 3641M 4.21% gc.ps_scavenge.count 246
ps_eden_space 79M 79M 1329M 5.97% gc.ps_scavenge.time(ms) 1396
ps_survivor_space 12M 17M 17M 73.02% gc.ps_marksweep.count 14
ps_old_gen 61M 122M 2731M 2.25% gc.ps_marksweep.time(ms) 202
nonheap 28M 29M -1 94.94%
code_cache 5M 5M 240M 2.21%
metaspace 20M 21M -1 96.66%
compressed_class_space 2M 2M 1024M 0.24%
direct 0K 0K - 0.00%
mapped 0K 0K - 0.00%
Runtime
os.name Mac OS X
os.version 10.16
java.version 1.8.0_201
java.home /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre
systemload.average 6.26
processors 10
timestamp/uptime Sun Jun 23 12:01:47 CST 2024/536s
[arthas@77952]$ heapdump /tmp/dump-1.hprof
Error: -F option used
Cannot connect to core dump or remote debug server. Use jhsdb jmap instead
Java9之后jmap -F等动态attach模式需使用jhsdb代替。
[root@VM-4-5-centos ~]# jps -lm
7491 jdk.jcmd/sun.tools.jps.Jps -lm
19513 org.apache.catalina.startup.Bootstrap start
20094 cn.edu.gxust.blogex.api.BlogExApiApplication
[root@VM-4-5-centos ~]# jhsdb jmap --heap --pid 20094
Attaching to process ID 20094, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 17.0.6+9-LTS-190
using thread-local object allocation.
Garbage-First (G1) GC with 2 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 1073741824 (1024.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 643825664 (614.0MB)
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 22020096 (21.0MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 1048576 (1.0MB)
Heap Usage:
G1 Heap:
regions = 1024
capacity = 1073741824 (1024.0MB)
used = 101319680 (96.6259765625MB)
free = 972422144 (927.3740234375MB)
9.43613052368164% used
G1 Young Generation:
Eden Space:
regions = 36
capacity = 160432128 (153.0MB)
used = 37748736 (36.0MB)
free = 122683392 (117.0MB)
23.529411764705884% used
Survivor Space:
regions = 7
capacity = 8388608 (8.0MB)
used = 7864320 (7.5MB)
free = 524288 (0.5MB)
93.75% used
G1 Old Generation:
regions = 55
capacity = 99614720 (95.0MB)
used = 55706624 (53.1259765625MB)
free = 43908096 (41.8740234375MB)
55.92208059210526% used
[root@VM-4-5-centos ~]# java -version
java version "17.0.6" 2023-01-17 LTS
Java(TM) SE Runtime Environment (build 17.0.6+9-LTS-190)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.6+9-LTS-190, mixed mode, sharing)
jmap -heap $PID
[hadoop@dev]$ jmap -heap 24464
Attaching to process ID 24464, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 1644167168 (1568.0MB)
NewSize = 357564416 (341.0MB)
MaxNewSize = 547880960 (522.5MB)
OldSize = 716177408 (683.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 470286336 (448.5MB)
used = 295203328 (281.52783203125MB)
free = 175083008 (166.97216796875MB)
62.77097704152731% used
From Space:
capacity = 36175872 (34.5MB)
used = 7907344 (7.5410308837890625MB)
free = 28268528 (26.958969116210938MB)
21.85806053272192% used
To Space:
capacity = 35651584 (34.0MB)
used = 0 (0.0MB)
free = 35651584 (34.0MB)
0.0% used
PS Old Generation
capacity = 790626304 (754.0MB)
used = 131458176 (125.3682861328125MB)
free = 659168128 (628.6317138671875MB)
16.627093651566646% used
55933 interned Strings occupying 5650672 bytes.