天天看点

OOM

OOM问题总结

    • 什么是OOM
    • 为什么会OOM
    • OOM的类型
    • OOM处理方式

什么是OOM

OOM ,全称"OutOfMemery",中文名称“内存不够用”。

很长时间以来,很多人都知道jvm内存调优是java知识中的重要组成部分,但是缺乏应用经验,不知道jvm的使用场景是什么,OOM就是其中一个典型应用场景。

为什么会OOM

内存不够用,要么是因为内存太小,要么是因为内存使用不充分

1.jvm内存分配不够,电脑内存的大小,不等于java程序能够使用的内存大小。jvm分配的内存大小,可以在JVM启动时,通过配置文件配置。

2.内存利用不当,有两个表现,内存泄漏和内存溢出。

内存泄漏,对象使用完毕后,不能够及时销毁,变成内存垃圾,如果不能够及时清理,内存垃圾越来越多,可用内存越来越少,影响程序的健康运行。虽然java提供GC机制,可以自动进行内存回收,但是逻辑错误,可能导致垃圾堆积过多。如,将太多的局部作用的对象保存为全局对象。

内存溢出,请求分配的内存,比jvm剩余可用内存少,导致程序不能够正确运行,导致崩坏。

OOM的类型

OOM的类型和jvm运行时数据区的划分有着直接关系,jvm运行时,会管理以下内存区域:

1.程序计数器,线程私有,用来记录线程当前执行的字节码的行号

2.jvm栈,线程私有,用来存储基本类型的数据。以函数栈为例,函数执行时,分配函数栈用于存储基本类型数据,函数执行结束后销毁。

3.堆(heap),所有线程共有(线程通信问题的根源),类实例化后生成的对象,一般保存到堆里面。

4.方法区,存储类信息,final常量,类中的静态常量,为所有线程共享

5.运行时常量池,是方法区的一部分,保存常量信息,方法和对象的引用信息等

6.本地方法栈,native方法,java应用非java代码,使用的内存。

7.直接内存,非jvm运行时数据区的一部分,可以直接访问的内存,例如NIO中用到的情况。

按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。

最常见的OOM情况有以下三种:

1.java.lang.OutOfMemoryError: Java heap space ------>java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。

2.java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

3.java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

OOM处理方式

1.heapdump

要dump堆的内存镜像,可以采用如下两种方式:

  • 设置JVM参数-XX:+HeapDumpOnOutOfMemoryError,设定当发生OOM时自动dump出堆信息。不过该方法需要JDK5以上版本。(事先设定好)
  • 使用JDK自带的jmap命令。"jmap -dump:format=b,file=heap.bin " 其中pid可以通过jps获取。(事后dump)

2.分析原因

dump堆内存信息后,需要对dump出的文件进行分析,从而找到OOM的原因。常用的工具有:

  • mat: eclipse memory analyzer, 基于eclipse RCP的内存分析工具。
  • jhat:JDK自带的java heap analyze tool,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言OQL,分析相关的应用后,可以通过http://localhost:7000来访问分析结果。不推荐使用,因为在实际的排查过程中,一般是先在生产环境 dump出文件来,然后拉到自己的开发机器上分析,所以,不如采用高级的分析工具比如前面的mat来的高效。