发生OOM怎么处理
怎么感知到OOM
- 使用Zabbix、Open-Falcon之类的监控平台,监控和报警,主要对机器(CPU、磁盘、内存、网络)资源的负载情况,JVM的GC频率和内存使用率,系统自身的业务指标,系统的异常报错进行监控
- CPU使用率都达到100%了,此时一定有问题
- 关注本地磁盘的使用量和剩余空间
- 关注机器上的内存使用量
- 关注机器上的网络负载
- 注意JVM的Full GC的频率
- 另一种是被动等待系统挂掉后客服来通知你
发生OOM的内存区域
方法区(MetaSpace)
方法区用来存放系统里的各种类的信息的,包括JDK自身内置的一些类的信息,都在这块区域里。
- 方法区发生溢出的情况:
- Metaspace区域直接用默认的参数,不设置其大小,导致默认的Metaspace区域可能才几十MB而已,此时对于一个稍微大型一点的系统,因为他自己有很多类,还依赖了很多外部的jar包有有很多的类,几十MB的Metaspace很容易就不够了
- 就是很多人写系统的时候会用cglib之类的技术动态生成一些类,一旦代码中没有控制好,导致你生成的类过于多的时候,就很容易把Metaspace给塞满,进而引发内存溢出.
/**
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:MetaspaceSize=10m
-XX:MaxMetaspaceSize=10m
-XX:+PrintGCDetails
-Xloggc:gc.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./
* @author fmj 模拟元空间内存溢出
* @date 2021 2021/9/8 15:32
*/
public class MetaSpaceOverFlow {
public static void main(String[] args) {
int count = 0;
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Subject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new ProxyClass());
Subject subject = (Subject)enhancer.create();
subject.run();
System.out.printf("正在创建第%d个对象\n", ++count);
}
}
}
Java虚拟机栈
每个线程都有一个自己的虚拟机栈,就是所谓的栈内存。然后这个线程只要执行一个方法,就会为方法创建一个栈桢,将栈桢放入自己的虚拟机栈里去,然后在这个栈桢里放入方法中定义的各种局部变量。
- 线程栈溢出的情况
- 如果不停的让线程调用方法,不停的往栈里放入栈桢,此时终有一个时刻,大量的栈桢会消耗完毕这个1MB的线程栈内存,最终就会导致出现栈内存溢出的情况。
- 一般来说,其实引发栈内存溢出,往往都是代码里写了一些bug才会导致的,正常情况下发生的比较少。避免出现无限制的方法递归,就一般可以避免栈内存的溢出。
/**
* @author 模拟虚拟机栈溢出
-XX:ThreadStackSize=1m
* @date 2021 2021/9/8 16:24
*/
public class StackOverflow {
static int count = 0;
public static void main(String[] args) {
work();
}
private static void work() {
System.out.printf("第%d次执行work方法\n", ++count);
work();
}
}
堆内存
在写好的代码里,特别在一些方法中,可能会频繁的创建各种各样的对象,这些对象都是放在堆内存里的
- 堆内存溢出情况
- 高并发场景下导致ygc后存活对象太多,高并发场景下,导致ygc后很多请求还没处理完毕,存活对象太多,可能就在Survivor区域放不下了,此时就只能进入到老年代里去了,老年代很快就会放满了,一旦老年代放满了就会触发Full GC,但是,老年代GC过后,依然存活下来了很多的对象,这个时候如果年轻代还有一批对象等着放进老年代,就会发生OOM
- 系统有内存泄漏的问题,就是莫名其妙弄了很多的对象,结果对象都是存活的,没有及时取消对他们的引用,导致触发GC还是无法回收,此时只能引发内存溢出,因为内存实在放不下更多对象了
/**
* @author 模拟堆内存溢出
-Xms5m
-Xmx5m
-XX:+PrintGCDetails
-Xloggc:gc.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
* @date 2021 2021/9/8 16:31
*/
public class HeapOverFlow {
public static void main(String[] args) {
int count = 0;
List<BigClass> list = new ArrayList<>();
while (true) {
list.add(new BigClass());
// System.out.printf("创建第%d个对象\n", ++count);
}
}
static class BigClass {
byte[] bytes = new byte[1024];
}
}
发生OOM处理思路
- 发生OOM了,必然说明系统中某个区域的对象太多了,塞满了那个区域,而且一定是无法回收掉那些对象,最终才会导致内存溢出的。
- 首先就得知道到底是什么对象太多了最终导致OOM的
- 在OOM时dump一份内存快照,事后分析这个内存快照,使用
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/local/app/oom 两个参数设置。
第一个参数意思是在OOM的时候自动dump内存快照出来,第二个参数是说把内存快照放到哪儿去
- 使用MAT工具分析内存快照,查看是什么大对象占用的内存,进而分析该对象被谁引用。
- MAT内存快照分析工具下载
https://www.eclipse.org/mat/downloads.php

- 栈内存溢出,只要把所有的异常都写入本地日志文件,那么当发现系统崩溃了,第一步就去日志里定位一下异常信息就知道了。
- 查看大对象

- 找到哪些线程创建了过多的对象

- 找到占用内存最大的对象之后,最后一步就是要定位一下是哪一行代码,或者是哪个方法创建了那么多的对象


浙公网安备 33010602011771号