OOM到底是如何产生的呢?

2021-05-24  本文已影响0人  程序狮

一、什么是OOM

OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error

二、为什么会OOM、出现的原因是什么

为什么会没有内存了呢?原因不外乎有两点:

① 分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。

② 应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。

堆内存溢出

用途

用来存储对象实例.(字符串String创建对象分析)

报错信息
java.lang.OutOfMemoryError: Java heap space ——>java堆内存溢出
产生

我们只要不停的创建对象,并且保证GC Roots和对象之间有可达路径避免垃圾回收,那么在对象数量超过最大堆的大小限制后很快就能出现这个异常。

分析

一般的排查方式可以通过设置-XX: +HeapDumpOnOutOfMemoryError在发生异常时dump出当前的内存转储快照来分析,分析可以使用Eclipse Memory Analyzer(MAT)来分析,独立文件可以在官网下载。

有什么方法可以增加应用程序可以使用的内存大小吗?

在API级别11+上运行的应用程序可以具有android:largeHeap="true"在<application>元素来请求比正常堆大小更大的堆大小,并且getLargeMemoryClass()在……上面ActivityManager会告诉你那堆有多大。然而:

  1. 这只适用于API级别11+(即蜂巢及更高级别)。

  2. 不能保证这个大堆会有多大。

  3. 用户会感知到您的大堆请求,因为它会迫使他们的其他应用程序离开RAM终止其他应用程序的进程以释放系统RAM供大型堆使用

  4. 因为第三,我希望android:largeHeap将被滥用,对此的支持可能会在将来被放弃,或者在安装时可能会警告用户(例如,您需要为它申请特别许可)。

  5. 目前,这一特性已被轻描淡写。

方法区(运行时常量池)和元空间溢出

报错信息

java.lang.OutOfMemoryError: PermGen space ——>java永久代溢出,即方法区溢出了
产生

一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区

用途

方法区: 和堆一样,是线程共享的区域,包含Class文件信息、运行时常量池、常量池,运行时常量池和常量池的主要区别是具备动态性,也就是不一定非要是在Class文件中的常量池中的内容才能进入运行时常量池,运行期间也可以可以将新的常量放入池中,比如String的intern()方法。

我们写一段代码验证一下String.intern(),同时我们设置-XX:MetaspaceSize=50m -XX:MaxMetaspaceSize=50m 元空间大小。由于我使用的是1.8版本的JDK,而1.8版本之前方法区存在于永久代(PermGen),1.8之后取消了永久代的概念,转为元空间(Metaspace),如果是之前版本可以设置PermSize MaxPermSize永久代的大小。

直接内存溢出

直接内存并不是虚拟机运行时数据区域的一部分,并且不受堆内存的限制,但是受到机器内存大小的限制。常见的比如在NIO中可以使用native函数直接分配堆外内存就容易导致OOM的问题。

直接内存大小可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java 堆最大值-Xmx一样。

由直接内存导致的内存溢出,一个明显的特征是在Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,那就可以考虑检查一下是不是这方面的原因。

栈内存溢出

报错信息

Java.lang.StackOverflowError ——> 不会抛OOM error

用途

栈是线程私有,它的生命周期和线程相同。每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,方法调用的过程就是栈帧入栈和出栈的过程。

在java虚拟机规范中,对虚拟机栈定义了两种异常:

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常

如果虚拟机栈可以动态扩展,并且扩展时无法申请到足够的内存,抛出OutOfMemoryError异常

上一篇下一篇

猜你喜欢

热点阅读