捕获Java内存泄露 (二)

来源:互联网 发布:适合笔记本的linux 编辑:程序博客网 时间:2024/06/05 00:17

原文:Hunting Memory Leaks in Java
上一篇:捕获Java内存泄露 (一)

捕获Java内存泄露 (二)

解密OutOfMemoryError

如上所述,OOM是内存泄漏的常见标志。本质上,是空间不足已分配新对象会引发此错误.。尽管百般努力,垃圾回收器依然找不到所需的空间,堆也不能进一步扩展。因此,stacktrace和错误一起被抛出。

诊断你的OOM的第一步是确定这个错误的确切意味。这听起来很明显,但答案并不总是那么清晰。例如:这个OOM出现是因为java堆已满,或者因为本地堆已满?为了帮助你回答这个问题,让我们分析几个可能的错误信息:

  • java.lang.OutOfMemoryError: Java heap space
  • java.lang.OutOfMemoryError: PermGen space
  • java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  • java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space?
  • java.lang.OutOfMemoryError: <reason> <stack trace> (Native method)

“Java heap space”

这条错误消息不一定意味着内存泄漏。其实,问题可能仅仅是配置错误。

例如,我曾经负责分析一个应用,它一直产生这种类型的OutOfMemoryError。经过一番调查,我发现罪魁祸首是一个数组的实例化请求了太多的内存;在这种情况下,它并不是应用程序的问题,而是应用服务器只配置了默认堆的大小,它太小了。我通过调整JVM的内存参数解决了这个问题。

在其他情况下,特别是对于长寿命的应用程序,消息可能表明我们无意中持有对象的引用,阻止垃圾收集器清理它们。这是Java语言相当于一个内存泄漏。(注意:被应用程序调用的API也可能成为被无意中持有对象引用。

另一个这种“java堆空间“OOM的潜在来源是finalizers的使用。如果类有finalize方法,则该类型的对象在垃圾回收时没有使它们的空间被回收。相反,在垃圾收集之后,这些对象排队等待后续的终结。在Sun的实现里,finalizers是由一个守护线程执行。如果finalizers线程不能跟上终结队列,然后java堆会被填满然后一个OOM被抛出。

“PermGen space”

此错误消息表示永久代(permanent generation)已满。永久代是存储类和方法对象的堆的区域.。如果一个应用程序要加载大量的类,那么永久代的大小可能需要使用-XX:MaxPermSize选项来增加。

驻留的java.lang.String对象也存储在永久代。java.lang.String类维护一个字符串池。当调用intern() 方法时,该方法将检查池以查看是否存在等效字符串.。如果是,则由intern()方法返回;如果没有,则将字符串添加到池中。更精确的描述是,java.lang.String.intern方法返回一个字符串的规范表示(canonical representation);这个结果是对相同的类的实例的引用,如果该字符串是一个字面量。如果一个应用驻留了大量的字符串,你可能需要增加永久代的大小。

译者注:上述观点适用于Java6,且Java6早期版本中字符串池大小不可配置,自Java7之后,字符串池移回到了堆中,并且池大小可以通过-XX:StringTableSize=N 选项来配置。

注意:你可以使用jmap - permgen命令打印的永久代相关的统计信息,包括驻留的字符串实例信息。

“Requested array size exceeds VM limit”

此错误表示应用程序(或该应用程序使用的APIs)试图分配超出堆大小的数组。例如,如果应用程序试图分配512MB的数组,但堆的最大尺寸是256MB,那么一个带有这个错误信息的OOM将被抛出。在大多数情况下,这要么是配置问题要么是应用程序试图分配巨大的数组时产生的bug。

“Request <size> bytes for <reason>. Out of swap space?”

此消息看起来是一个OOM。但是,当在本地堆的一次分配失败,并且本地堆可能接近耗尽时,HotSpot VM会抛出这个明显的异常。消息中包含了请求并失败的大小(以字节为单位)和内存请求的原因.。在大多数情况下,<reason>是报告分配失败的源模块的名称。

如果这种类型的OOM被抛出,你可能需要在你的操作系统上使用故障排除工具来进一步诊断问题。在某些情况下,这个问题甚至可能与应用程序无关。例如,您可能会看到此错误如果:

  • 操作系统配置的交换空间不足。
  • 系统上的另一个进程正在消耗所有可用的内存资源。

应用程序也可能由于本地泄漏而失败(例如,如果某个应用程序或库代码正在连续分配内存,但未能将其释放回操作系统)。

“<reason> <stack trace> (Native method)”

如果您看到此错误消息,并且堆栈跟踪的第一帧是一个本地方法,那么那个本地方法遇到了分配失败。这一消息和以前的区别是:分配失败被检测到来自一个JNI或本地方法而不是java虚拟机代码。

如果这种类型的OOM被抛出,你可能需要在你的操作系统上使用故障排除工具来进一步诊断问题。

下一篇:捕获Java内存泄露 (三)


0 0