1. 模拟线上oom问题
1.1 代码
@GetMapping("/addList")
public void addList(){
List list = new ArrayList();
while (true){
String a = "aaaaa"+new Date();
list.add(a);
System.out.println(a);
}
}
2. 线上环境启动,并配置打印gc日志
启动脚本
设置内存小一点 都为20m。
java -Xms20m -Xmx20m -Xloggc:./gc.log -jar test.jar >> test.log 2>&1 &
3. 调用死循环代码
3.1 控制台报错日志
3.2 gc日志
可以看到全是full gc
4. 问题排查方法
4.1 TOP命令查看cpu
此时的cpu已经飙升到了80%多了
4.1 jps
查看当前的java程序信息,前面是进程号。
如果显示的是jar。没有显示项目的名称,是因为启动的时候没事使用包的绝对路径。自己改一下。
4.2 jinfo -进程号
5. JConsule链接远程服务器
5.1 启动脚本可以使用如下的。不然可能链接不上
注意hostname的ip地址要和本机地址一样
nohup java \
-Djava.rmi.server.hostname=192.168.17.129 \
-Dcom.sun.management.jmxremote=true \
-Dcom.sun.management.jmxremote.port=1099 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-XX:+PrintGC -Xms80M -Xmx80M \
-jar test.jar > test.log 2>&1 &
5.2 登录页面
这里的端口号,和上面启动脚本的端口号保持一直,不然等不上去。
5.3 登录成功
选择不安全链接
6. jvisualvm
6.1 登录
点击文件。选择添加jmx连接。然后输入和上面jconsule一样的地址。就可以登录。
6.2 登录成功页面
可以看到一些启动参数信息。
7. 线上真实环境
首先为什么不用图形化界面。首先如果你需要实时监控。这样的话那就需要一直的启动一个程序jmxremote来一直监听这服务。所以比较消耗性能。这样是不可能的。
7.1 jmap 获取相关信息(重要)
获取内存占用情况的前二十行。
jmap -histo 8995 | head -20
可以看出占用最多的是char[]。
自动生产堆文件,当内存溢出的时候。
-XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError
启动脚本,在第一次oom的时候生成一个dump文件,他的后缀不是dump的。注意可能只在第一次oom的时候生成文件。
nohup java \
-Djava.rmi.server.hostname=192.168.17.129 \
-Dcom.sun.management.jmxremote=true \
-Dcom.sun.management.jmxremote.port=1099 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError \
-Xms80M -Xmx80M \
-jar test.jar > test.log 2>&1 &
7.2 jmap的相关命令
7.2.1 查看整个JVM内存状态
jmap -heap [pid]
要注意的是在使用CMS GC 情况下,jmap -heap的执行有可能会导致JAVA 进程挂起
7.2.2 查看JVM堆中对象详细占用情况(jmap -histo)(极力推荐)在线定位问题
jmap -histo [pid]
jmap -histo [pid] | head -20 (前20行)
基本就可以定位到问题,我们可以看到是OomController这个类的,有关Person的相关问题。
7.2.3 导出整个JVM 中内存信息Dump文件(jmap -dump)
一般在线系统不要这么干。
缺点和方法:
- jmap执行期间会对进程产生很大影响,甚至卡顿。
- 线上是不能执行jmap。
- 设定参数HeapDump ,OOM的时候会自动产生堆文件。
- 面试最佳答案:我们很多服务器备份,停掉这台服务器,然后将这台服务器隔离开。然后用jmap导出,导出之后在观察,面试官就不会再有问题了。
jmap -dump:format=b,file=文件名 [pid]
jmap -dump:format=b,file=oom.dump 10922
8. 使用jvisualvm分析dump文件
在文件按钮选择转入->文件类型选择堆->选择dump文件
转入之后生产的文件。导入分析页面。
8.1 装入好多字符串oom
@GetMapping("/addList")
public void addList(){
List list = new ArrayList();
while (true){
String a = "aaaaa"+new Date();
list.add(a);
System.out.println(a);
}
}
8.2 装入好多对象错误
@GetMapping("/addPerson")
public void addPerson(){
List list = new ArrayList();
while (true){
list.add(new Person());
System.out.println('p');
}
}
8.3 查找最大的对象(很重要)
一下就可以知道是arrayList数组太大。
面试
案例一:
发现不知道哪位同事在设置tomcat的http-header-size参数的时候设置的过大。
server.port=9898
server.max-http-header-szie=10000000
通过jmap查询对象的时候,发现一定会有http对象较大。这样比较容易定位是这个问题。
案例二(比较牛逼)
Distuptor有个可以设置链的长度,如果过大,然后对象大,消费完不主动释放,会溢出
案例三
线程池不当运用产生OOM问题
不断的往List里加对象(实在太LOW)