问题描述
小弟有个web程序使用tomcat发布jvm最大内存分配了1G,使用visualvm观察其内存使用发现了如下情况:1、tomcat启动后内存占用呈比较平稳的波浪曲线300-500M之间波动,此时强制回收,内存会降至200;2、大约两天后占用的内存有所提高500-800之间波动;3、在观察三天,发现内存大概在800-顶峰1G之间波动当接近1G时jvm存在明显的内存回收,但是回收后,内存仍然会占用在800M左右然后继续上升然后接近1G时又产生明显的回收至800如此循环此时的情况会不会出现内存溢出呢?在夜晚基本无人访问的时候为什么仍然占用了这么多的内存且回收幅度很小呢?请各位大神指教非常感谢
解决方案
解决方案二:
内存泄漏是肯定的。
解决方案三:
如果泄露的内存太多,导致正常运行的内存不够用,就会溢出。
解决方案四:
内存泄露请检查会经常新创建的对象各种连接、流,比如网络连接,数据库连接,文件流,用完是否close,是否做好收尾工作,是否断开了一切对其的引用(典型的例子就是一个Map存储每个用户的连接和资源等,但用户走了忘记remove)还有多线程时,每个线程完成任务后是否都能够正常停止,正常被销毁,是否在线程中存在死循环另外注意像Timer这类会开启另外的线程处理任务的类是否能够停止,并得到释放
解决方案五:
请问如何查找到是什么位置出现了内存泄漏呢我使用visualvm的threaddump发现char[]占用最高但是这个无法定位泄露的位置找不到原因对于用户线程确实使用了但是观察没有发现问题正常打开后正常死掉且缓存启动线程的hashtable中缓存的线程也都和visualvm中alive线程一致,说明启动和杀死过程都在程序管理的范围内进行,如果管理不当应该不会能够正常运行5天的情况,会较早的就内存溢出,所以和迷茫,今早发现已经内存溢出了,正常运行了1周的时间
解决方案六:
引用4楼shanzhaikaifa的回复:
请问如何查找到是什么位置出现了内存泄漏呢我使用visualvm的threaddump发现char[]占用最高但是这个无法定位泄露的位置找不到原因对于用户线程确实使用了但是观察没有发现问题正常打开后正常死掉且缓存启动线程的hashtable中缓存的线程也都和visualvm中alive线程一致,说明启动和杀死过程都在程序管理的范围内进行,如果管理不当应该不会能够正常运行5天的情况,会较早的就内存溢出,所以和迷茫,今早发现已经内存溢出了,正常运行了1周的时间
按3楼说的方式一点点的查代码,代码中的内存泄露不是很严重,就泄露一点点,但时间长了就会威胁到系统的正常运行。这也是为什么很多代码自己测试时不出问题,但到正式服务器上面运行一段时间久出现内存泄露的现象,因为家里测试不会那么长时间的让系统一直运行。如何查找到是什么位置出现了内存泄漏,这个比价难,如果系统出现了泄露的异常信息可以帮助查看。一般的就要靠自己分析代码了,重点查看网络连接,数据库读写,文件流读写这几处操作,看操作完是否进行了连接关闭,流关闭。
解决方案七:
你的tomcat和jdk版本是什么?好像tomcat6.x和jdk5配合,存在内存泄露。另外,你说的在夜晚内存还是占用很多,这个只能说明白天用的内存没有立即回收。垃圾回收不是说用完了就立即会回收的。
解决方案八:
引用5楼magi1201的回复:
Quote: 引用4楼shanzhaikaifa的回复:
请问如何查找到是什么位置出现了内存泄漏呢我使用visualvm的threaddump发现char[]占用最高但是这个无法定位泄露的位置找不到原因对于用户线程确实使用了但是观察没有发现问题正常打开后正常死掉且缓存启动线程的hashtable中缓存的线程也都和visualvm中alive线程一致,说明启动和杀死过程都在程序管理的范围内进行,如果管理不当应该不会能够正常运行5天的情况,会较早的就内存溢出,所以和迷茫,今早发现已经内存溢出了,正常运行了1周的时间按3楼说的方式一点点的查代码,代码中的内存泄露不是很严重,就泄露一点点,但时间长了就会威胁到系统的正常运行。这也是为什么很多代码自己测试时不出问题,但到正式服务器上面运行一段时间久出现内存泄露的现象,因为家里测试不会那么长时间的让系统一直运行。如何查找到是什么位置出现了内存泄漏,这个比价难,如果系统出现了泄露的异常信息可以帮助查看。一般的就要靠自己分析代码了,重点查看网络连接,数据库读写,文件流读写这几处操作,看操作完是否进行了连接关闭,流关闭。
我现在初步怀疑是birt报表导致的每次birt访问都伴随曲线的大幅度上升每次内存曲线明显的上升都伴随着birt访问通过log日志也可以找到对应时间点的birt日志。。。。但是是初步诊断也没有充分的证据还有一个现象是之前内存强制回收幅度是很大的但是昨天晚上竟然强制回收后还会快速涨到最大内存数
解决方案九:
引用6楼oh_Maxy的回复:
你的tomcat和jdk版本是什么?好像tomcat6.x和jdk5配合,存在内存泄露。另外,你说的在夜晚内存还是占用很多,这个只能说明白天用的内存没有立即回收。垃圾回收不是说用完了就立即会回收的。
回版主我的版本是tomcat5.5和jdk6运行环境是64位windows2008服务器
解决方案十:
引用7楼shanzhaikaifa的回复:
我现在初步怀疑是birt报表导致的每次birt访问都伴随曲线的大幅度上升每次内存曲线明显的上升都伴随着birt访问通过log日志也可以找到对应时间点的birt日志。。。。但是是初步诊断也没有充分的证据还有一个现象是之前内存强制回收幅度是很大的但是昨天晚上竟然强制回收后还会快速涨到最大内存数
楼主有怀疑点,那就查看下birt报表访问部分的代码,看看数据读写有没有及时关闭,顺带查下其他的涉及文件读写的代码。这种问题一般都不是很好跟,之前其他项目组一个问题,流没有关闭,项目上线两周之后服务器给挂了,报内存溢出,但抛出异常的地方早已不是之前内存泄漏的地方了,好几个人排查将近一天,才找到是之前的一个流没有关闭导致的。
解决方案十一:
引用6楼oh_Maxy的回复:
你的tomcat和jdk版本是什么?好像tomcat6.x和jdk5配合,存在内存泄露。另外,你说的在夜晚内存还是占用很多,这个只能说明白天用的内存没有立即回收。垃圾回收不是说用完了就立即会回收的。
是的应该是白天的内存但是之前内存都很平稳强制回收后回收幅度挺大的感觉这样肯定不会出问题但是昨天晚上突然回收都不好使不断上升且强制回收效果不明显了明显是非要溢出的节奏。。。。我的程序中不涉及文件上传流读写,数据库读取都采用的成熟框架其他系统也一直在用感觉问题不在这里1、用户线程问题我的程序确实启动了用户线程每天都会有大量的用户线程启动和消亡经过几天观察启动和消亡的线程都可以在visualvm和程序中的页面查询到说明管理过程是一致的在控制范围内,而且每天都是如此即使出问题当时也是一致的2、birt问题目前观察每次内存曲线大幅度上升都伴随birt访问
解决方案十二:
引用10楼shanzhaikaifa的回复:
Quote: 引用6楼oh_Maxy的回复:
你的tomcat和jdk版本是什么?好像tomcat6.x和jdk5配合,存在内存泄露。另外,你说的在夜晚内存还是占用很多,这个只能说明白天用的内存没有立即回收。垃圾回收不是说用完了就立即会回收的。是的应该是白天的内存但是之前内存都很平稳强制回收后回收幅度挺大的感觉这样肯定不会出问题但是昨天晚上突然回收都不好使不断上升且强制回收效果不明显了明显是非要溢出的节奏。。。。我的程序中不涉及文件上传流读写,数据库读取都采用的成熟框架其他系统也一直在用感觉问题不在这里1、用户线程问题我的程序确实启动了用户线程每天都会有大量的用户线程启动和消亡经过几天观察启动和消亡的线程都可以在visualvm和程序中的页面查询到说明管理过程是一致的在控制范围内,而且每天都是如此即使出问题当时也是一致的2、birt问题目前观察每次内存曲线大幅度上升都伴随birt访问
感觉你也分析出来一些原因了,可以针对性看看birt的处理,有没有使用大对象或者超大list、map等集合。看看能否分批处理。另外,不建议通过工具强制gc。LZ可以试试,不手动GC,看看会不会真的出现内存溢出。
解决方案十三:
不知道是不是birt
解决方案十四:
引用11楼oh_Maxy的回复:
Quote: 引用10楼shanzhaikaifa的回复:
Quote: 引用6楼oh_Maxy的回复:
你的tomcat和jdk版本是什么?好像tomcat6.x和jdk5配合,存在内存泄露。另外,你说的在夜晚内存还是占用很多,这个只能说明白天用的内存没有立即回收。垃圾回收不是说用完了就立即会回收的。是的应该是白天的内存但是之前内存都很平稳强制回收后回收幅度挺大的感觉这样肯定不会出问题但是昨天晚上突然回收都不好使不断上升且强制回收效果不明显了明显是非要溢出的节奏。。。。我的程序中不涉及文件上传流读写,数据库读取都采用的成熟框架其他系统也一直在用感觉问题不在这里1、用户线程问题我的程序确实启动了用户线程每天都会有大量的用户线程启动和消亡经过几天观察启动和消亡的线程都可以在visualvm和程序中的页面查询到说明管理过程是一致的在控制范围内,而且每天都是如此即使出问题当时也是一致的2、birt问题目前观察每次内存曲线大幅度上升都伴随birt访问
感觉你也分析出来一些原因了,可以针对性看看birt的处理,有没有使用大对象或者超大list、map等集合。看看能否分批处理。另外,不建议通过工具强制gc。LZ可以试试,不手动GC,看看会不会真的出现内存溢出。
引用11楼oh_Maxy的回复:
Quote: 引用10楼shanzhaikaifa的回复:
Quote: 引用6楼oh_Maxy的回复:
你的tomcat和jdk版本是什么?好像tomcat6.x和jdk5配合,存在内存泄露。另外,你说的在夜晚内存还是占用很多,这个只能说明白天用的内存没有立即回收。垃圾回收不是说用完了就立即会回收的。是的应该是白天的内存但是之前内存都很平稳强制回收后回收幅度挺大的感觉这样肯定不会出问题但是昨天晚上突然回收都不好使不断上升且强制回收效果不明显了明显是非要溢出的节奏。。。。我的程序中不涉及文件上传流读写,数据库读取都采用的成熟框架其他系统也一直在用感觉问题不在这里1、用户线程问题我的程序确实启动了用户线程每天都会有大量的用户线程启动和消亡经过几天观察启动和消亡的线程都可以在visualvm和程序中的页面查询到说明管理过程是一致的在控制范围内,而且每天都是如此即使出问题当时也是一致的2、birt问题目前观察每次内存曲线大幅度上升都伴随birt访问
感觉你也分析出来一些原因了,可以针对性看看birt的处理,有没有使用大对象或者超大list、map等集合。看看能否分批处理。另外,不建议通过工具强制gc。LZ可以试试,不手动GC,看看会不会真的出现内存溢出。
不强制gc也会溢出我捉摸着不行起个定时器每小时GC一次至少可以回收birt访问后驻留的内存
解决方案十五:
如果代码没问题,有没有考虑升级tomcat呢?tomcat6.35+jdk1.6.13,这是之前项目用过的配置,也是经历过内存泄露后,升级验证过的配置。定时GC,好吧,没试过。。
解决方案:
引用14楼oh_Maxy的回复:
如果代码没问题,有没有考虑升级tomcat呢?tomcat6.35+jdk1.6.13,这是之前项目用过的配置,也是经历过内存泄露后,升级验证过的配置。定时GC,好吧,没试过。。
这个有用,收藏了。定时GC可以试试,但不是根本,根本还是找出泄漏。
解决方案:
引用14楼oh_Maxy的回复:
如果代码没问题,有没有考虑升级tomcat呢?tomcat6.35+jdk1.6.13,这是之前项目用过的配置,也是经历过内存泄露后,升级验证过的配置。定时GC,好吧,没试过。。
好的这个配置我记下了谢谢版主大人指点
解决方案:
引用16楼shanzhaikaifa的回复:
Quote: 引用14楼oh_Maxy的回复:
如果代码没问题,有没有考虑升级tomcat呢?tomcat6.35+jdk1.6.13,这是之前项目用过的配置,也是经历过内存泄露后,升级验证过的配置。定时GC,好吧,没试过。。好的这个配置我记下了谢谢版主大人指点
呃,恰好接触过,别大人小人的啦,心虚啊~
解决方案:
内存溢出的原因基本找到了,这里和大家分享下我发现问题的分析过程,希望对大家有所用:1、使用mat观察定期观察heapdump快照,发现birt报表和flex的消息队列占用了很大内存;2、通过分析和试验证明flexblazeds消息队列始终缓存数据的原因为用户刷新了浏览器或后退造成了订阅线程无法捕获到退订事件,从而订阅线程不断往queue中写入消息而客户端刷新后停止了轮询,queue中不断堆积消息导致内存溢出,此种情况绝对是偶发的,如果不定时观察分析快照,很难发现,原因为:a、我的系统设计问题待完善且实施人员未培训用户使用指定的系统首页(弹出窗口打开应用,无刷新和后退按钮,但避免不了F5刷新,只是一种无法彻底解决问题而缓解避免问题的方式);b、flexblazeds对浏览器刷新和后退暂无很好的解决方案和处理机制;3、基于2的原因我做了如下工作:a、定时gc,回收birt查询后堆积的内存,确保回收及时(这里仅是保障的方式,其实不强制回收也会安全,毕竟jvm会根据自身回收算法回收);b、改写flexblazeds的processor,对过期消息和客户端超时情况进行捕获和处理,确保无法捕获到关闭事件的客户端订阅线程在2分钟内被程序杀死并清理消息队列,防止无限堆积消息,经测试,暂无问题可以达到预期效果;继续观察2周,如果系统使用稳定,说明以上过程是正确的,问题就解决了,到时结贴发分,谢谢各位的回复,欢迎继续讨论!ps:一年前预料的系统问题采用了回避方式尽量绕行而没有着手彻底从机制上处理,一年后的今天问题终于出现了,我只想说出来混迟早要还的!
解决方案:
你的问题就是一个虚拟机内存配置的问题,一般我们都是配置在512~256之间,不需要配置1G,因为程序跑起来,本身就需要一定的内存,所以你把1G改成512就OK了