3年前 (2021-01-05)  jvm |   抢沙发  2157 
文章评分 0 次,平均分 0.0

本文旨在详细说明如何查看堆中的对象。包括对象之间的引用以识别潜在的Java内存泄漏。

用于分析内存问题的最简单工具是所谓的Java Heapdumps,即堆转储dump文件,它可以在出现错误时自动生成,例如,通过JVM选项-XX:+HeapDumpOnOutOfMemoryError。在这种情况下,转储是在JVM结束之前创建的,这也称为事后分析。当然,这些转储也可以在运行时手动生成,但以后会更多。

当我们在这一幕中处理Heapdums时,我们将在下一幕中看到有哪些其他替代方法来追踪丢失的内存。

Java堆转储

Heap dump是指Java堆的文本或二进制图像,它被写入一个文件中。堆中的信息可用于重建整个对象,包括它们之间的引用。但是,没有关于对象位于哪个堆区域的信息。毕竟,这是一个遗憾,因为我们在第三幕中了解到了这些领域的重要性。然而,在Java内存中查找heumps信息是相当无趣的。

通缉令:活着还是死

dump文件定位内存泄露

堆转储可以包含两种类型的对象:

  • 仅活动对象(即通过GC根引用访问的对象)
  • 所有对象(即包括非引用对象)

由于要通过各种VM内部机制(如标记列表或GC根)来确定活动对象,因此JVM会相对快速地创建一个具有独占活动对象的堆。

但是,如果要包含非引用对象,则生产需要更长的时间,也需要更多的存储空间。

一般来说,存活的对象就足以搜索内存泄漏。不再引用的对象在查看对象循环或GC抖动时最有趣。在这些情况下,人们不仅要查找泄漏,还要查找不必要地创建了许多泄漏的对象。

创建堆转储dump文件

如开头所示,heap dump可以通过两种方式创建。但是,转储的生成过程会导致应用程序停止,因为在生成转储的过程中执行会停止。这是有意义的,因为否则堆的状态会在生产过程中发生变化。因此,不应在生产系统中过度使用此功能。

JVM选项-XX:+HeapDumpOnOutOfMemoryError在JVM以OutOfMemoryError结束时写入Heap dump。

没有理由不使用此选项。JVM已经崩溃了,负性能是不有趣的。只有堆转储的大小对某些服务器造成了问题。

但是,由于堆通常是有关崩溃的唯一信息源,因此肯定应该生成它。崩溃后,管理服务器应该保护转储,以便进行事后分析,不幸的是,由于空间原因,它们通常只是被删除。

使用jmap工具,可以从命令行创建堆:

jmap -dump:live,format=b,file=<filename> <PID>

live选项只会导致转储活动对象,如果您想查看整个堆,也可以省略它。格式“b”适用于“二进制”。也有一个ASCII版本,也可以手动计算,但这是不可能的,在实践中,由于数据量。

heap,让我看看你有什么!

有一系列工具可用于脱机检查堆转储。然而,这里不值得比较,因为有一个没有竞争力的赢家:

Eclipse内存分析器工具包(Eclipse MAT)

eclipse mat可以读取、处理、分析和评估不同格式和尺寸的堆转储dump文件。整个东西用的很好,完全免费。因此一方面可以分析非常大的堆,另一方面分析非常快。尤其是较旧的工具经常会遇到这样的问题:堆被完全加载到工作内存中进行分析,甚至需要堆的1,5倍来进行分析。换句话说,2GB大小的堆需要3GB的堆进行分析。

dump文件定位内存泄露

如果您使用Eclipse MAT查看一个巨大的堆转储,您将发现许多对象。通常包括:

  • char[]
  • java.lang.Object[]
  • java.lang.Class
  • java.lang.String
  • byte[]
  • java.util.HashMap
  • java.util.HashMap$Entry
  • short[]
  • int[]
  • java.util.ArrayList
  • java.lang.Integer

这些物体总是会大量出现。现在唯一的问题是:这些物体是问题还是我如何找到原因?

大面积内存泄漏

如果事后分析OutOfMemoryError堆转储dump文件,通常可以假设内存泄漏已占用大部分内存。但漏洞有多大?在Heapdump中,所有对象实例都存在,Eclipse MAT显示在类直方图上,但只显示类名、实例数和浅层大小。浅层大小是实例中所有基元数据类型以及对其他对象的引用的大小。相当有趣的是保留堆,即由这些支配者保持活动的所有对象的总大小。要查看保留堆,MAT必须首先通过跟踪所有对象引用来计算它。对于MemoryLeak的搜索,按保留堆排序通常是有用的,这就是为什么MAT表示蛋糕图中保留堆最大的类。

web应用程序中大量保留堆的一个突出例子通常是

org.apache.catalina.session.StandardManager

技术目的很明确:记录会话期间的用户数据。但是通常您可以在这里找到框架工作信息,正如您可以从我们关于Ajax4JSF的条目中看到的那样。

但也有自己的,不幸的是几乎总是不正确的,缓存实现经常出现在饼图中,并相对快速地传递内存泄漏的原因。

dump文件定位内存泄露

为了确定是谁产生了这些对象,是什么使这些对象具有专业性,以及它们是否仍然存在,具体的对象实例及其传入和传出的引用都很有帮助。

要访问实例,请从关联菜单中选择“List objects with incoming References”列出具有传入引用的对象。这将显示所有实例和包含对此对象的所有引用的树结构。因此,这些引用导致对象没有被垃圾收集。传出引用不太有趣,但可以通过查看此对象中包含的内容来帮助识别实例。如果支配者本身(例如SessionManager)是正常的,但是意外地引用了许多对象,那么它们也很有用。

dump文件定位内存泄露

滑动存储器泄漏

如果要在OutOfMemoryError发生之前找到泄漏,可以使用上述方法创建快照。但是,快照无法判断当前状况是好是坏。只有结构不断增长,泄漏的嫌疑才能得到加强。

Eclipse Mat提供了比较热堆的可能性,因此可以检测不断增长的结构。但它需要两个堆转储在良好的调整时间。体重足够的话应该在两次倾倒之间停留几分钟,体重稍重的话多呆几个小时。只有这样,我们才能真正从“自然波动”中看到显著的差异。

例如,以下示例显示了XML相关对象的显著增加:

dump文件定位内存泄露

更好的方法

但是比较快照很复杂,在极端情况下甚至会产生误导,而且生产中的OutOfMemoryError非常烦人。最后但并非最不重要的一点是,在分析之后,我们仍然缺少在哪些代码点创建对象,然后可能不再被引用的信息。

所以没有更好的方法了?

当然!探查器和一些APM工具可以在程序期间进行统计并记录访问路径。这些程序提供的信息通常比堆转储dump文件更快地达到目标,并将成为我们下一步行动的主要参与者。

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1186.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册