1
既然选择了远方,即使天寒地冻,路遥马亡,我本就一无所有,又有何惧。

OOM(内存溢出)是一个让人很头疼的问题,出现 OOM 的问题有很多,下面就 OOM 可能出现的原因进行介绍。

1、堆空间太小

用以下参数启动 jvm
-Xms20m -Xmx20m

public class OOMTest {
    public static void main(String[] args) {
        Byte[] bs = new Byte[1024 * 1024 * 30];
    }
}

最大堆、初始化堆均为 20m,程序创建了 30m 的数组,直接 OOM。

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

1.1、处理方式

  1. 调大-Xmx 的值
  2. 通过内存分析工具,找出占用内存较多的对象

2、直接内存溢出

直接内存属于堆外内存,是直接向操作系统申请内存空间。
直接内存申请速度一般小于堆内存的申请速度,但是访问速度会比堆内存的访问速度快。
直接内存并没有完全的归 GC 管理,使用不当,也会造成 OOM

2.1、直接内存不足,溢出

用以下参数启动 jvm
-Xms20m -Xmx20m

public class OOMTest {
    public static void main(String[] args) {
        while (true) {
            ByteBuffer.allocateDirect(1024 * 1024 * 30);
        }
    }
}

直接内存最大值为 20m, 所以直接溢出

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:694)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at com.csp.boot.jvm.OOMTest.main(OOMTest.java:14)

注:直接内存最大值,默认为 -Xmx 配置的值

2.2、处理方式

  1. 调小 -Xmx 的值,让操作系统有更多的内存分配给直接内存
  2. 避免一次性分配太大的直接内存
  3. 设置合理的 -XX:MaxDirectMemorySize

2.3、直接内存回收

直接内存只有在使用量触到 -XX:MaxDirectMemorySize 时,才会触发 GC,具体看下面 demo

用以下参数启动 jvm
-Xms20m -Xmx20m -verbose:gc

public class OOMTest {
    public static void main(String[] args) {
        while (true) {
            ByteBuffer.allocateDirect(1024 * 1024 * 1);
        }
    }
}

启动后,会在程序控制台,看到有 GC 日志不断的输出

[GC (System.gc())  2474K->731K(19968K), 0.0016716 secs]
[Full GC (System.gc())  731K->631K(19968K), 0.0043987 secs]

3、线程过多

在 java 内存模式中,虚拟机栈,本地方法栈,程序计算器 这些都是线程独有的,线程本身也占用内存空间,如果线程启用太多,会直接导致 OOM

用以下参数启动 jvm
-Xms20m -Xmx20m

public class OOMTest {
    public static void main(String[] args) {
        while (true) {
            new Thread(() -> {
                try {
                    Thread.sleep(30000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:717)
    at com.csp.boot.jvm.OOMTest.main(OOMTest.java:19)

3.1、处理方式

  1. 适当调低 -Xss 的值,可使 jvm 创建更多的线程

4、GC 效率低下

当 GC 效率低下时,也会出现 oom。GC 效率低下导致的 oom 条件苛刻,但是还是不可避免会出现

4.1、触发条件

  1. 花在 GC 的时间上,超过 98%
  2. 老年代释放的内存小于 2%
  3. 新生代释放的内存小于 2%
  4. 连续 5 次 GC 都出现以上情况

触发后,会抛出以下异常
image.png

注:必须满足以上所有条件,才会触发

5、内存泄露

出现内存泄露的原因,主要是对象已经无用了,但是却无法被回收。从而可使用的内存越来越少,最终触发 OOM.

找出内存泄露的罪魁祸首一般需要借助内存分析工具,进行分析。这一部分的内容比较多,会放到下一章节来说。


心无私天地宽
513 声望22 粉丝