线上oom问题

1. oom项目模拟代码

写个模拟oom的伪代码,该代码每分钟会消耗1M的内存

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.oom;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class User {
private String id;
private String name;
private String age;
private byte[] bytes;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.oom;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class UserInitializer implements ApplicationRunner {

@Override
public void run(ApplicationArguments args) throws Exception {
oom();
}

public void oom() throws InterruptedException {
long m_size = 0;
System.out.println("OOM mock run....");
List<User> list = new ArrayList<>();
while (true) {
System.out.println(String.format("memory use: %sM", m_size));
Thread.sleep(1000 * 60);
list.add(new User("111", "王霄", "18", new byte[1024 * 1024]));
m_size += 1;
}
}

}

2. 部署到服务器

为了尽快看到OOM现象,配置了初始内存和最大堆内存为128m,理论上该代码2个小时就会OOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[wx@localhost download]$ java -jar oom.jar Xms128M -Xmx128M

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.3)

2021-08-15 12:42:34.015 INFO 5559 --- [ main] com.example.oom.OomApplication : Starting OomApplication v0.0.1-SNAPSHOT using Java 1.8.0_302 on localhost.localdomain with PID 5559 (/home/wx/download/oom.jar started by wx in /home/wx/download)
2021-08-15 12:42:34.032 INFO 5559 --- [ main] com.example.oom.OomApplication : No active profile set, falling back to default profiles: default
2021-08-15 12:42:35.331 INFO 5559 --- [ main] com.example.oom.OomApplication : Started OomApplication in 2.162 seconds (JVM running for 3.035)
OOM mock run....
memory use: 0M
memory use: 1M
memory use: 2M

3. 堆内存分析(没发生OOM之前)

3.1 jps查看java进程号

1
2
3
[wx@localhost ~]$ jps
6921 jar
6955 Jps

3.2 jmap导出堆内存快照

1
2
3
[wx@localhost ~]$ jmap -dump:format=b,file=/home/wx/download/oom.bin 6921
Dumping heap to /home/wx/download/oom.bin ...
Heap dump file created

3.3 下载内存分析工具

  1. 先安装eclipse, eclipse官网
  2. 下载之后安装Memory Analyser插件

    eclipse主界面 -> help -> Eclipse Marketplace -> 搜索mat -> Memory Analyser 1.9.2 -> install

  3. 切换到内存分析视图

    eclipse主界面 -> Open Perspective -> 点击左上角的 File -> Open Heap Dump -> 导入的自己的内存文件

  4. 导入之后就可以查看目前服务器堆内存的使用情况,排查出导致OOM的原因

3.4 堆内存分析(已经发生了OOM)

  • 如果项目部署时配置了jvm启动参数,oom时自动导出堆文件,那就可以拿到生成的文件导入到Eclipse中进行分析
1
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof
  • 如果项目部署时没有配置了jvm启动参数,就只能重启项目,然后在还没发生OOM时导出堆文件再去分析
    • 方法一: 实时监控生产环境内存使用情况,在其内存占用率异常的时候导出堆文件
    • 方法二: 为了更快重现生存环境OOM的场景,一般会在测试服务器部署一份同样的代码,并将初始堆内存调的很小,然后批量请求使其尽快OOM

4. 总结

  • 生产环境发生OOM时,如果要应急处理,一般可以重启服务当做应急处理,之后找到问题再重新部署
  • 生产环境项目部署时,建议配置启动参数,当OOM时自动将堆文件导出,免得到时候因为OOM导致服务挂掉,就无法还原当时的场景了
文章目录
  1. 1. 1. oom项目模拟代码
  2. 2. 2. 部署到服务器
  3. 3. 3. 堆内存分析(没发生OOM之前)
    1. 3.1. 3.1 jps查看java进程号
    2. 3.2. 3.2 jmap导出堆内存快照
    3. 3.3. 3.3 下载内存分析工具
    4. 3.4. 3.4 堆内存分析(已经发生了OOM)
  4. 4. 4. 总结
|