前言
最近的一台服务器出现了OOM的情况,出于好奇就进行了一些探查,这里做一些简单的记录
问题排查
日志查看
登陆服务器查看当天的报错日志,根据服务器的错误日志定位到了代码,发现是一个定时任务引起的,发现该定时任务报错的同时还有一个线程也在打印错误信息。所以想到了两个可能的问题:
- 新增的定时任务冲突,导致多个线程同时跑任务,导致最后OOM
- 源代码就有问题,数据量增多导致的定时任务出现的问题
代码查看
根据代码可以发现没有大对象的存在,最大的对象也只是几千万的Long对象,但是哪怕1亿的Long对象 也不会导致OOM,所以排出了代码问题。
集群查看
由于是分布式定时任务,也就是说定时任务是随机分发的,先对该任务的执行历史进行查看,发现执行的历史记录发现同一台机器,前一天失败了,但是后一天却成功了,而其他的所有机器都是成功的。也就是可以确定源代码应该没有问题。
所以怀疑是问题一导致的OOM,该服务器的JVM参数没同步,即可能数据量增多之后,其他的服务器都进行了扩容,但是该服务器忘记扩容导致的,故先去集群的监控中心查看其他的服务器的堆内存大小,发现确实该服务器的内存只有8G,而其他的服务器都已经扩容到了12G。再根据其他的禁用的服务器,发现都是8G。
问题解决
机器申请扩容最后成功解决问题
多线程OOM时候的回收
上面说在查看服务器的错误日志的时候,发现一个任务的所有线程都OOM了,但是另一个定时任务的线程没有立即结束,还在运行并进行日志的打印。故进行了简单测试,查看当多线程的情况下某个线程OOM之后会发生什么?怎么样才会彻底OOM直接宕机?
测试代码
public class OutOfMemoryTest {
public static void main(String[] args) {
new Thread(() -> {
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024 * 50]);
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1_000);
} catch (Exception e) {
}
}
}, "dead thread").start();
new Thread(() -> {
while (true) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1_000);
} catch (Exception e) {
}
}
}, "alive thread").start();
}
}
启动参数设置
-Xmx10m -Xms10m
设置堆大小为10m
控制台打印
根据控制台的打印可以发现在多线程的情况下,如果某个线程导致了OOM,虚拟机会终止该线程并回收它所占用的资源。但是,如果OOM是由于整个应用程序使用的内存超过了虚拟机的最大内存限制而导致的,那么整个虚拟机将会崩溃。
即当应用程序使用的内存超过了虚拟机的最大内存限制时,虚拟机会抛出OutOfMemoryError
异常。如果该异常没有被捕获并处理,那么虚拟机将会崩溃,并且应用程序将无法继续执行。
因此,在编写多线程应用程序时,需要注意内存的使用情况,并且及时处理OutOfMemoryError
异常,以避免整个虚拟机崩溃。
这个就好像是操作系统的资源分配一样,如果出现了资源的长时间持有,导致了可能死锁的发生,就允许剥夺该线程资源进行资源重分配。