金氧

诊断Java.lang.OutOfMemoryError(OOM)

在跟踪性能问题时,堆内存是首先应该被监控的最重要的组件之一。一旦堆内存的实际使用量超过其所允许的堆空间,就会产生堆内存压力。而这将导致频繁的全面垃圾回收事件,垃圾回收将窃取CPU周期,轻则导致响应时间延迟,重则导致必须重新启动Java虚拟机才能解决的内存溢出错误。

内存溢出错误(OOM)

当我运行应用时,出现了如下异常:

  • java.lang.OutOfMemoryError: GC overhead limit exceeded[7,9]
  • java.lang.OutOfMemoryError: Java heap space

第一条信息意味着,出于某种原因,垃圾收集器每次执行都花费了大量时间但只回收了很少量的内存,当我删除了如下代码后:

System.gc();

第一条信息消失了,取而代之的,系统出现了第二条信息。很明显堆内存空间依然存在问题。下面是我调查问题的步骤:

  1. 添加下面的Java启动参数
  2. -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
    • 系统会生成gc.log文件
  3. -XX:+HeapDumpOnOutOfMemoryError
    • 系统会生成堆内存转储文件
  4. 分析日志文件:
  5. 使用常规的文本编辑器查看gc.log 文件。
  6. 使用 Eclipse Memory Analyzer 查看 堆内存转储文件 (例如, java_xxx.hprof)

请注意,本文所讨论的所有虚拟机参数都是基于Hotspot虚拟机的。

Java命令行参数说明:

  • -XX:+PrintGCDetails
    • 打印更多的关于垃圾收集的信息。
  • -XX:+PrintGCTimeStamps
    • 打印从HotSpot 虚拟机开始执行直至垃圾收集事件发生所花费的时间(以秒为单位)。
  • -Xloggc:gc.log
    • 在每次垃圾收集时打印堆内存以及垃圾收集的信息。

在JDeveloper中可以按照如下方式设定:

  1. 右键选择你的项目(例如ViewController),显示出菜单
  2. 选择Project Properties…
  3. 选择Run/Debug/Profile
  4. 选择你Run Configuration(例如, Default)
  5. 点击Edit按钮
  6. 在Java虚拟机参数栏位设定 -Xloggc:gc.log -XX:-PrintGCDetails

运行你的应用并重现内存溢出异常,系统将会生成日志文件gc.log,
我的是在如下目录:

…/system11.1.1.5.37.60.13/DefaultDomain

因为我的Web应用是部署在集成的WLS中,并且通过DefaultDomain来执行。
所以,想要理解gc.log文件的格式,请参考关联阅读[5,15]。

不过,gc.log文件并不能真正的帮到我们,因为他只是简要的打印了堆内存问题,

但并没有指出问题出在哪。
接下来我要做的是添加如下参数,并重新执行服务。
-XX:+HeapDumpOnOutOfMemoryError
当服务发生堆内存错误时,会生成java_pid30835.hprof文件。

Eclise内存分析器(Eclipse Memory Analyzer)

堆内存转储文件由HPROF(堆内存和CPU分析工具)生成,堆内存转储文件是2进制格式的,因此必须使用Eclpse Memory Analyzer 来查看。

你可以通过Eclipse Update manager 来安装Eclipse MAT,选择”General Purpose Tools “并安装”Memory Analyser (Incubation)”以及”Memory Analyser (Charts)”。

安装之后,双击堆内存转储文件并且选择”Leak Suspects Report”

Eclipse MAT会显示图表以及问题的嫌疑人:

调整堆内存空间

如果你观察到垃圾收集日志文件中有内存溢出错误,那么可以尝试将Java堆内存空间调整为你能够分配给Java虚拟机的物理内存空间的80%,基于具体是老年代空间还是永久代空间发生内存溢出,你可以像这样调整内存空间。

  • 针对老年代发生内存溢出
    • increase -Xms and -Xmx
  • 针对永久代发生内存溢出
    • increase -XX:PermSize and -XX:MaxPermSize

参考文献:

« Log4j 2 介绍 单元测试要做多深?——测试驱动开发(TDD)引发的争论 »