调试奇怪的内存泄漏 - Java / Tomcat

时间:2011-05-12 21:33:58

标签: java tomcat memory-leaks

我遇到了在Tomcat下运行的Java应用程序的一个非常奇怪的问题。

我们尝试从一个为期一周的sprint中新生产的更新生产代码,该应用程序已运行数月而不会出现打嗝,然后这个新代码使我们的Linux服务器在一段时间后开始交换。

非常奇怪的是,当查看VisualVM的内存使用时,它永远不会超过最大堆大小,JVM不会抛出OutOfMemory,机器只会启动交换,JVM会在此之后继续运行。

所以,它似乎是从某个地方泄漏内存,看起来它来自新代码,但奇怪的是它不在JVM内部,如何调试它的任何想法?

谢谢!

3 个答案:

答案 0 :(得分:2)

交换不是泄漏的最终指标。它是由低物理内存造成的。在Linux上使用vmstat来获取交换使用。尝试使用其他机器,尝试配置 - 交换大小,物理内存大小,地址空间。

如果您确信问题出在您的程序中,请尝试以下操作:

  1. 估算程序使用的中位数和峰值内存。您必须能够考虑与这些指标的所有偏差。如果不能,请继续执行步骤3.

  2. 假设您正确执行了第1步并能够解决所有偏差,您可以排除泄漏(抱歉这些模糊的建议,但调试只有侦探一样好)。您现在应该专注于GC调整。首先,启用GC日志记录。查看您的堆是否实际已满,以及GC将大部分时间用于收集的位置。 This可能是开始优化的良好起点。尝试查看调整GC选项是否有帮助。尝试尝试收集算法,最大/最小堆大小,基因比率等。只有在排除泄漏时才进行实验(步骤1)。

  3. 假设您正确执行了第1步并且无法解释所有偏差,您可以假设您有泄漏。使用内存分析器查看哪些对象最有助于堆大小增长。让一个探查器运行一段时间 - 你的程序会处理一些常规期望得到的请求,然后让它相对隔离。如果内存水平不断增长,您可能会在某处出现泄漏。如果没有,那么它可能不是内存泄漏。你能指出你的程序中可能正在创建它们的部分吗?如果是,请尝试发送仅针对您的程序部分的多个请求。它是否确定性地复制了问题?如果不是,请重复步骤3.如果是,请使用除法并征服并重新应用步骤3,直到找到作为罪魁祸首的类/方法。它也可以是多个部分的某种组合(意味着它们可能看起来无辜,但它们可能形成一个辉煌的犯罪集团)。

  4. 希望这有帮助,如果没有,请在我的帖子上发表评论。

    锻炼身体最好!

答案 1 :(得分:1)

我建议你不要使用jvisualvm来创建堆转储。对于基于Unix的Oracle JVM,通常通过使用kill发送信号3到JVM来完成。

有关详细信息,请参阅http://www.startux.de/index.php/java/45-java-heap-dumpyvComment45

然后,您可以查看模式是否发生变化。

如果你没有从中得到一个想法,那么这可能是因为你从一个非常大的原始字符串(它带有底层的字符串数组)存储一个子字符串,或者因为你坚持使用操作系统资源比如开放数据库连接等。

您检查过连接池看起来不错吗?

答案 2 :(得分:0)

如果您不使用它,我建议使用visual VM version 1.3.2和所有插件。这是早期版本的一大跳跃。

perm gen space会发生什么?

您正在使用的内存设置是什么?当然,最小值和最大值,但是烫发空间大小呢?