2

我想我有内存泄漏。
(他们说第一步是承认问题,对吧?)

无论如何,我想我知道 - 请参阅按区域划分的堆的附加图像:堆区域。绿色是伊甸园,蓝色/红色是 S0/S1,紫色是旧的。我有无限的任期(> 15),在分配内存和溢出到老一代之间经过了很多时间。因此 - 内存泄漏。我认为。

那么 - 问题 - 我如何分析泄漏的内容?如您所见,我的伊甸园非常活跃。很多对象一直在被创建和销毁。

有没有办法只对老一代进行堆转储?或者以某种方式识别完整堆转储中的旧代(如果是,使用什么工具)?

编辑1:澄清:我没有做任何应该在内存中保留对象的事情。我在最初启动后分配的所有东西都应该在年轻时消亡。

Edit2:新发现:我进行了一次堆转储,疯狂地进行了 GC,然后又进行了一次。第二个显示老一代的使用水平显着降低。两者的主要区别在于终结器持有的对象。终结器不是在年轻的 GC 周期中运行吗?他们总是等待清理完整的 GC 吗?

4

2 回答 2

2

看到一些东西传播到老一代并不是一个大问题。在您的老一代达到某个阈值后,将启动完整的 GC。如果这不能回收内存,那么你就有问题了。您看到在年轻收集期间分配的一些内存这一事实不应该是一个令人担忧的问题。

在分配内存和溢出到旧代之间经过了很多时间。因此 - 内存泄漏。我认为

不是真的......仅仅因为内存被添加到旧代并不意味着它是内存泄漏。在年轻的收集过程中,老对象被提升为老对象是正常的做法。正是在那些年轻的收集期间,较旧的对象被添加到旧代中。这可能只是您的应用程序仍在增加。在大规模应用程序中,可能有一些功能并非每天都在使用,这些功能可能会比您预期的更晚进入内存。

话虽如此,如果您真的关心添加到旧代的任何内存并想进一步调查,我建议您在演示环境中运行此应用程序。附加一个分析器(VisualVM 可以工作)和负载测试(JMeter 很好而且免费)你的应用程序。如果您查看对象,您可以了解对象是哪一代。您还想看看当您的老一代达到一个完整 GC 将启动的阈值时会发生什么(通常在 70%-90% 范围内)。如果你的老一代恢复到 20% 的阈值,那么就没有泄漏。在某些情况下,老一代可能永远不会达到启动完整 GC 的地步,而是按照您的预期趋于平稳。负载测试将有助于识别这一点。

如果它没有恢复并且您确认您有内存泄漏,那么您将需要捕获堆转储 (hprof) 并使用诸如 MAT(内存分析器工具)之类的工具来分析转储以找到罪魁祸首。

于 2012-05-17T13:29:29.503 回答
1

使用 JVisualVM(自 Java 6 Build 10 或类似版本以来的 JDK 的一部分),您可以查看内存中对象的类型。这将帮助您追踪泄漏的位置。当然,这需要大量挖掘代码,但这是我用过的最好的工具,它始终可用且可靠。

注意传递的对象,可能是您有一个句柄被保存在一个没有被清除的列表或数组中。我发现如果我在几分钟内观察在 JVisualVM 中创建和保存的对象的数量,我通常会知道在代码中的何处去挖掘未释放的有问题的对象。

于 2012-05-17T11:50:09.833 回答