涉及对象之间引用的任何堆分析都需要堆快照,因为无法向JVM询问对对象的传入引用是什么 - 您必须遍历整个堆来回答该问题。从该堆快照,JProfiler创建一个内部数据库,该数据库经过优化,可以生成在堆walker中提供视图所需的数据。
堆快照有两个来源:JProfiler堆快照和HPROF堆快照。JProfiler堆快照支持堆walker中的所有可用功能。概要分析代理使用概要分析接口JVMTI迭代所有引用。如果配置文件JVM在另一台计算机上运行,则所有信息都将传输到本地计算机,并在那里执行进一步的计算。HPROF快照是使用JVM中的内置机制创建的,并以JProfiler可以读取的标准格式写入磁盘。
在堆walker的概述页面上,您可以选择是否应创建JProfiler堆快照或HPROF堆快照。默认情况下,建议使用JProfiler堆快照。HPROF堆快照在另一章中讨论的特殊情况下非常有用。
堆walker由几个视图组成,这些视图显示所选对象集的不同方面。在获取堆快照之后,您正在查看堆上的所有对象。每个视图都有导航操作,用于将某些选定对象转换为当前对象集。堆walker的标题区域显示有关当前对象集中包含的对象数的信息。
最初,您正在查看“类”视图,该视图类似于实时内存部分中的“所有对象”视图 。通过选择一个类并调用 Use-> Selected Instances,可以创建一个仅包含该类实例的新对象集。在堆walker中,“using”始终意味着创建一个新的对象集。
对于新的对象集,显示堆walker的classes视图不会很有趣,因为它实际上只是将表过滤到先前选择的类。相反,JProfiler使用“新建对象集”对话框建议另一个视图。您可以取消此对话框以放弃新对象集并返回上一个视图。建议使用传出引用视图,但您也可以选择其他视图。这仅适用于最初显示的视图,之后您可以在堆walker的视图选择器中切换视图。
标题区域现在告诉您有两个选择步骤,包括用于计算保留和深度大小的链接或用于使用当前对象集保留的所有对象的链接。后者将添加另一个选择步骤并建议类视图,因为该对象集中可能存在多个类。
在堆步行器的下半部分,列出了到此为止的选择步骤。单击超链接将返回任何选择步骤。也可以使用工具栏中的“ 转到开始”按钮访问第一个数据集 。如果需要在分析中回溯,工具栏中的后退和前进按钮非常有用。
堆walker顶部的视图选择器包含五个视图,显示当前对象集的不同信息。第一个是“类”视图。
类视图类似于实时内存部分中的“所有对象”视图,并且具有可以将类分组到包中的聚合级别选择器。此外,它还可以显示课程的估计保留大小。这是从堆中删除所有类的实例时将释放的内存量。如果单击“ 计算估计保留大小”超链接,则会添加新的“ 保留大小”列。显示的保留大小是估计的下限,计算确切的数字会太慢。如果您确实需要确切的数字,请选择感兴趣的类或包,并使用 新对象集标题中的计算保留和深度大小超链接。
根据您选择的一个或多个类或包,您可以选择实例本身,关联java.lang.Class
对象或所有保留对象。双击是最快的选择模式,并使用选定的实例。如果有多种选择模式可用,则在此情况下,视图上方会显示“ 使用”下拉菜单。
解决与类加载器相关的问题时,通常必须按类加载器对实例进行分组。“ 检查”选项卡提供了“按类加载器分组”检查,该检查在类视图中可用,因为它在该上下文中尤为重要。如果执行该分析,则顶部的分组表将显示所有类加载器。选择类加载器会在下面的视图中相应地过滤数据。当您切换到堆walker的其他视图时,分组表将保持不变,直到您执行另一个选择步骤。然后,类加载器选择成为该选择步骤的一部分。
在缩小内存泄漏的嫌疑人或尝试减少内存消耗时,分配对象的信息可能很重要。对于JProfiler堆快照,“分配”视图显示分配调用树以及已记录分配的那些对象的分配热点。其他对象在分配调用树中的“未记录对象”节点中分组。对于HPROF快照,此视图不可用。
与在类视图中一样,您可以选择多个节点并使用顶部的“ 使用所选项”按钮创建新的选择步骤。在“分配热点”视图模式下,您还可以选择后面跟踪中的节点。这将仅选择在以所选回溯跟踪结束的调用堆栈上分配的关联顶级热点中的对象。
JProfiler在记录分配时可以保存的另一条信息是分配对象的时间。堆walker中的“Time”视图显示当前对象集中所有记录实例的分配时间的直方图。您可以单击并拖动以选择一个或多个间隔,然后使用“ 使用所选项”按钮创建新的对象集。
要更精确地选择时间间隔,您可以指定一系列 书签。然后将标记第一个和最后一个所选书签之间的所有对象。
除时间视图外,分配时间在参考视图中显示为单独的列。但是,默认情况下不启用分配时间记录。您可以在时间视图中直接打开它,也可以编辑配置文件设置对话框的“ 内存配置文件”选项卡上的设置。
最大的对象视图显示当前对象集中最重要的对象的列表。此上下文中的“Biggest”表示如果从堆中删除它们将释放大多数内存的对象。该大小称为保留大小。相反,深度大小是通过强引用可到达的所有对象的总大小。
可以展开每个对象以显示对此对象保留的其他对象的传出引用。通过这种方式,您可以递归地展开保留对象的树,如果要移除其中一个祖先,这些树将被垃圾收集。这种树被称为“支配树”。此树中为每个对象显示的信息类似于传出参考视图,只是显示了仅限制的引用。
并非所有支配对象都被其支配者直接引用。例如,请考虑下图中的引用:
对象A支配对象B1和B2,并且它没有对象C的直接引用.B1和B2都引用C.B1和B2都不支配C,但A支配。在这种情况下,B1,B2和C在支配树中被列为A的直接子节点,而C不会被列为B1和B2的子节点。对于B1和B2,显示它们所在的A中的字段名称。对于C,“[传递参考]”显示在参考节点上。
在支配树中每个参考节点的左侧,大小条显示目标对象仍保留顶级对象的保留大小的百分比。当您深入到树中时,数字会减少。在视图设置中,您可以将百分比基数更改为总堆大小。
支配树具有内置截断,消除了保留大小低于父对象保留大小0.5%的所有对象。这是为了避免过多的小型主导对象列表,这些对象会分散对重要对象的注意力。如果发生这样的截止,将显示一个特殊的“cutoff”子节点,通知您有关此级别上未显示的对象数,它们的总保留大小以及单个对象的最大保留大小。
主导树也可以将最大的对象分组到类中,而不是显示单个对象。视图顶部的分组下拉列表包含一个激活此显示模式的复选框。此外,您可以在顶层添加类加载器分组。在计算最大对象之后应用类加载器分组,并显示谁加载了最大对象的类。如果要分析一个特定类加载器的最大对象,可以先使用“按类加载器分组”检查。
最大对象视图上方的视图模式选择器允许您切换到备用可视化:树图,将所有支配对象显示为一组嵌套矩形。
在树形图中,每个矩形表示一个主导对象,其面积与其保留大小成比例。与树形成对比,树形图为您提供了支配树中所有叶子的平面透视图。如果您对大数组感兴趣,可以使用树形图快速找到它们,而无需深入挖掘树的分支。此外,树形图可以让您全面了解支配对象的相对重要性以及堆上的对象大小分布。
在树形图的右下角,您可以看到树映射表示的整个堆的总百分比。如果尚未放大,则由于保留大小的内部阈值,堆的剩余部分由未进入最大对象列表的对象控制。
与之前的视图不同,仅当您执行了至少一个选择步骤时,参考视图才可用。对于初始对象集,这些视图没有用,因为传入和传出的引用视图显示所有单个对象,并且合并的引用视图只能针对一组焦点对象进行解释。
传出引用视图类似于调试器在IDE中显示的视图。打开对象时,您可以看到原始数据和对其他对象的引用。可以选择任何引用类型作为新对象集,并且可以一次选择多个对象。与在类视图中一样,您可以选择保留的对象或关联的java.lang.Class
对象。如果所选对象是标准集合,则还可以通过单个操作选择所有包含的元素。对于类装入器对象,可以选择所有已加载的实例。
默认情况下不显示具有空引用的字段,因为该信息可能会分散内存分析的注意力。如果要查看所有字段以进行调试,可以在视图设置中更改此行为。
除了简单选择显示的实例外,传出引用视图还具有 强大的过滤功能。对于实时会话,传出和传入参考视图都具有高级操作和显示功能,这将在同一章中讨论。
传入引用视图是解决内存泄漏的主要工具。要找出对象未被垃圾收集的原因,“ 显示路径到GC根”按钮将找到垃圾收集器根的参考链。关于内存泄漏的章节有关于这个重要主题的详细信息。
检查许多不同对象的引用可能很繁琐,因此JProfiler可以向您显示当前对象集中所有对象的合并传出和传入引用。默认情况下,引用按类聚合。如果类的实例由同一个类的其他实例引用, 则会插入一个特殊节点,该 节点显示原始实例以及这些类递归引用中的实例。此机制会自动折叠常见数据结构中的内部引用链,例如链接列表中的内部引用链。
您还可以选择显示按字段分组的合并参考。在这种情况下,每个节点都是引用类型,例如类的特定字段或数组的内容。对于标准集合,会破坏累积的内部引用链,因此您会看到引用类型,如“java.lang.HashMap的映射值”。与类聚合不同,此机制仅适用于来自JRE标准库的显式支持的集合。
在“合并的传出引用”视图中,实例计数引用引用的对象。在“合并传入引用”视图中,您会在每行上看到两个实例计数。第一个实例计数显示沿此路径引用当前对象集中的实例数。节点左侧的条形图标可视化此分数。箭头图标指向保存对父节点的引用的对象后的第二个实例计数。执行选择步骤时,您可以选择是否要从当前对象集中选择以所选方式引用的对象,或者您是否对具有所选引用的对象感兴趣 - 引用持有者。
使用“Merged dominating references”视图,您可以找出必须删除哪些引用,以便可以对当前对象集中的部分或全部对象进行垃圾回收。主导参考树可以被解释为最大对象视图中的支配树的合并逆,为类聚合。参考箭头可能不表示两个类之间的直接引用,但是其间可能存在其他类,其中包含非支配引用。在多个垃圾收集器根的情况下,当前对象集中的一些或所有对象可能不存在主导引用。
默认情况下,“Merged dominating references”视图显示传入的主导引用,通过打开树,您可以访问GC根保留的对象。有时,参考树可能会沿着许多不同的路径导致相同的根对象。通过在视图顶部的下拉列表中选择“GC根到对象”视图模式,您可以看到反向透视图,其中根位于顶层,而当前对象集中的对象位于叶节点中。在这种情况下,引用从顶层到叶节点。哪个视角更好取决于您要消除的引用是接近当前对象集还是接近GC根。
“检查”视图本身不显示数据。它提供了许多堆分析,这些分析根据其他视图中不可用的规则创建新对象集。例如,您可能希望查看本地线程保留的所有对象。这在参考视图中是不可能的。检查分为几类,并在其说明中进行了解释。
检查可以将计算出的对象集划分为组。组显示在堆walker顶部的表中。例如,“重复字符串”检查将重复的字符串值显示为组。如果您在参考视图中,则可以在java.lang.String
下面看到具有所选字符串值的实例。最初,选择组表中的第一行。通过更改选择,可以更改当前对象集。组表的“ 实例计数”和“ 大小”列会告诉您选择行时当前对象集的大小。
组选择不是堆步行器中的单独选择步骤,但它成为检查所做选择步骤的一部分。您可以在底部的选择步骤窗格中查看组选择。更改组选择时,将立即更新选择步骤窗格。
每个创建组的检查决定哪些组在检查的上下文中最重要。由于这并不总是对应于其他列之一的自然排序顺序, 因此组表中的“ 优先级”列包含一个数值,用于强制执行检查的排序顺序。
对于大堆计算,检查可能很昂贵,因此结果会被缓存。通过这种方式,您可以返回历史记录并查看先前计算的检查结果,而无需等待。
实例的最真实表示及其引用是一个图表。虽然图形具有低视觉密度并且对于某些类型的分析是不切实际的,但它仍然是可视化对象之间关系的最佳方式。例如,循环引用很难在树中解释,但在图中很明显。此外,将传入和传出的引用放在一起可能是有益的,这在树结构中是不可能的,在树结构中您可以看到其中一个或另一个。
堆walker图不会自动显示当前对象集中的任何对象,也不会在更改当前对象集时清除它。通过选择一个或多个实例并使用“ 在图形中显示”操作,可以从传出引用视图,传入引用视图或最大对象视图中手动将所选对象添加到图形中。
默认情况下,图表中的包名称会缩短。与CPU调用图一样,您可以在视图设置中启用完整显示。参考文献被绘制为箭头。如果将鼠标移到参考上,将显示工具提示窗口,其中显示特定参考的详细信息。从参考视图手动添加的实例具有蓝色背景。最近添加了一个实例,背景颜色越深。垃圾收集器根有红色背景,类有黄色背景。
默认情况下,参考图仅显示当前实例的直接传入和传出引用。您可以通过双击任何对象来展开图形。这将扩展该对象的直接传入或直接传出引用,具体取决于您移动的方向。通过实例左侧和右侧的扩展控件,您可以选择性地打开传入和传出引用。如果您需要回溯,请使用撤消功能恢复图形的先前状态,这样您就不会被太多节点分心。要修剪图形,可以删除所有未连接的节点,甚至删除所有对象。
与传入引用视图一样,该图具有“ 显示路径到GC根”按钮,该按钮将一个或多个引用链扩展到垃圾收集器根( 如果可用)。此外,如果选择了两个实例,则“ 两个选定节点之间的查找路径”操作处于活动状态。它可以搜索有向和无向路径,也可以选择沿弱引用。如果找到合适的路径,则以红色显示。
获取堆快照时,可以指定控制初始对象集的选项。如果已记录分配,则“ 选择记录的对象”复选框会将最初显示的对象限制为已记录的对象。这些数字通常与实时内存视图中的数字不同,因为堆栈walker会删除未引用的对象。堆栈快照中仍存在未记录的对象,它们不会显示在初始对象集中。通过进一步的选择步骤,您可以访问未记录的对象。
此外,堆walker执行垃圾收集并删除弱引用的对象,软引用除外。这通常是可取的,因为在查找仅有强引用对象相关的内存泄漏时,弱引用的对象会分散注意力。但是,在您对弱引用对象感兴趣的情况下,您可以告诉堆walker保留它们。JVM中的四个弱引用类型是“软”,“弱”,“幻像”和“终结器”,您可以选择其中哪些应足以在堆快照中保留对象。
如果存在,可以使用堆步行器中的“弱引用”检查从当前对象集中选择或删除弱引用的对象。
通常,您希望查看为特定用例分配的对象。虽然您可以通过启动和停止围绕该用例的分配记录来实现此目的,但有一种更好的方法可以减少开销并保留分配记录功能用于其他目的:在堆步行器概述上公布的Mark Heap操作这也可以在“性能分析” 菜单中使用,或者作为触发器操作将堆上的所有对象标记为“旧”。当您获取下一个堆快照时,现在可以清楚“新”对象应该是什么。
如果存在先前的堆快照或标记堆调用,堆walker的标题区域将显示新的实例计数和两个标题为Use new和Use old的链接,这些链接允许您选择自该点以来已分配的实例。时间,或之前分配的幸存实例。此信息可用于每个对象集,因此您可以先向下钻取并稍后选择新的或旧的实例。