服务器上应该安装JDK,而不是JRE,并且让JDK/bin/目录里的命令允许sudo,遇到问题的时候就有工具可以用了。
1 获取heapdump
1.1 自动获取heapdump
"-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heapdump",加到JVM启动选项里面。我当时没加,因为在profile的时候没发现内存泄漏的迹象,确实也没想到这么有名的一个系统会出这样的错。
1.2 手动获取heapdump
> jps -l查到Java进程对应的pid。
jps --help显示的信息很简陋,可以参考jps - Java Virtual Machine Process Status Tool - docs.oracle.com。
> jmap -heap <pid>
print java heap summary。打印堆内存概要信息。运行稳定以后堆内存还是只增不减的话,那就很可疑。可以继续看看对象统计表。
> jmap -histo <pid>
print histogram of java object heap。打印Java对象的统计表,主要包括数量及占用内存的大小,如果某一类对象占用了很大一部分内存并且只增不减,那就很可疑。如果已经出现过OOME并且没有拿到heap.hprof,不用等到OOME就可以dump堆内存了。
> jmap -F -dump:live,format=b,file=heap.hprof <pid>
dump java heap in hprof binary format。把堆里的Java对象都保存到heap.hprof文件,当然就包括对象之间的引用关系,这样有利于分析问题。dump期间JVM停止响应,如果只有单台机器,那用户就无法访问服务了。
2 分析heapdump
2.1 jhat
jhat不好用,很快就OOME了(这是个Java程序),我当时没有使用-J-Xmx设置jhat允许使用的最大内存,设置过或许能用。2.2 SAP Memory Analyzer
SAP Memory Analyzer挺不错的,是一个Eclipse插件。分析完成之后,在"Dominator Tree"标签页,很容易就能看到占用了最多内存的对象(占用的内存大小包括对象自己以及所有引用的对象)。点开对象引用路径,找到具体占用那么多内存的类,剩下的就是去看代码了,看看对象在哪里初始化及销毁,如果光建立引用而不断开引用,那肯定是内存泄露。现在已经成为http://www.eclipse.org/mat/了,可以在这下载。
3 引起OOME的原因
上面的例子是由一个scope="prototype"的Spring bean引起,这个bean对应的类有两个这样的方法:init() {
dispatcher.addListener(this);
}
destroy() {
dispatcher.removeListener(this);
}
this对应的listener包含Lucene搜索需要的字符串,随着用户量增长,这个字符串会越来越长,目前已经有几十KB。
scope="prototype"的Spring bean,Spring不会调用destroy方法,需要由程序员自己管理。下面是官方文档引用:
/spring-framework-2.5.5/docs/reference/html_single/index.html#beans-factory-scopes-prototype
3.4.2. The prototype scope
There is one quite important thing to be aware of when deploying a bean in the prototype scope, in that the lifecycle of the bean changes slightly. Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, decorates and otherwise assembles a prototype object, hands it to the client and then has no further knowledge of that prototype instance. This means that while initialization lifecycle callback methods will be called on all objects regardless of scope, in the case of prototypes, any configured destruction lifecycle callbacks will not be called. It is the responsibility of the client code to clean up prototype scoped objects and release any expensive resources that the prototype bean(s) are holding onto. (One possible way to get the Spring container to release resources used by prototype-scoped beans is through the use of a custom bean post-processor which would hold a reference to the beans that need to be cleaned up.)
In some respects, you can think of the Spring containers role when talking about a prototype-scoped bean as somewhat of a replacement for the Java 'new' operator. All lifecycle aspects past that point have to be handled by the client. (The lifecycle of a bean in the Spring container is further described in the section entitled Section 3.5.1, “Lifecycle callbacks”.)
4 参考资料
- JDK Tools and Utilities - docs.oracle.com
- Java HotSpot VM Options - www.oracle.com
- Re: 通过GC输出分析内存泄露问题 - Author: willpower - www.iteye.com
- SAP Memory analyzer now available as a separate download - Author: Markus Kohler - scn.sap.com
- Java Memory Analysis - Author: Vedran Lerenc & Krum Tsvetkov - www.sdn.sap.com
- spring 2.5.4 # 3.4.2. The prototype scope - static.springsource.org
=文章版本=
2013042420130517
No comments:
Post a Comment