Java线上服务挂了之OOM场景分析

createh51周前 (05-16)技术教程2

Linux服务器进程OOM的原理

Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是瞬间占用内存很快的进程,然后防止内存耗尽而自动把该进程杀掉。

具体过程如图所示:

在源码层面,内核检测到系统内存不足、挑选并杀掉某个进程的过程,可以参考内核源代码linux/mm/oom_kill.c。

当系统内存不足的时候,out_of_memory()被触发,然后调用select_bad_process()选择一个”bad”进程杀掉。

如何判断和选择一个”bad进程呢?linux选择”bad”进程是通过调用oom_badness(),挑选的算法和想法都很简单很朴实:最bad的那个进程就是那个最占用内存的进程。


与Java进程相关的内存布局分析

与Java进程相关的内存包括:Java进程所在的JVM的内存,主要是堆(Heap Space,线程共享)、方法区(Method Area,线程共享)、栈+程序计数器(Native Area,线程私有);以及Java进程可能会使用到的堆外的本地内存(Off Head Space)

Java进程发生内存溢出主要与以上内存区域相关,具体分析如下。

Java进程OOM的场景

堆内存溢出

1、堆内存空间不足

当堆内存(Heap Space)没有足够空间存放新创建的对象时,就会抛出
java.lang.OutOfMemoryError: Java heap space
错误。

2、垃圾回收过于频繁

当 Java 进程花费 98% 以上的时间执行 GC,但只恢复了不到 2% 的内存,且该动作连续重复了 5 次,就会抛出
java.lang.OutOfMemoryError:GC overhead limit exceeded
错误。简单地说,就是应用程序已经基本耗尽了所有可用内存, GC 也无法回收。

方法区内存溢出

J当抛出
java.lang.OutOfMemoryError: PermGen space 错误时,
表示永久代(Permanent Generation)已用满,通常是因为加载的 class 数目太多或体积太大。JDK 1.8 使用 Metaspace 替换了永久代(Permanent Generation),会抛出
java.lang.OutOfMemoryError: Metaspace。

栈内存溢出

栈空间不足时,需要分下面两种情况处理:

(1)线程请求的栈深度大于虚拟机允许的最大深度 - StackOverflowError,这个错误一般是由于如无限递归导致的栈溢出。

(2)虚拟机在扩展栈深度时,无法申请到足够的内存空间 - OutOfMemoryError,其中这个错误一般是会抛出
java.lang.OutOfMemoryError: unable to create new native thread,具体原因一般为:

1)内存空间不足以满足创建线程所需的stack size:virtual memory < stack size*the number of threads

2)线程数已达到操作系统的上限

堆外本地内存溢出

Java 允许应用程序通过 Direct ByteBuffer 直接访问堆外内存,许多高性能程序通过 Direct ByteBuffer 结合内存映射文件(Memory Mapped File)实现高速 IO。

Direct ByteBuffer 的默认大小为 64 MB,一旦使用超出限制,就会抛出 Direct buffer memory 错误。

操作系统内存不足,OOM机制杀掉

检查操作系统的messages日志文件(/var/log/messages)就会看到下面类似的 Out of memory: Kill process 信息。其中messages日志是Linux服务器排障的一个很重要的日志,这个日志文件记录了系统的一般信息和错误消息,包括内核产生的消息、启动脚本消息以及其他系统守护进程的消息。

相关文章

java算法题-在区间范围内统计奇数数目

在leetcode(https://leetcode-cn.com/)上看到一道有趣的算法题:给你两个非负整数 low 和 high 。请你返回 low 和 high 之间(包括二者)奇数的数目。示例...

JVM 深度解析:运行时数据区域、分代回收与垃圾回收机制全攻略

共同学习,有错欢迎指出。JVM 运行时数据区域1. 程序计数器程序计数器是一块较小的内存空间,可看作当前线程所执行的字节码的行号指示器。在虚拟机概念模型里,字节码解释器通过改变这个计数器的值选取下一条...

图解常见的限流算法(计数器、滑动窗口计数、漏桶、令牌桶)

哈喽,大家好呀,我是呼噜噜,好久没有更新文章了,今天我们来聊聊在企业级项目中,常见的几种限流手段的原理及其实现什么场景需要限流随着互联网的业务发展,比如秒杀、双十一、618等这些我们耳熟能详,也有被人...

Java高并发解决方案:轻松应对海量请求

Java高并发解决方案:轻松应对海量请求在当今互联网时代,高并发问题已经成为每个Java开发者绕不开的话题。无论是电商平台的大促活动,还是社交平台的热门话题讨论,都可能瞬间产生海量请求。那么,我们该如...

Java并发工具:CountDownLatch

CountDownLatch是Java并发包(java.util.concurrent)中提供的一种同步工具,用于控制一个或多个线程等待其他线程完成操作。它是一个非常有用的工具类,常用于协调多个线程之...

踩坑!Java集合必学技能:Collection.size()方法深度解析与避坑

一、开发中遇到的问题:动态集合统计的"陷阱"在实际开发中,我们经常需要统计集合中的元素数量。例如,电商订单处理场景中需要实时统计待处理订单数。但如果不理解size()方法的底层逻辑,容...