阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

什么是java OOM?如何分析及解决oom问题?

30次阅读
没有评论

共计 2797 个字符,预计需要花费 7 分钟才能阅读完成。

导读 最近查找了很多关于 OOM,甚至于 Java 内存管理以及 JVM 的相关资料,发现这方面的东西太多了,竟有一种眼花缭乱的感觉,要想了解全面的话,恐非一篇文章能说清的,因此按照自己的理解整理了一篇,剩下的还需要继续学习。
什么是 OOM?

OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,来源于 java.lang.OutOfMemoryError。看下关于的官方说明:Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. 意思就是说,当 JVM 因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个 error(注:非 exception,因为这个问题已经严重到不足以被应用处理)。

为什么会 OOM?

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

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

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

内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。
内存溢出:申请的内存超出了 JVM 能提供的内存大小,此时称之为溢出。

在之前没有垃圾自动回收的日子里,比如 C 语言和 C ++ 语言,我们必须亲自负责内存的申请与释放操作,如果申请了内存,用完后又忘记了释放,比如 C ++ 中的 new 了但是没有 delete,那么就可能造成内存泄露。偶尔的内存泄露可能不会造成问题,而大量的内存泄露可能会导致内存溢出。

而在 Java 语言中,由于存在了垃圾自动回收机制,所以,我们一般不用去主动释放不用的对象所占的内存,也就是理论上来说,是不会存在“内存泄露”的。但是,如果编码不当,比如,将某个对象的引用放到了全局的 Map 中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。如果该种情况出现次数多了,就会导致内存溢出,比如系统中经常使用的缓存机制。Java 中的内存泄露,不同于 C ++ 中的忘了 delete,往往是逻辑上的原因泄露。

OOM 的类型

JVM 内存模型:

按照 JVM 规范,JAVA 虚拟机在运行时会管理以下的内存区域:
程序计数器:当前线程执行的字节码的行号指示器,线程私有
JAVA 虚拟机栈:Java 方法执行的内存模型,每个 Java 方法的执行对应着一个栈帧的进栈和出栈的操作。
本地方法栈:类似“JAVA 虚拟机栈”,但是为 native 方法的运行提供内存环境。
JAVA 堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
方法区:用于存储已经被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot 中的“永久代”。
运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
直接内存:并不是 JVM 运行时数据区的一部分,可直接访问的内存,比如 NIO 会用到这部分。
按照 JVM 规范,除了程序计数器不会抛出 OOM 外,其他各个内存区域都可能会抛出 OOM。

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

  • java.lang.OutOfMemoryError: Java heap space ——>java 堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数 -Xms,-Xmx 等修改。
  • java.lang.OutOfMemoryError: PermGen space ——>java 永久代溢出,即方法区溢出了,一般出现于大量 Class 或者 jsp 页面,或者采用 cglib 等反射机制的情况,因为上述情况会产生大量的 Class 信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似 -XX:PermSize=64m -XX:MaxPermSize=256m 的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。
  • java.lang.StackOverflowError ——> 不会抛 OOM error,但也是比较常见的 Java 内存溢出。JAVA 虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数 -Xss 来设置栈的大小。
  • OOM 分析 –heapdump

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

  • 设置 JVM 参数 -XX:+HeapDumpOnOutOfMemoryError,设定当发生 OOM 时自动 dump 出堆信息。不过该方法需要 JDK5 以上版本。
  • 使用 JDK 自带的 jmap 命令。”jmap -dump:format=b,file=heap.bin ” 其中 pid 可以通过 jps 获取。
  • dump 堆内存信息后,需要对 dump 出的文件进行分析,从而找到 OOM 的原因。常用的工具有:
    mat: eclipse memory analyzer, 基于 eclipse RCP 的内存分析工具。详细信息参见:http://www.eclipse.org/mat/,推荐使用。
    jhat:JDK 自带的 java heap analyze tool,可以将堆中的对象以 html 的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言 OQL,分析相关的应用后,可以通过 http://localhost:7000 来访问分析结果。不推荐使用,因为在实际的排查过程中,一般是先在生产环境 dump 出文件来,然后拉到自己的开发机器上分析,所以,不如采用高级的分析工具比如前面的 mat 来的高效。
    这个链接:http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html 中提供了一个采用 mat 分析的例子。

    注意:因为 JVM 规范没有对 dump 出的文件的格式进行定义,所以不同的虚拟机产生的 dump 文件并不是一样的。在分析时,需要针对不同的虚拟机的输出采用不同的分析工具(当然,有的工具可以兼容多个虚拟机的格式)。IBM HeapAnalyzer 也是分析 heap 的一个常用的工具。

    小结

    涉及到的虚拟机的技术或者工具,往往需要考虑到虚拟机规范以及不同的虚拟机实现。尤其是针对虚拟机调优时,往往需要针对虚拟机在某些方面的实现策略来考虑,比如,不同的虚拟机的垃圾回收算法是不一样的,而这直接影响了虚拟机某些参数的设置,以达到虚拟机的最佳性能。

    阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

    腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

    代金券:在阿里云专用满减优惠券

    正文完
    星哥说事-微信公众号
    post-qrcode
     0
    星锅
    版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计2797字。
    转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
    【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
    阿里云-最新活动爆款每日限量供应
    评论(没有评论)
    验证码
    【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中