JProfiler帮助文档Download

查找内存泄漏


区分常规内存使用和内存泄漏通常并不简单。然而,过多的内存使用和内存泄漏具有相同的症状,因此可以用相同的方式进行分析。分析分为两个步骤:定位可疑对象并找出这些对象为何仍在堆中。

查找新对象

当具有内存泄漏的应用程序运行时,它会随着时间的推移消耗越来越多的内存。检测内存使用增长的最佳方法是使用VM遥测和"所有对象"和"记录对象"视图中的 差异功能。通过这些视图,您可以确定是否存在问题以及问题的严重程度。有时,调用直方图表中的差异列已经可以让您了解问题所在。

对内存泄漏的任何深入分析都需要在堆漫游器中进行。要详细调查特定用例周围的内存泄漏,"标记堆"功能最为合适。它允许您识别自特定先前时间点以来仍留在堆中的新对象。对于这些对象,您必须检查它们是否仍然合法地留在堆中,或者是否有错误的引用使它们保持活动状态,即使对象不再有任何用途。

隔离您感兴趣的对象集的另一种方法是通过分配记录。当拍摄堆快照时,您可以选择显示所有记录的对象。然而,您可能不希望将分配记录仅限于特定用例。此外,分配记录的开销很高,因此标记堆操作的影响相对较小。最后,堆漫游器允许您在任何选择步骤中使用使用新使用旧超链接选择旧对象和新对象。

分析最大对象

如果内存泄漏填满了可用堆,它将使被分析应用程序中的其他类型内存使用显得微不足道。在这种情况下,您无需检查新对象,只需分析哪些对象最为重要。

内存泄漏可能具有非常缓慢的速率,并且可能在很长时间内不会变得显著。在内存泄漏变得可见之前进行分析可能不切实际。通过JVM内置的功能,在抛出OutOfMemoryError时自动 保存HPROF快照,您可以获得一个快照,其中内存泄漏比常规内存消耗更为重要。事实上,始终添加

-XX:+HeapDumpOnOutOfMemoryError

到VM参数或生产系统中是个好主意,这样您就有办法分析在开发环境中难以重现的内存泄漏。

如果内存泄漏占主导地位,堆漫游器的"最大对象"视图中的顶级对象将包含错误保留的内存。虽然最大对象本身可能是合法对象,但打开它们的支配树将导致泄漏对象。在简单情况下,可能有一个对象将包含大部分堆。例如,如果使用映射来缓存对象并且该缓存从未清除,那么该映射将在最大对象的支配树中显示。

查找从垃圾收集器根到强引用链

只有当对象被强引用时,它才可能成为问题。"强引用"意味着至少有一条从垃圾收集器根到对象的引用链。"垃圾收集器"根(简称GC根)是JVM中垃圾收集器知道的特殊引用。

要查找从GC根到引用链,您可以使用"传入引用"视图或堆漫游器图中的显示到GC根的路径操作。这样的引用链在实践中可能非常长,因此通常可以在"传入引用"视图中更容易地解释。引用从底部指向顶层的对象。只有搜索结果的引用链被展开,同一层次上的其他引用在节点关闭并重新打开或在上下文菜单中调用显示所有传入引用操作之前不可见。

要获取GC根类型和引用节点中使用的其他术语的解释,请使用树图例。

当您在树中选择节点时,非模态树图例会突出显示所选节点中使用的所有图标和术语。单击对话框中的一行将在底部显示解释。

垃圾收集器根的重要类型是来自堆栈的引用,通过JNI创建的本机代码引用以及当前正在使用的资源,如活动线程和对象监视器。此外,JVM还添加了一些"粘性"引用以保持重要系统的稳定。

类和类加载器具有特殊的循环引用方案。当

  • 该类加载器加载的类没有任何活动实例
  • 类加载器本身除了其类之外没有被引用
  • 除了在类加载器的上下文中,没有引用任何java.lang.Class对象

在大多数情况下,类是您感兴趣的GC根路径上的最后一步。类本身不是GC根。然而,在不使用自定义类加载器的所有情况下,将它们视为GC根是合适的。这是JProfiler在搜索垃圾收集器根时的默认模式,但您可以在路径到根选项对话框中更改它。

如果您在解释到GC根的最短路径时遇到问题,可以搜索其他路径。一般来说,不建议搜索到GC根的所有路径,因为它可能会产生大量路径。

与实时内存视图不同,堆漫游器从不显示未引用的对象。然而,堆漫游器可能不仅显示强引用的对象。默认情况下,堆漫游器还保留仅由软引用引用的对象,但消除仅由弱引用、幻影引用或终结器引用的对象。因为软引用在堆耗尽之前不会被垃圾收集,所以它们被包括在内,因为否则您可能无法解释大堆使用。在拍摄堆快照时显示的选项对话框中,您可以调整此行为。

在堆漫游器中拥有弱引用的对象可能对调试目的很有趣。如果您想稍后删除弱引用的对象,可以使用"移除由弱引用保留的对象"检查。

在搜索到GC根的路径时,堆漫游器选项对话框中选择的保留对象的引用类型会被考虑在内。这样,路径到GC根搜索始终可以解释为什么对象在堆漫游器中被保留。在路径到GC根搜索的选项对话框中,您可以将可接受的引用类型扩展到所有弱引用。

消除整个对象集

到目前为止,我们只看了单个对象。通常,您会有许多相同类型的对象,它们是内存泄漏的一部分。在许多情况下,单个对象的分析也适用于当前对象集中的其他对象。对于更一般的情况,其中感兴趣的对象以不同方式被引用,"合并支配引用"视图将帮助您找出哪些引用负责将当前对象集保留在堆中。

支配引用树中的每个节点都会告诉您,如果消除该引用,当前对象集中有多少对象将有资格进行垃圾收集。由多个垃圾收集器根引用的对象可能没有任何支配的传入引用,因此该视图可能只对一部分对象有帮助,或者甚至可能为空。在这种情况下,您必须使用合并传入引用视图并逐个消除垃圾收集器根。