文档库 最新最全的文档下载
当前位置:文档库 › 探查内存不足

探查内存不足

>> 返回首页

探查内存不足/内存泄漏问题

问题描述

内存不足(OOM) -由于java 堆或本地内存中的内存耗尽,应用程序显示“内存不足”错误。

内存泄漏-java 堆或本地内存的持续内存增长,最终将导致内存不足状态。调试内存泄漏状态的技术与调试内存

不足状态的技术相同。

故障排除

请注意,并非下面所有任务都需要完成。有些问题仅通过执行几项任务就可以解决。

快速链接:

?Java 堆、本地内存和进程大小

?进程地址空间和物理内存之间的差异

?为什么会发生OOM 问题,JVM 在这种情况下如何处理?

?排除故障的步骤

o确定是Java OOM 还是本地OOM

o Java OOM

o本地OOM

?Jrockit 特定特性

?参考文献

Java 堆、本地内存和进程大小

Java 堆-这是JVM 用来分配java 对象的内存。java 堆内存的最大值用java 命令行中的 .Xmx 标志来指定。

如果未指定最大的堆大小,那么该极限值由JVM 根据诸如计算机中的物理内存量和该时刻的可用空闲内存量这类因

素来决定。始终建议您指定最大的java 堆值。

本地内存-这是JVM 用于其内部操作的内存。JVM 将使用的本地内存堆数量取决于生成的代码量、创建的线程、GC 期间用于保存java 对象信息的内存,以及在代码生成、优化等过程中使用的临时空间。

如果有一个第三方本地模块,那么它也可能使用本地内存。例如,本地JDBC 驱动程序将分配本地内存。

最大本地内存量受到任何特定操作系统上的虚拟进程大小限制的约束,也受到用.Xmx 标志指定用于java 堆的内存

量的限制。例如,如果应用程序能分配总计为 3 GB 的内存量,并且最大java 堆的大小为 1 GB,那么本地内存量

的最大值可能在 2 GB 左右。

进程大小-进程大小将是java 堆、本地内存与加载的可执行文件和库所占用内存的总和。在32 位操作系统上,

进程的虚拟地址空间最大可达到 4 GB。从这 4 GB 内存中,操作系统内核为自己保留一部分内存(通常为 1 - 2 GB)。剩余内存可用于应用程序。

Windows缺省情况下,2 GB 可用于应用程序,剩余2 GB 保留供内核使用。但是,在Windows 的一些变化版本

中,有一个/3GB 开关可用于改变该分配比率,使应用程序能够获得 3 GB。有关/3GB 开关的详细信息,可以在以下网址中找到:

https://www.wendangku.net/doc/9413356230.html,/library/default.asp?url=/library/en-us/ddtools/hh/ddtools/bootini_1fcj.asp

RH Linux AS 2.1 -3 GB 可用于应用程序。

对于其它操作系统,请参考操作系统文档了解有关配置。

返回页首

进程地址空间和物理内存之间的差异

每个进程都获得其自有的地址空间。在32 位操作系统中,此地址空间范围为0 到4 GB。此范围与计算机的可用随机存取内存(RAM) 或交换空间无关。计算机中的可用物理内存总量是该计算机上的可用RAM 和交换空间之和。所有运行的进程共享这些物理内存。

进程内的存储地址是虚拟地址。内核将此虚拟地址映射到物理地址上。物理地址指向物理内存中的某个位置。在任一给定时间,计算机中运行进程所使用的全部虚拟内存的总和不能超过该计算机上可用物理内存的总量。

返回页首

为什么会发生OOM 问题,JVM 在这种情况下如何处理?

java 堆中的内存不足

如果JVM 不能在java 堆中获得更多内存来分配更多java 对象,将会抛出java 内存不足(java OOM) 错误。如果java 堆充满了活动对象,并且JVM 无法再扩展java 堆,那么它将不能分配更多java 对象。

在这种情况下,JVM 让应用程序决定在抛出https://www.wendangku.net/doc/9413356230.html,ng.OutOfMemoryError 后该执行什么操作。例如,应用程序可以处理此错误,并决定以安全方式自行关闭或决定忽略此错误。如果应用程序不处理此错误,那么抛出此错误的线程将退出(如果您进行java Thread Dump,那么将看不到该线程)。

在使用Weblogic Server 的情况下,如果此错误是由某个执行线程抛出的,则会处理此错误并将其记录在日志中。如果连续抛出此错误,那么核心运行状况监视器线程将关闭Weblogic Server。

本地堆中的内存不足

如果JVM 无法获得更多本地内存,它将抛出本地内存不足(本地OOM)错误。当进程到达操作系统的进程大小限值,或者当计算机用完RAM 和交换空间时,通常会发生这种情况。

当发生这种情况时,JVM 处理本地OOM 状态,记录说明它已用完本地内存或无法获得内存的消息,然后退出。如果JVM 或加载的任何其它模块(如libc 或第三方模块)不处理这个本地OOM 状态,那么操作系统将给JVM 发送命令JVM 退出的sigabort 信号。通常情况下,JVM 收到sigabort 信号时将会生成一个核心文件。

返回页首

排除故障的步骤

确定是Java OOM还是本地OOM:

?如果stdout/stderr 消息说明这是一个https://www.wendangku.net/doc/9413356230.html,ng.OutOfMemoryError,那么这就是Java OOM

?如果stdout/stderr 消息说明无法获得内存,那么这就是本地OOM

请注意,上述消息仅发送到stdout 或stderr 中,而不发送到应用程序特定的日志文件(如weblogic.log)

返回页首

对于Java OOM:

1.收集和分析verbose gc 输出

a.在java 命令行中添加“-verbosegc”标志。这样将会把GC 活动信息打印到stdout/stderr。将

stdout/stderr 重定向到一个文件。运行应用程序,直到该问题重现。

b.确保JVM 在抛出java OOM 之前完成下列任务

完整GC 运行:

执行一次完整GC 运行,并且删除了所有不可及对象以及虚可及、弱可及、软可及对象,并回收了那些空间。有关不同级别的对象可及性的详细信息,可以在以下网址中可找到:

https://www.wendangku.net/doc/9413356230.html,/developer/technicalArticles/ALT/RefObj

您可以检查是否在发出OOM 消息之前执行了完整GC 运行。当完成一次完整GC 运行时,将会打印类似如下消息(格式取决于JVM -请查看JVM 帮助信息以了解有关格式)

[memory ] 7.160: GC 131072K->130052K (131072K) in 1057.359 ms

以上输出的格式如下(备注:在此模式下将全部使用相同的格式):

[memory ] : GC K->K (K), ms

[memory ] - start time of collection (seconds since jvm start)

[memory ] - memory used by objects before collection (KB)

[memory ] - memory used by objects after collection (KB)

[memory ] - size of heap after collection (KB)

[memory ] - total time of collection (milliseconds)

但是,没有办法断定是否使用verbose 消息删除了软/弱/虚可及的对象。如果您怀疑在抛出OOM 时这些对象仍然存在,请与JVM 供应商联系。

如果垃圾回收算法是一种按代回收算法(对于Jrockit 为gencopy 或gencon,对于其它JDK 则是缺省算法),您也将看到类似如下的verbose 输出:

[memory ] 2.414: Nursery GC 31000K->20760K (75776K), 0.469 ms

以上是nursery GC(即young GC)周期,它将把活动对象从nursery(或young 空间)提升到old 空间。这个周期对我们的分析不重要。有关按代回收算法的详细信息,可以在JVM 文档中找到。

如果在java OOM 之前未发生GC 周期,那么这是一个JVM 错误。

完全压缩:

确保JVM 执行了适当的压缩工作,并且内存并未成碎片(否则会阻止分配大对象并触发java OOM 错误)。

Java 对象要求内存是连续的。如果可用空闲内存是一些碎片,那么JVM 将无法分配大对象,因为它可能无法放入任何可用空闲内存块中。在这种情况下,JVM 将执行一次完全压缩,以便形成更多连续的空闲内存来容纳大对象。

2.压缩工作包括在java 堆内存中将对象从一个位置移动到另一个位置,以及更新对这些对象

的引用以指向新位置。除非确有必要,否则JVM 不会压缩所有对象。这是为了减少GC

周期的暂停时间。

3.我们可以通过分析verbose gc 消息来检查java OOM 是否由碎片引起。如果您看到类似

如下的输出(在此无论是否有可用的空闲java 堆都会抛出OOM),那么这就是由碎片

引起的。

4.[memory ] 8.162: GC 73043K->72989K (131072K) in 12.938 ms

[memory ] 8.172: GC 72989K->72905K (131072K) in 12.000 ms

[memory ] 8.182: GC 72905K->72580K (131072K) in 13.509 ms

https://www.wendangku.net/doc/9413356230.html,ng.OutOfMemoryError

5.在上述情况中您可以看到,所指定的最大堆内存是128MB,并且当实际内存使用量仅为

72580K 时,JVM 抛出OOM。堆使用量仅为55%。因此在这种情况下,碎片影响是:

即使还有45%的空闲堆,内存也会抛出OOM。这是一个JVM 错误或缺陷。您应当与

JVM 供应商联系。

6.如果JVM 一切都正常(上一步中提到的所有操作),那么此java OOM 可能是应用程序的问题。应用程

序可能在不断泄漏一些java 内存,而这可能导致出现上述问题。或者,应用程序使用更多的活动对象,因此它需要更多java 堆内存。在应用程序中可以检查以下方面:

o应用程序中的缓存功能-如果应用程序在内存中缓存java 对象,则应确保此缓存并没有不断增大。对缓存中的对象数应有一个限值。我们可以尝试减少此限值,来观察其是否降低java 堆使用

量。

Java 软引用也可用于数据缓存,当JVM 用完java 堆时,可以保证删除软可及对象。

o长期活动对象-如果应用程序中有长期活动对象,则可以尝试尽可能减少这些对象的存在期。例如,调整HTTP 会话超时值将有助于更快地回收空闲会话对象。

o内存泄漏-内存泄漏的一个例子是在应用服务器中使用数据库连接池。当使用连接池时,必须在finally 块中显式关闭JDBC 语句和结果集对象。这是因为,当从池中调用连接对象上的close()

时,只是简单地把连接返回池中以供重用,并没有实际关闭连接和关联的语句/结果集对象。

o增加java 堆-如果可能的话,我们也可尝试增加java 堆,以观察是否能解决问题。

4.如果上述建议都不适用于该应用程序,那么,我们需要使用一个基于JVMPI(JVM 事件探查器接口)的事

件探查器(如Jprobe 或OptimizeIt)来找出哪些对象正在占用java 堆。事件探查器还提供java 代码中正在创建这些对象的位置的详细信息。本文档并不介绍每个事件探查器的详细信息。可以参考事件探查器文档来了解如何用事件探查器设置和启动应用程序。一般而言,基于JVMPI 的事件探查器需要较高的系统开销,并会大大降低应用程序的性能。因此,在生产环境中使用这些事件探查器并不可取。

https://www.wendangku.net/doc/9413356230.html,/optimizeit

https://www.wendangku.net/doc/9413356230.html,/jprobe

返回页首

对于本地OOM 问题:

1.收集下列信息:

a..verbosegc输出,通过它可监视java 堆使用量。这样将有助于了解此应用程序的java 内存要

求。

应当注意,指定的最大堆内存量(在java 命令行中使用 Xmx标志)与应用程序的实际

java 堆使用量无关,其在JVM 启动时被保留,并且此保留内存不能用于其它任何用途。

在使用Jrockit 时,使用-verbose来代替-verbosegc,因为这可以提供codegen 信

息以及GC 信息。

b.定期记录进程虚拟内存大小,从启动应用程序时起直到JVM 用完本地内存。这样将有助于了解此

进程是否确实达到该操作系统的大小限值。

在Windows 环境下,使用下列步骤来监视虚拟进程大小:

i.在“开始”-> “运行”对话框中,输入“perfmon”并单击“确定”。

ii.在弹出的“性能”窗口中,单击“+”按钮(图表上部)。

iii.在显示的对话框中选择下列选项:

?性能对象:进程(不是缺省的处理器)

?从列表中选择计数器:虚拟字节数

?从列表中选择实例:选择JVM (java) 实例

?单击“添加”,然后单击“关闭”

在Unix 或Linux 环境下,对于一个给定PID,可以使用以下命令来查找虚拟内存大小-

ps -p -o vsz。

在Linux 环境下,单个JVM 实例内的每个java 线程都显示为一个独立的进程。如果我

们获得根java 进程的PID,那么这就足够了。可以使用ps命令的.forest选项来找到

根java 进程。例如,ps lU --forest将提供一个由指定用户启动的所有进程的

ASCII 树图。您可以从该树图中找到根java。

2.计算机中的内存可用性

如果计算机没有足够的RAM 和交换空间,则操作系统将不能为此进程提供更多内存,这样也会导致内存不足。请确保RAM 与磁盘中的交换空间之和足以满足该计算机中正在运行的所有进程的需要。

3.调整java 堆

如果java 堆使用量完全在最大堆范围内,则减小java 最大堆将为JVM 提供更多的本地内存。这不是一个解决办法,而是一个可尝试的变通方法。由于操作系统限制进程大小,我们需要在java 堆和本地堆之间寻求一个平衡。

4.JVM 的本地内存使用量

在加载了所有类并调用了方法(代码生成结束)后,JVM 的本地内存用量预计将会几乎达到稳定。对于大多数应用程序而言,这通常发生在最初几小时内。此后,JVM 可能会因加载运行时类型、生成优化代码等处理而仅使用少量本地内存。

为了缩小问题的范围,可尝试禁用运行时优化,并检查这是否会产生任何效果。

o在使用Jrockit 时,可使用-Xnoopt标志来禁用运行时优化。

o在使用SUN hotspot JVM 时,-Xint标志将强迫JVM 在解释模式中运行(不生成代码)。

如果在整个运行过程中,本地内存使用量继续不断增加,那么这可能是本地代码中的内存泄漏。

5.第三方本地模块或应用程序中的JNI 代码

检查您是否在使用类似数据库驱动程序的任何第三方本地模块。这些本地模块也可以分配本地内存,泄漏可能从这些模块中发生。为了缩小问题的范围,应尝试在没有这些第三方模块的情况下重现问题。例如,可以使用纯java 驱动程序来代替本地数据库驱动程序。

检查应用程序是否使用一些JNI 代码。这也可能造成本地内存泄漏,如果可能的话,您可以尝试在没有JNI 代码的情况下运行应用程序。

6.如果在执行上述步骤后还不能找到本地内存泄漏的根源,那么您需要与JVM 供应商合作来获得一个特殊

的编译版本,它可以跟踪本地内存分配调用,并可提供有关泄漏的更多信息。

返回页首

Jrockit 特定特性

Jrockit 81SP1 和更高版本支持JRA 记录(Java 运行时间分析器)。这对于收集JVM 运行时的信息很有用,将提供应用程序的有关信息,例如,正在运行的GC 数、软/弱/虚引用的数目、热方法,等等。如果JVM 出现性能问题或挂起问题,那么用几分钟进行记录和分析数据就会很有用。有关详细信息,可以在Jrockit 文档中找到。

https://www.wendangku.net/doc/9413356230.html,/wljrockit/docs142/userguide/jra.html

参考文献

?Sun Java 网站

?Sun Java 新闻组

?JRockit 文档

?dev2dev (JRockit)

?JRockit 新闻组

返回页首

是否需要更多帮助?

如果您已经理解这个模式,但仍需要其它帮助,您可以:

1.在https://www.wendangku.net/doc/9413356230.html,上查询AskBEA(例如使用“out of memory”),以查找其它已发布的解决方案。

2.在https://www.wendangku.net/doc/9413356230.html,上,向BEA 的某个新闻组中提出更详细具体的问题。

如果这还不能解决您的问题,并且您拥有有效的技术支持合同,您可以通过登录以下网站来打开支持案例:

https://www.wendangku.net/doc/9413356230.html,。

返回页首

相关文档
相关文档 最新文档