优秀的编程知识分享平台

网站首页 > 技术文章 正文

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

nanyue 2025-01-17 12:29:41 技术文章 3 ℃

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服务器排障的一个很重要的日志,这个日志文件记录了系统的一般信息和错误消息,包括内核产生的消息、启动脚本消息以及其他系统守护进程的消息。

最近发表
标签列表