内存泄漏

终于能发帖了,最近连着几天都看不了, 发帖出去后, 就显示system maintainance. 然后就永远是那样了? 论坛在升级维护?

我的tomcat server最近经常有内存泄漏,可是又不稳定。 用jProfile查了好几天,老说有大量String, char[] 或int[] 没被释放。 可是再查这些object来自哪里, 就报来自没被记录的object, 也就是外部的一些包。没法再查下去了。

而且这种现象也不稳定, 拿jProfile一直跟踪, 有时跟踪了5,6个小时,垃圾回收曲线都是很均匀漂亮的平滑曲线, 而有的时候又是tomcat重启没几分钟, 就出来剧烈的垃圾回收锯齿, 同时cpu和memory都大幅上扬。

有一次用cpu hotspot查, 说是去sqlserver 的一个jdbc call 占用了74%的cpu, 但是这个方法在server没事的时候也是一直执行的啊。

我对性能调优这块有点菜,现在有点头疼,不知接着该从何查起。 各位老大如能指点一下迷津, 不胜感激!

系统用的就是简单的jDon + jsp + jdbcTemp. 没在jdon框架下的class都使用了singleton, 以前一直也比较稳定, 只是最近做了一些改动, 使用了JAI 及时压缩生成一些图片, 还有在model中读取文本文件的内容,存在了model的一个property里。 这些会造成问题吗?


[该贴被little1234于2008-10-07 21:51修改过]

只是看String Char[]是没有用的,要以包名为查看顺序,这样,才能看到你自己的类。

对象占据内存暴涨后,一般gC后恢复到原来水平,但是如果内存泄漏,那么就不可能完全回到原来水平。

如果一个jdbc call占据74%CPU,那么肯定是有问题的,你可以使用缓存来优化到,这样就减轻这个jdbc的执行次数。

总之,性能调试Jprofiler可以定位哪个具体的类占据CPU最多,哪个具体的类耗内存最多,要得到这个结果,除了正常Jprofiler配置外,还需要将这项目源码目录告诉Jprofiler,至于具体如何配,多多摸索。

参考这个贴:
http://www.jdon.com/jivejdon/thread/34422.html

发贴好痛苦呀。试了两天了,经常报错,发不成功。用了各种浏览器, IE6,firefox, IE7, 不知这次行不行?

终于又能上jdon了, 我的原来的IP被封了? 用ADSL换了一个IP地址,才能看jDon, 耽误我好几天时间. :(

谢谢banq的回复, 我会按package再去查的。

现在问题还没解决, 就临时用重启tomcat service的方法应付着。 但是偶尔会碰到, service 刚起, 访问页面,就报如下错误:
2008-10-13 10:16:39,937 - 0 ERROR [ajp-8009-6] util.Debug - com.jdon.aop.AopClient
java.lang.NullPointerException

这个时候, 我通常再重启一下tomcat service就好了。 请问这是什么原因, 应该如何改动?

另外, 关于这个jive论坛使用功能的一个小建议, 我有好几次发帖, 都说操作失败, 计时。。。。 然后就死在这个计时页面, 回也回不去。 而刚才打的内容也没法选中copy了。这样用起来比较麻烦。 不知能否更改一下?

最近发表回复或发帖失败的几率较大,有时一两天都发不成功一个贴,难道现在这个论坛对客户端的软件有什么要求我没满足?

谢谢。

2008-10-13 10:16:39,937 - 0 ERROR [ajp-8009-6] util.Debug - com.jdon.aop.AopClient
java.lang.NullPointerException at com.jdon.aop.joinpoint.AdvisorChainFactory.create(AdvisorChainFactory.java:69)
at com.jdon.aop.AopClient.invoke(AopClient.java:92)
at com.jdon.bussinessproxy.dyncproxy.DynamicProxyWeaving.invoke(DynamicProxyWeaving.java:62)
at $Proxy0.getLatestUpdates(Unknown Source)
at org.apache.jsp.g.book.index_jsp._jspService(index_jsp.java:232)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:393)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.jdon.util.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:92)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
at org.apache.coyote.ajp.AjpAprProcessor.process(AjpAprProcessor.java:419)
at org.apache.coyote.ajp.AjpAprProtocol$AjpConnectionHandler.process(AjpAprProtocol.java:394)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1508)
at java.lang.Thread.run(Unknown Source)
2008-10-13 10:16:39,968 - 31 ERROR [ajp-8009-21] util.Debug - com.jdon.bussinessproxy.dyncproxy.DynamicProxyWeaving
java.lang.Exception: java.lang.NullPointerException
at com.jdon.aop.AopClient.invoke(AopClient.java:98)
at com.jdon.bussinessproxy.dyncproxy.DynamicProxyWeaving.invoke(DynamicProxyWeaving.java:62)
at $Proxy0.getLatestUpdates(Unknown Source)
at org.apache.jsp.g.book.index_jsp._jspService(index_jsp.java:232)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:393)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.jdon.util.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:92)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
at org.apache.coyote.ajp.AjpAprProcessor.process(AjpAprProcessor.java:419)
at org.apache.coyote.ajp.AjpAprProtocol$AjpConnectionHandler.process(AjpAprProtocol.java:394)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1508)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
at java.util.ArrayList.addAll(Unknown Source)
at com.jdon.aop.joinpoint.AdvisorChainFactory.create(AdvisorChainFactory.java:58)
at com.jdon.aop.AopClient.invoke(AopClient.java:92)
... 27 more

看来现在jdon只支持IE7, 而且对帖子的长度有限制。可惜报错时,只说是system error,不说是帖子超长了, :(

传上几张jProfiler的截屏,系统在运行5个小时前,GC的曲线看上去都挺漂亮的, 5个小时的时候突然出现问题了。 这个时候我自己的class个数好像也不多啊,请问banq老师,在这种情况下,我关注的重点应该是哪?




>com.jdon.aop.joinpoint.AdvisorChainFactory.create(AdvisorChainFactory.java:69)

我看了一下AdvisorChainFactory.java的69行,是一个大刮号,不可能出现空指针错误,是不是你的Jdon框架版本不对啊。

建议,你重新下载新的tomcat,新的Jdon框架,新的JDBC驱动,重新开始搞一下,Java这玩艺由于环节太多,就象windows操作系统,重新安装一遍,很多奇怪问题就会没有。

看了你Jprofiler,发现有一个arrays为什么占据160M左右?
关键这些对象占据内存后,通过GC垃圾回收是否能够清零或减少,如果减少后的实例个数还是在增长,就有内存泄漏嫌疑...

另外,监控一下thread view中,是否有死锁,dead lock,特别是运行5小时那段时间。


这个论坛发言,支持chrome IE7/IE8 FIrxfox 3.0都支持,测试过了。如果你有问题,估计你浏览器中毒了。

还有,如果5小时候,出问题,一定要找到出问题的LOG,你贴的上面LOg,我看是tomcat重新启动后,如果你的客户端还在访问,才会出现的错误。

打开你的LOG,搜索第一 ERROR,那就是所有错误的罪魁祸首 。

死锁看了, 没有。

160M的arrays 是因为有一堆String没释放掉。 可是到底由谁引起的,我不知道怎么确定。在跟踪过程中,发现BigDecimal 和BigInteger 类好像持续增长的情况较明显,但是也不是量很大。 我的DAO层用的是jdon的jdbcTemp,我数据库中的key都比较大,通常为numeric(8)以上,我的model里key的值用的是string, 好象jdbcTemp里有将key值转为BigDecimal的过程。 不知道会不会是这些又是BigDecimal又是String 的key 被cache了,没释放掉? 我对jDon原理不懂,胡乱猜测的,猜的不对请原谅。

通常在memory剧增后,就是5小时左右,server并不是马上就完蛋了。这个时候,memory和CPU大概都50%左右,然后持续增长,到CPU 90%+以上,也还能工作一段时间,只是这时500的错误几率增加了许多,刚才想看在5小时左右Log里的第一Error,没看到,不过我会继续观察。

重装tomcat等也许是一个好办法,我没想到。会去试试。 谢谢banq的快速支持。
[该贴被little1234于2008-10-14 15:08修改过]



没看到你的code就是乱猜

很有可能是你在处理数据库数据的时候做了大量的"literal"操作.活着你大量的使用了intern method.这样导致了大量的字串被intern,进入了permgen.这样的字串是无法被GC的。

改进方法:
0.查查自己的code,看看是不是有上述的问题。如果没有。就是你用的第三方软件有问题。你没办法从根上解决了
1。改变perm大小 比如 -XX:MaxPermSize=256m 。这个设的足够大之后能放下你所有的interned string.就不会有内存问题了。
2。换server或者JVM. Jetty比较好。Jrocket也可以试试。

谢谢 lgao. 不是太明白你指的"literal"操作和intern method到底是怎么回事。

不过,我已经开始逐行检查我的代码。发现在data Model里,因为有些数据在数据库里定义的是varchar, 而不是nvarchar, 所以在读出的时候,写了 strIn = new String(strIn.getBytes("iso-8859-1"), "utf-8") 好把字符转成中文。 是不是这就是你所说的"literal"操作?

做一下改动,继续跟踪检查。

intern()主要是类似于hashcode的用途。用于字串的比较。比如说
“abcde@"和“abcde@"如果在heap中就需要一个一个字符比较。效率低。如果这个字串被intern了,这个字串就要被放在perm里面。那里面有个String pool,所有的literal和字符常量都是被intern的。字符变量如果调用intern()也会被intern.

这样做的好处就是在字串比较的时候速度快。就和比较hashCode一样,效率高。 比如:

public boolean comp(String str1, String str2) {
return str1.inter() == string2.inter();
}

这样写的坏处就是程序写得不好容易失控,比如说str1, str2来自于无限的集合。

现在尤其是大量的应用xml parsing.很多程序员愿意这样写,这样做“看起来很美”,就是可以宣布自己的程序效率多高多快,可以向客户吹嘘,但结果就是会有你那样的问题。160M的字符没办法被GC。估计这个问题不是你引起的,就是某些第三方软件引起的。如果使用IBM,或者BEA的JVM,他们利用别的机制进行GC,就不会产生这样的问题。

literal就是你在程序里敲的引号里的东西。比如说
String x = "abc" + "xyz";
"abc", "xyz"在perm中。x在heap中


[该贴被lgao于2008-10-16 11:38修改过]

当然了,上面说的只是一方面。另外很大的可能性就是你已经指出来的,cache问题。在GC过程中。如果heap中的变量长期得不到释放。为了减短GC的CPU占有量(保证在一个GC周期中遍历完所有应该遍历的内容),一部分长期仍然被引用的对象就被认为是不能GC的,放到了perm中,从而下次GC时候不再考虑该部分内容。

这个也很有可能。如果是这个原因,你需要做的就是把cache关上。或者,对于大量的数据,换种别的方法进行cache.

关于这部分内容有很多文章可以阅读。你仔细研究一下,如果能把问题彻底解决,也是让我们这些旁观者多了一些经验。

中文问题不是用strIn = new String(strIn.getBytes("iso-8859-1"), "utf-8")这样来解决的。

参考本站 中文编码终极解决方案 一文,Jdon框架案例中也没有这样做的。

把中文问题改了, literal 的code等也彻底查了一遍, 换了一个jdbc driver, 好象好点,可是问题依旧, 过一阵子就得重启一次tomcat.

这段时间正在学习jdon的各种例子,觉得是不是以前的代码没用structs等,业务层和表现层都在jsp里导致的呢?

因为structs, hibernate等对于我来说都是陌生的, 只能学习比较简单的例子,按照jdonRails里提供的视频教程做了一遍, 一个表是能成功的, 但是如果加上另一个与之关联的对象, 更新时就出错了。 不知道banq大哥可不可以把你给出的例子稍微加一点能体现怎样处理关系的代码, 这样对于我们初学者就会快很多了。 比如, 除了user外, 还有一个对象course, 一个user可以有多个course。

谢谢!