堆转储是堆空间中存在的所有Java对象的快照,即dump文件。堆转储文件通常以.hprof
扩展名存储。
在本文中,我们将看到如何获取正在运行的Java应用程序的堆转储,并使用Eclipse的内存分析器(MAT)来识别内存热点和检测内存泄漏的可能性。
我为什么和什么时候应该做堆转储?
如果Java应用程序占用的内存超出预期,或者Java应用程序因OutOfMemoryError而崩溃,则可能需要进行堆转储。分析堆转储可以找到异常的根本原因。
使用堆转储,我们可以找到每个类的内存使用情况、每个类的对象数等详细信息。我们还可以进入详细信息,找出应用程序中单个Java对象保留的内存量。这些细节可以帮助我们确定导致内存泄漏问题的实际代码。
如何分析非常大的堆转储?
通常,分析堆转储比实际的堆转储大小占用更多的内存,如果您试图从开发机器上的大型服务器分析堆转储,这可能会有问题。例如,服务器可能崩溃,堆转储大小为24 GB,而本地计算机可能只有16 GB内存。因此,MAT、Jhat等工具将无法加载堆转储文件。在这种情况下,您应该在没有内存约束的同一台服务器上分析堆转储,或者使用VisualVM提供的实时内存采样工具。
如何对正在运行的Java应用程序进行堆转储?
有几种方法可以进行堆转储。我们将讨论3种最简单的方法。
生成堆转储的命令行接口
这些步骤对于所有操作系统都是通用的,包括Windows、Linux和macOS。
1. 查找正在运行的Java应用程序的进程id。您可以使用jps工具列出本地机器上所有正在运行的Java进程。进程将按以下格式列出“<pid><MainClass>
”
2. 找到pid后,运行以下命令:
jmap -dump:live,file=<file-name + .hprof> <pid>
如果只想收集活动对象(即在运行代码中仍有引用的对象),那么live
选项很重要。
VisualVM生成堆转储
visualvm使得在本地机器上运行堆转储非常容易。以下步骤可用于使用VisualVM生成堆转储
1. 启动visualvm并将本地Java应用程序连接到它。
2. 在Monitor选项卡下,单击Heap Dump。
3. 单击堆转储后,您将被重定向到一个新选项卡,从中可以找到堆转储的位置。
JConsole生成堆转储
1. 将应用程序连接到JConsole。
2. 切换到MBeans选项卡并选择com.sun.management > HotSpotDiagnostic > Operations > dumpHeap。
3. 在单击dumpHeap函数之前,设置下面描述的参数p0、p1。
1. 参数p0是堆转储文件的位置和名称。确保在文件名末尾添加“.hprof”扩展名。
2. 参数p1如果设置为true,则在转储堆之前执行GC,以便堆转储中只存在活动对象。
哪些工具可用于分析堆转储或打开.hprof文件?
一旦有了堆转储dump文件,下一步就是使用工具对其进行分析。有多种付费的、同样优秀的开源工具可用于分析堆转储。内存分析器(MAT)是最好的开源工具之一,可以作为Eclipse的插件使用,如果没有安装eclipse ide,也可以作为独立的应用程序使用。除了MAT,你还可以使用Jhat,VisualVM。但是,在本文中,我们将讨论MAT提供的特性。
下载内存分析器(MAT)
有两种方法可以使用内存分析器工具。
将MAT插件与Eclipse集成
1. 打开eclipseide并选择帮助>Eclipse市场。
2. 搜索内存分析器并安装它。
3. 重新启动Eclipse,插件就可以使用了。
下载独立版本的Eclipse MAT
1. 下载并安装Java开发工具包。
2. 从这个链接下载安装独立的MAT应用程序。
3. 解压包后,打开MemoryAnalyzer应用程序,开始使用MAT的独立版本。
在Eclipse MAT中加载堆转储dump文件
我们将分析这个Java应用程序生成的堆转储。本教程将深入讨论应用程序中的内存泄漏。下面的截图来自eclipseide使用的MAT插件。
加载堆转储的步骤如下:
1. 打开eclipseide或独立MAT工具。
2. 从工具栏中,从下拉菜单中选择“文件”>“打开文件”。
3. 打开扩展名为.hprof的堆转储文件,您将看到如下所示的概述页面。
我们将介绍一些重要的工具,如直方图、支配树和泄漏可疑报告,它们可以用来识别内存泄漏。
Histogram 直方图
直方图列出了堆转储时Java应用程序中加载的所有不同类。它还列出了每个类的对象数以及浅堆和保留堆的大小。使用直方图,很难确定哪个对象占用了最多的内存。但是,我们可以很容易地确定哪个类类型拥有最大的内存量。例如,在下面的屏幕截图中,字节数组拥有最大的内存量。但是,我们无法确定哪个对象实际持有该字节数组。
Shallow Heap浅堆 VS Retained Heap保留堆
浅堆是对象本身的大小。例如,在下面的屏幕截图中,字节数组本身拥有最大的内存量。Retained Heap是对象本身的大小以及其中保留的所有对象的大小。例如,在下面的屏幕截图中,DogShelter对象本身拥有16个字节的大小。但是,它的保留堆大小超过305Mb,这意味着它可能持有字节数组,而字节数组会导致非常大的保留堆大小。
最后,根据直方图,我们推断问题嫌疑犯是dogsholter
或Dog
类的对象保留的byte[]。
Dominator Tree 支配树
Java对象的支配树允许您轻松地识别拥有最大内存块的对象。例如,我们可以从下面的截图中看到,主线程对象拥有最大的内存。在折叠主线程树时,我们可以看到dogheerd类的实例拥有一个hashmap,它拥有超过300Mb的内存。
当您有一个正在消耗大量内存的对象时,支配树非常有用。如果多个小对象导致内存泄漏,那么支配树就没有多大意义。在这种情况下,最好使用直方图来找出占用内存最多的类的实例。
从支配树,我们推断出问题嫌疑犯是dogsholter
类。
Duplicate Classes 重复类
“复制类”选项卡将列出多次加载的类。如果在代码中使用类加载器,则可以使用重复的类来确保代码正常运行,并且不会多次加载类。
Leak Suspect 泄漏嫌疑犯
最后,Leak suspect报告运行一个Leak suspect查询,该查询分析堆转储并尝试查找内存泄漏。对于不常见的内存泄漏,泄漏嫌疑犯查询可能无法识别内存泄漏,而使用上面讨论的工具来确定泄漏则取决于具有程序知识的开发人员。
由于我们有一个非常小的内存泄漏,我们使用直方图和支配树手动导出的推断与从泄漏可疑报告中得到的推断是相同的,如下所示。
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1285.html
暂无评论