2024年4月30日发(作者:)

一、OOM含义:

OOM,全称“OutOfMemory”,意思是“内存用完了”。它来源于

emoryError。

二、为什么会出现emoryError:即OOM:

官方介绍为当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没

有空间可回收时,就会抛出emoryError:···(注意:这是个很严重的

问题,因为这个问题已经严重到不足以被应用处理)。

具体原因大致为两方面:

1、自身原因:比如虚拟机本身可使用的内存太少。

2、外在原因:如应用使用的太多,且用完没释放,浪费了内存。此时就会造成内存

泄露或者内存溢出。

内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段

内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。

内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。

三、OOM的error类型

首先说一下JAVA虚拟机运行时会管理的内存区域吧:

1.程序计数器:当前线程执行的字节码的行号指示器,线程私有

虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧

的进栈和出栈的操作。

3.本地方法栈:类似“JAVA虚拟机栈”,但是为native方法的运行提供内存环境。

堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分

为新生代,老生代。

5.方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译

后的代码等数据。Hotspot中的“永久代”。

6.运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。

7.直接内存:并不是JVM运行时数据区的一部分,可直接访问的内存,比如NIO会

用到这部分。

所以除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。

常见OOM情况:

emoryError:Javaheapspace------>java堆内存溢出,此种情

况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存

监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-ms,-m等修改。

emoryError:PermGenspace------>java永久代溢出,即方法

区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因

为上述情况会产生大量的Class信息存储于方法区。当出现此种情况时可以通过更改方法

区的大小来解决,使用类似-:PermSize=64m-:MaPermSize=256m的形式修改。注意,

过多的常量尤其是字符串也会导致方法区溢出。

verflowError------>不会抛OOMerror,但也是比较常见的Java

内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,

栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-ss来设置栈的大小。

四、OOM分析

HeapDump(堆转储文件)它是一个Java进程在某个时间点上的内存快照。

HeapDump是有着多种类型的。不过总体上heapdump在触发快照的时候都保存了

java对象和类的信息。通常在写heapdump文件前会触发一次FullGC,所以

heapdump文件中保存的是FullGC后留下的对象信息。

通过设置如下的JVM参数,可以在发生OutOfMemoryError后获取到一份HPROF

二进制HeapDump文件:

-:+HeapDumpOnOutOfMemoryError

生成的文件会直接写入到工作目录。

注意:该方法需要JDK5以上版本。

转存堆内存信息后,需要对文件进行分析,从而找到OOM的原因。可以使用以下方

式:

mat:eclipsememoryanalyzer,基于eclipseRCP的内存分析工具。具体使用参考:

/mat/,推荐使用。

jhat:JDK自带的javaheapanalyzetool,可以将堆中的对象以html的形式显示出

来,包括对象的数量,大小等等,并支持对象查询语言OQL,分析相关的应用后,可以

通过localhost:7000来访问分析结果。不推荐使用。