1. 收集与分析verbose gc 的错误讯息输出
* 将“verbosegc“参数加入命令提示列启动Server,这将会将GC 的活动信息显示在标准输出/输入,转到stdout/stderr 的档案中。执行应用程序直到问题再次产生。
* 确定在java out of memory 之前,JVM 做如下内容:
* Full GC run:
执行full GC 与所有soft/weak/phantomly reachable 的对象能被移除与这些空间能被回收,在下面网址可以找到更多不同等级对象消耗细项说明:
http://java.sun.com/developer/technicalArticles/ALT/RefObj
你能检查full GC 在out of memory 讯息之前做,接下来的一个讯息显示当GC 已经完成(讯息的格式是依照JVM 定义,检查JVM 的Help message 以了解讯息格式):
[memory ] 7.160: GC 131072K->130052K (131072K) in 1057.359 ms
下面是上面格式的说明(注意:相似格式将会被使用在遍及这份文件):
[memory ] <start>: GC <before>K-><after>K (<heap>K), <total> ms
[memory ] <start> - start time of collection (seconds since jvm start)
[memory ] <before> - memory used by objects before collection (KB)
[memory ] <after> - memory used by objects after collection (KB)
[memory ] <heap> - size of heap after collection (KB)
[memory ] <total> - total time of collection (milliseconds)
然而,这无法使用讯息推断soft/weak/phantomly reachable objects 被移除。假如GC 算法是generational 算法,你会看到verbose 输出像这样:
[memory ] 2.414: Nursery GC 31000K->20760K (75776K), 0.469 ms
上述是nursery GC(或young GC)循环将会提升正在执行的objects 从
nursery(或young space)到old space。这个循环不是重要的分析,更详细项目有关generational 算法可以在JVM 的文件中找到;假如GC 的循环不是发生在java out of memory,这样就可能是JVM 的bug。
* Full compaction:
保证JVM 做适当的紧密程度工作,内存没有碎裂,能防止large objects被配置而且引发一个java out of memory 错误。Java Objects 需要连续的记忆区块,假如没有空的内存区块,那么JVM 将无法配置一个large objects,它将无法符合任何一个空的区块。在这个情况下JVM 将会做full compaction,这样才会有更多连续内存区块能够符合容纳large objects。Compaction 工作包含移动objects(data)从java heap memory 一个区堆到另一个与更新references 到这些objects 的指定的新的位置上。JVM 将不会重排所有objects,除非这是需要的。这是减少GC 循环的暂停时间。我们能否透过verbose gc 讯息检查出内存碎裂的java out of memory。
假如我们看到输出像下列所示,即使仍有空的java heap 而out of memory 还是会发生,是因为内存碎裂的原因。
[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
java.lang.OutOfMemoryError
以上的情况你能够看出max heap 是被设定128MB 与JVM 丢出out ofmemory 当物理内存使用只有72580K,Heap 使用率只有55%。因此,即使当有45%free heap 的时候,在这个案例内存碎裂的影响仍会丢出out of
memory。这是JVM 的bug 或是限制。你必需联络原厂请求协助。
2. 假如JVM 运作是正常的
假如JVM 运作是正常的(的在上述所提及的动作),那么java out of memory可能是应用程序的问题。这个应用程序可能不断泄漏一些java memory,可能是会引发这个问题。或者是应用程序使用过多的objects 它需要更多的heapmemory,以下是可以在这个应用程序中做检查的:
* 应用程序的caching:
假如应用程序在内存中caches java objects,那么我们将需要确定这个cache 是否一直在成长;可能需要在cache 中限制objects 的数量。我们能试着降低这个限制看看是否它会降低java heap 使用量。JAVA soft references 像softly reachable objects 一样使用data caching,当JVM 执行发生out of heap 时,是允许被移除。
* 执行过久的objects:
假如在应用系统里有在Heap 中存在过久的objects,那么我们可以尽可能地试着降低存在的objects。例如:调校HTTP session timeout 将能帮助更快回收无效session objects。
* Memory leaks:
一个memory leak 的范例是在应用系统中使用database connectionpools。当使用connection pools,JDBC statement 与resultset objects必需确定最后有被关闭。这基于事实,这由于从pool 中的connection
objects 使用呼叫close(),将简单connection 传回到pool 以提供重新使用,若没有真正关闭connection 与关联到的statement/resultset
objects。
* 增加java heap:
我们也能试着增加java heap 假如可能的话看是否能解决问题。
3. 假如不是属于前两者的状况:
那们我们需要使用JVMPI(JVM Profiler Interface)基础profiler 像Jprobe 或OptimizeIt 去找出那些Objects 是占住java heap,profilers 也从这些对象正被建立的地方,在java code 里的细节地方上。这份数据不包括在每profiler 上的细节。profiler 文件可能提到有关如何设定与启动应用系统与profilers。 通常,基于JVMPI
的profilers 会有过多负担与大大降低应用系统的效能。因此,在上线环境里使用这些profilers 是不适当的。
For Native OOM Problem:
1. 收集下列信息
* –verbosegc output to monitor the java heap usage.
这将能帮助了解应用系统的java memory 需求。应用系统它可能会需要独立使用的实际java heap,在JVM 启动时预留最大的heap 设定(使用-Xmx 参数在java 命令提示列)与在其他用途是不允许预留内存。在这个Jrockit 的案例,使用-verbose 取代-verbosegc 如同在GC 信息中增加给codegen 信息。* 记录Process virtual memory size 从应用系统被启动时,开始在JVM执行到out of native memory;这将能帮助了解不管是process 真的达到操作系统的大小限制。在Windows 的案例,遵照下列程度监控virtual process size:在开始\执行,输入”perfmon”并且按OK在Performance 图形上方按下“+“按键选择下面项目:
性能对象: Process (不是预设的processor)
从清单选取计数器: Virtual Bytes
从清单选择例项: Select the JVM (java) instance
按 “新增”, 并且 “关闭”
在Unix 或Linux 中对于PID 而言,virtual memory size 能够使用这个命令看到:– ps –p <PID> -o vsz.
在Linux 中每一个java thread 都会有单一个JVM instance 以process方式显示,假如我们选择一个root java process 的PID,这个root javaprocess 可以使用ps 命令加上-forest 参数能够被发现,例如:ps –IU
<user> --forst 将会显示某一特定用户所有process 的ASCII 的树状结构显示,你还可以从这树状结构找到root java。
2. 机器上可用的内存
假如机器RAM 与swap space 是不足,那么操作系统将无法提供更多的内存给process,那么也可能造成out of memory 的结果,确定同机器上的RAM
与swap space 在硬盘空间的加总是足够执行所有的Process。
3. Java heap 调校
假如java heap 的使用都在max heap 之内,那么可以考虑降低max heap的大小,以提供更多的native memory 给JVM 使用。这并不是一个解决方案,但是一个测试的变通方法。因为操作系统限定process 大小,我们需要在javaheap 与native heap 之间达到一个平衡。
4. JVM 的Native memory 使用量
在所有的classes 被加载JVM 的Native memory 使用量是将可预期会趋于平稳,并且方法已被呼叫(code 的产生已经结束)。应用系统通常发生在最初的几个小时,在那之后,JVM 可能在执行时加载class,code 的优化只需要很小的native memory。
为了减少问题,试着取消执行时优化并且检查是否有何不同。* 在这个jrockit 案例,参数-Xnoopt 能够取消执行时优化。在SUN的JVM,参数-Xint 将强制JVM执行在interpreted mode(nocode generation)。假如执行时native memory 使用量仍持续成长,那么问题就可能是在native
code 这边发生。
5. 应用程序中Third party 的native modules 或JNI code
检查不论你是使用Third party 的native module 像是database driver,这些native modules 可能也会配置native memory 并且漏洞可能就是这些modules。基于缩小问题,你能试着重建问题在不使用协助厂商的driver,例如:你能使用pure java drivers 取代native database drivers。检查你的应用系统是否使用JNI 的程序代码,这也可能造成native memory泄漏,并且你也可以尝试执行应用系统尽量不使用JNI 程序代码。
6. 假如照上述步骤仍无法找出native memory 问题
那么你就得向JVM 厂商取得能侦测native memory 配置的特别的版本并且更多有关JVM 上有关内存泄漏的讯息。
结论
系统开发时往往问题发生out of memory,或内存一直成长无法透过GC回到平均值时,这往往不知道该如何处理此问题所在,到底是应用系统问题,还是JDK 发生的问题,以上的这些是针对有关Memory leak 问题进行说明的了解,与问题排解原则加以说明,期望日后再碰到此问题能有所参考依据与检测的方式,以让此类问题能够不再发生。