老生代内存使用量的增长是否总是意味着Java中存在内存泄漏?

7
我们有几个正在生产中运行数据服务的虚拟机,客户端会发送Restful HTTP请求到数据服务,负载有些重(通常每个主机每秒500个请求),并且负载总是均衡分布在每台虚拟机上。我们在所有主机上都有相同的配置(2个CPU,-Xms2048m -Xmx4096m -XX:MaxPermSize=192m -XX:NewSize=512m -XX:MaxNewSize=512M -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError)。
两天前,我们发现这5台虚拟机的老年代堆使用量开始增长(每天300 MB),而其他虚拟机的老年代堆使用量保持不变(大约80 MB)。我们正在试图确定根本原因,我想问一下这是否是内存泄漏问题还是正常情况?老年代内存使用量的增长是否总是意味着Java中存在内存泄漏?
谢谢。
更新:昨天我们刚刚重新启动了这5台主机,在所有主机上老年代堆使用量都恢复正常了,但是在今天早上的高峰负载之后,其中一台主机的老年代堆使用量又开始增长了...

我对这些东西是新手,但我认为GC应该完全防止内存泄漏。换句话说,我认为内存泄漏和Java永远不会出现在同一句话中。 - takendarkk
Java只会回收未被引用的内存。如果你创建一个for循环,不断向列表中添加新对象,你会在说出蓝莓派之前就耗尽了内存。 - Natan Cox
@Takendarkk Java 可能存在内存泄漏问题,但这需要 GC 本身存在缺陷。我相信确实存在过这样的情况,但请不要引用我的话。 - Reinstate Monica
2
@NatanCox 我不确定内存泄漏和简单地使用完所有内存是否真的是一回事。 - takendarkk
2
@Nata 在Java中完全有可能出现内存泄漏--例如,将越来越多的数据放入作为缓存使用的HashMap中,而没有任何逐出/过期策略。 - Matt R
2个回答

10
老年代内存使用量增长是否总是意味着Java中存在内存泄漏?
不一定。
并发标记扫描垃圾收集器在收集期间不会压缩老年代。因此,在足够的内存负载下,可能会出现大量碎片化,使得无法回收足够的内存以允许将对象晋升到老年代空间。
尝试打开以下参数并查看情况:
-XX:+PrintGCDetails -XX:+PrintPromotionFailure -XX:PrintFLSStatistics = 1
查找晋升失败和频繁的全局垃圾收集扫描,这些扫描无法释放大量内存。
如果您正在使用Java 7或更高版本,则可以尝试切换到G1 collector(-XX:+UseG1GC而不是-XX:+UseConcMarkSweepGC)。这是一种避免上述问题的紧凑型收集器。
如果在此之后仍然遇到问题,则应查看您的代码,看看是否有任何东西在不应该时保留了对象引用。
编辑:由于这种情况在某些主机上发生而在其他主机上没有发生,我倾向于认为这是一个代码问题,可能与意外的用户输入有关,因为它只偶尔发生。

非常感谢您的帮助!我们已经设置了-verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCDetails,我检查了过去7天没有晋升失败。是的,我们正在使用Java 7,并将尝试G1。 - zhengyu

0

听起来你所描述的是一种内存泄漏。对我来说,内存泄漏是指内存在没有任何可靠理由或开发者不明白原因的情况下随着时间增加。这可能意味着一些你实际上不需要的数据仍然被保留在内存中,可能是由于错误导致的。

我建议使用一个好的分析器来找到根本原因。这可能是最简单的方法。我不知道是否可以在这里提及产品,但 JProfiler 在我参与的多个项目中拯救了我的团队。


如果内存无限增长,那就好了,但如果它可以减少,那就不一定是好事。 - aled

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接