Memory Profiling——内存分析

有两种方法可以获取有关堆上对象的信息。一方面,分析代理可以跟踪每个对象的分配和垃圾收集。在JProfiler中,这称为“分配记录”。它告诉您已分配对象的位置,还可用于创建有关临时对象的统计信息。另一方面,JVM的分析界面允许分析代理采用“堆快照”,以便检查所有活动对象及其引用。需要此信息才能理解为什么不能对对象进行垃圾回收。

分配记录和堆快照都是昂贵的操作。分配记录对运行时特性有很大影响,因为必须对java.lang.Object构造函数进行检测,并且垃圾收集器必须不断向分析界面报告。这就是默认情况下不记录分配的原因,您必须明确地开始和停止记录。获取堆快照是一次性操作。但是,它可以暂停JVM几秒钟,并且对获取的数据的分析可能需要相对长的时间,可以根据堆的大小进行缩放。

JProfiler将其内存分析分为两个视图部分:“实时内存”部分显示可以定期更新的数据,而“Heap walker”部分显示静态堆快照。分配记录在“实时存储器”部分中控制,但记录的数据也由堆步行器显示。

java 获得jvm内存大小 java获取内存快照_java

可以通过内存分析解决的三个最常见的问题是: 查找内存泄漏,减少内存消耗并减少临时对象的创建。对于前两个问题,您将主要使用堆walker,主要是通过查看谁持有JVM中的最大对象以及它们的创建位置。对于最后一个问题,您只能依赖显示记录分配的实时视图,因为它涉及已经被垃圾回收的对象。

一、跟踪实例计数——Tracking instance counts

要全面了解堆上的对象,“所有对象”视图会显示所有类及其实例计数的直方图。此视图中显示的数据不是通过分配记录收集的,而是通过执行仅计算实例计数的小堆快照来收集的。堆越大,执行此操作所需的时间越长,因此视图的更新频率将根据测量的开销自动降低。当视图未处于活动状态时,不会收集任何数据,并且视图不会产生任何开销。与大多数动态更新的视图一样,冻结工具栏按钮可用于停止更新显示的数据。

java 获得jvm内存大小 java获取内存快照_jprofiler_02

另一方面,“记录的对象”视图仅显示在开始分配记录后已分配的对象的实例计数。停止分配记录时,不会添加新分配,但会继续跟踪垃圾回收。通过这种方式,您可以查看特定用例在堆上保留的对象。请注意,对象可能不会长时间被垃圾回收。使用“ 运行GC”工具栏按钮可以加快此过程。

在查找内存泄漏时,您经常需要比较实例计数。要对所有类执行此操作,可以使用视图的差异功能。使用“ 标记当前”工具栏按钮,将插入“ 差异”列,实例计数的直方图将以绿色显示标记时的基线值。

java 获得jvm内存大小 java获取内存快照_jprofiler_03

对于选定的类,您还可以使用上下文菜单中的“ 添加选择到类跟踪器”操作来显示时间分辨图 。

二、分配点——Allocation spots

当分配记录处于活动状态时,JProfiler会在每次分配对象时记录调用堆栈。它不使用精确的调用堆栈,例如来自堆栈行走API,因为这将非常昂贵。相反,使用为CPU分析配置的相同机制。这意味着根据调用树过滤器过滤调用堆栈,并且实际分配点可以位于调用堆栈中不存在的方法中,因为它来自忽略或压缩过滤的类。但是,这些更改在直观上很容易理解:压缩过滤方法负责在进一步调用压缩过滤类时进行的所有分配。

如果使用抽样,分配点将变为近似值,可能会造成混淆。与时间测量不同,您通常清楚地知道可以分配某些类的位置以及不分配的位置。因为抽样绘制的是统计图片而非精确图片,您可能会看到看似不可能的分配点,例如java.util.HashMap.get分配您自己的一个类。对于任何类型的分析,其中确切的数字和调用堆栈很重要,建议将分配记录与仪器一起使用。

就像CPU分析一样,分配调用堆栈表示为一个调用树,只有分配计数和分配的内存而不是调用计数和时间。与CPU调用树不同,分配调用树不会自动显示和更新,因为树的计算更加昂贵。JProfiler不仅可以为所有对象显示分配树,还可以为选定的类或包显示分配树。与其他选项一起,这是在您要求JProfiler从当前数据计算分配树后显示的选项对话框中配置的。

java 获得jvm内存大小 java获取内存快照_java_04

CPU调用树的一个有用属性是,您可以从上到下遵循累积时间,因为每个节点都包含在子节点中花费的时间。默认情况下,分配树的行为方式相同,这意味着每个节点都包含子节点所做的分配。即使分配仅由调用树中内部的叶节点执行,数字也会传播到顶部。通过这种方式,您可以在打开分配调用树的分支时始终查看哪条路径值得研究。“自我分配”是由节点而不是其后代实际执行的那些。与在CPU调用树中一样,百分比条用不同的颜色显示它们。

java 获得jvm内存大小 java获取内存快照_java 获得jvm内存大小_05

在分配调用树中,通常有很多节点根本不执行任何分配,尤其是当您显示所选类的分配时。这些节点仅用于向您显示通向实际分配的节点的调用堆栈。这样的节点在JProfiler中被称为“桥接”节点,并且在上面的屏幕截图中显示为灰色图标。在某些情况下,分配的累积可能会妨碍您只想看到实际的分配点。分配树的视图设置提供了为此目的显示未填充数字的选项。如果激活,桥节点将始终显示零分配并且没有百分比条。

java 获得jvm内存大小 java获取内存快照_垃圾收集_06

分配热点视图与分配调用树一起填充,并允许您直接关注负责创建所选类的方法。与记录对象视图一样,分配热点视图支持标记当前状态并观察随时间的差异。差异列将添加到视图中,以显示自 调用“ 标记当前值”操作以来热点的变化程度。由于默认情况下不会定期更新分配视图,因此必须单击“ 计算”工具栏按钮以获取新数据集,然后将其与基准值进行比较。选项对话框中提供了自动更新,但不建议用于大堆大小。

java 获得jvm内存大小 java获取内存快照_jprofiler_07

三、分析分配的类——Analyzing allocated classes

在计算分配树和分配热点视图时,您必须指定要预先查看其分配的类或包。如果您已经专注于特定的课程,这很有效,但在尝试查找没有任何预先设想的分配热点时不方便。一种方法是开始查看“记录的对象”视图,并使用上下文菜单中的操作切换到所选类或包的分配树或分配热点视图。

java 获得jvm内存大小 java获取内存快照_垃圾收集_08

另一种方法是从所有类的分配树或分配热点开始,并使用 Show classes操作显示所选分配点或分配热点的类。

java 获得jvm内存大小 java获取内存快照_java 获得jvm内存大小_09

分配的类的直方图显示为 调用树分析。此操作也适用于其他调用树分析。

java 获得jvm内存大小 java获取内存快照_垃圾收集_10

类分析视图是静态的,并且在重新计算分配树和热点视图时不会更新。“ 重新加载分析”操作将首先更新分配树,然后从新数据重新计算当前分析视图。

四、分析垃圾收集对象——Analyzing garbage collected objects

分配记录不仅可以显示活动对象,还可以保存有关垃圾收集对象的信息。这在调查临时分配时很有用。分配大量临时对象会产生很大的开销,因此降低分配率通常会大大提高性能。

要在记录的对象视图中显示垃圾收集的对象,请将活动选择器更改为 垃圾收集的对象实时和垃圾收集的对象。分配调用树和分配热点视图的选项对话框具有等效的下拉列表。

java 获得jvm内存大小 java获取内存快照_垃圾收集_11

但是,默认情况下,JProfiler不会收集垃圾收集对象的信息,因为只能以较少的开销维护活动对象的数据。将活动选择器切换到包含垃圾收集对象的模式时,JProfiler建议更改录制类型。这是配置文件设置的更改,因此如果您选择立即应用更改,则将清除所有先前记录的数据。如果要提前更改此设置,可以在配置文件设置的“内存配置”部分中执行此操作。

java 获得jvm内存大小 java获取内存快照_垃圾收集_12

五、下一站:堆——Next stop: heap walker

任何更高级的问题类型都涉及对象之间的引用。例如,记录的对象,分配树和分配热点视图中显示的大小浅的大小。它们只包含类的内存布局,但不包括任何引用的类。要查看类的重量对象是多少,您通常需要知道保留的大小,这意味着如果从堆中删除这些对象,将释放的内存量。

实时内存视图中不提供此类信息,因为它需要枚举堆上的所有对象并执行昂贵的计算。该作业由堆walker处理。要从实时内存视图中的兴趣点跳转到堆步行器, 可以使用Show in Heap Walker工具栏按钮。它将带您进入堆walker中的等效视图。

java 获得jvm内存大小 java获取内存快照_java 获得jvm内存大小_13

如果没有可用的堆快照,则会创建新的堆快照,否则JProfiler将询问您是否使用现有的堆快照。

java 获得jvm内存大小 java获取内存快照_jprofiler_14

无论如何,重要的是要理解实时内存视图和堆walker中的数字通常会非常不同。除了堆walker在与实时内存视图不同的时间点显示快照的事实之外,它还消除了所有未引用的对象。根据垃圾收集器的状态,未引用的对象可占据堆的很大一部分。