java 内存泄漏的堆和线程分析
java heap and thread analysis for memory leak
我的 WebLogic 服务器配置了 16gb 的堆 space,但是当大多数用户开始工作时,它在生产使用的 1 小时内使用了 90%。我观察到每当发生这种情况时都会有几个线程卡住。
我在堆空闲大约 10% 时捕获了堆转储。我如何检查堆转储以找出导致此问题的内存泄漏或进程代码。
我试图理解内存泄漏,运行 JMap 和 Eclipse MAT 等工具,但可能由于缺乏经验,我无法理解这些工具试图显示的内容。或者 how/what 我应该注意什么?
我有两个 before/after GC 堆转储要分析。
我查看了线程转储,没有 "waiting to lock" 个对象线程,线程类似于下图,线程卡住没有明显原因。
看来你可能有内存泄漏的情况。您最好的方法是使用 Java 飞行记录器的任务控制来获取 class 和方法泄漏。
您应该使用以下参数设置您的 weblogic 托管服务器:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8999
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder
设置时,请按照说明here检测泄漏。
希望对您有所帮助!!
我是名为 Plumbr 的工具的开发者之一。除此之外,我们会在内存使用过多的情况下自动分析堆内容。您可能会发现它很有用。
根据您的评论:您有 Java 7 和 16GB 堆,没有明确指定 GC 算法,因此 Java 7 的默认值是 Throughput GC,这不适合大多数网络应用程序,因为它会导致大堆的长时间 GC 暂停。
切换到 ConcurrentMarkSweep GC,这样 GC 就不会等到你的内存被填满,而是会尽可能地增量收集垃圾,这样你就会有更少的 Stop The World 停顿。
根据您的堆转储,您最大的内存问题是 int 数组,实际上它占用了将近 70% 的堆(是的,改为对大小列进行排序)。
- Select 它在您的堆转储中,右键单击并在
Show in Instances View
上 select
- 然后浏览最大的对象并为每个对象右键单击并 select
Show Nearest GC Root
以查看哪个对象仍然具有对 int 数组的硬引用,这阻止了 GC 的资格。
假设它是内存泄漏,它可以帮助您找到内存泄漏。
请参阅下面的 Nearest GC Root
示例,该示例允许识别我故意添加到我的程序中的泄漏,只是为了展示这个想法。正如您在屏幕截图中看到的,我有一个 int 数组,它不符合 GC 的条件,因为它存储在我的 class Application
中名为 leak
的 HashMap
中,所以我知道我的内存问题可能是由于这个特定的 HashMap
引起的,特别是如果我有许多其他对象导致这个 HashMap
.
注意: 当你试图识别泄漏时要有耐心,因为它并不总是很明显,理想的情况是你有一个巨大的对象占据了整个堆但很明显这不是你的情况,没有什么是真正明显的,这就是我建议首先调查 int 数组的原因。不要忘记它也可以是小的 int 数组,但成千上万的数组具有相同的 Nearest GC Root
.
另一个技巧,如果你有 JProfiler you can simply follow this wonderful tutorial 找到你的漏洞。
响应更新:
一种更好地识别内存泄漏根本原因的简单方法是至少进行 2 次堆转储,然后使用 jhat 之类的工具与语法
进行比较
jhat -J-Xmx2G -baseline ${path-to-the-first-heap-dump} ${path-to-the-second-heap-dump}
它将在端口 7000
上启动一个小型 HTTP 服务器,因此:
- 启动http://localhost:7000/
- 然后点击
Show instance counts for all classes (including platform)
然后您将看到 类 的列表,按创建的新实例总数排序。然后,您可以使用 VisualVM 执行我在回答的第一部分中描述的操作,以找到内存泄漏的根本原因。
您也可以使用 jhat
- 通过 select 的顶部 类 然后对每个
- 点击一个"Reference to this Object"
- 然后点击
Exclude weak refs
然后您将看到每个实例的 GC 根,如下图所示:
另一种方法是使用 Eclipse Memory Analyzer
也称为 MAT
。
- 用它打开第二个快照
- Select 视图
histogram
- 然后对每个顶部类右键单击
- 选择
Merge Shortest Paths To GC Roots
/Exclude All references
然后您将看到类似下一个屏幕截图的内容:
您是否尝试过 yourkit profiler? It's not free, but you can evaluate it for 30 days. In this case if you dump contains all object (not only live), you will be able to check roots for them as well. Because it could be that you don't have memory leak, but too big memory footprint. Also it would be great to enable GC logs 并解析您有多少 FullGC 暂停:
grep "Full GC" jvm_gc.log | wc -l
在理想世界中它应该是 0 :)
顺便说一句,整个 article 可能对您有所帮助。
JDK 的“jmap -histo”命令会将所有 class 的对象 counts/bytes 转储到文本文件中。如果随着时间的推移 capture/compare 这些转储中的一些转储,您将看到哪些转储不断增长——您的内存泄漏。 -histo 的开销比捕获完整堆转储的开销低得多。
仅比较在后台(每隔一段时间)运行此 jmap -histo 命令的几个转储(如 python 脚本 detailed here) seems like too small of a sample, so I wrote an open-source tool (here)。它具有实时显示并跟踪每个 class 的字节数上升的时间百分比。
我的 WebLogic 服务器配置了 16gb 的堆 space,但是当大多数用户开始工作时,它在生产使用的 1 小时内使用了 90%。我观察到每当发生这种情况时都会有几个线程卡住。
我在堆空闲大约 10% 时捕获了堆转储。我如何检查堆转储以找出导致此问题的内存泄漏或进程代码。
我试图理解内存泄漏,运行 JMap 和 Eclipse MAT 等工具,但可能由于缺乏经验,我无法理解这些工具试图显示的内容。或者 how/what 我应该注意什么?
我有两个 before/after GC 堆转储要分析。
我查看了线程转储,没有 "waiting to lock" 个对象线程,线程类似于下图,线程卡住没有明显原因。
看来你可能有内存泄漏的情况。您最好的方法是使用 Java 飞行记录器的任务控制来获取 class 和方法泄漏。
您应该使用以下参数设置您的 weblogic 托管服务器:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8999
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder
设置时,请按照说明here检测泄漏。
希望对您有所帮助!!
我是名为 Plumbr 的工具的开发者之一。除此之外,我们会在内存使用过多的情况下自动分析堆内容。您可能会发现它很有用。
根据您的评论:您有 Java 7 和 16GB 堆,没有明确指定 GC 算法,因此 Java 7 的默认值是 Throughput GC,这不适合大多数网络应用程序,因为它会导致大堆的长时间 GC 暂停。
切换到 ConcurrentMarkSweep GC,这样 GC 就不会等到你的内存被填满,而是会尽可能地增量收集垃圾,这样你就会有更少的 Stop The World 停顿。
根据您的堆转储,您最大的内存问题是 int 数组,实际上它占用了将近 70% 的堆(是的,改为对大小列进行排序)。
- Select 它在您的堆转储中,右键单击并在
Show in Instances View
上 select
- 然后浏览最大的对象并为每个对象右键单击并 select
Show Nearest GC Root
以查看哪个对象仍然具有对 int 数组的硬引用,这阻止了 GC 的资格。
假设它是内存泄漏,它可以帮助您找到内存泄漏。
请参阅下面的 Nearest GC Root
示例,该示例允许识别我故意添加到我的程序中的泄漏,只是为了展示这个想法。正如您在屏幕截图中看到的,我有一个 int 数组,它不符合 GC 的条件,因为它存储在我的 class Application
中名为 leak
的 HashMap
中,所以我知道我的内存问题可能是由于这个特定的 HashMap
引起的,特别是如果我有许多其他对象导致这个 HashMap
.
注意: 当你试图识别泄漏时要有耐心,因为它并不总是很明显,理想的情况是你有一个巨大的对象占据了整个堆但很明显这不是你的情况,没有什么是真正明显的,这就是我建议首先调查 int 数组的原因。不要忘记它也可以是小的 int 数组,但成千上万的数组具有相同的 Nearest GC Root
.
另一个技巧,如果你有 JProfiler you can simply follow this wonderful tutorial 找到你的漏洞。
响应更新:
一种更好地识别内存泄漏根本原因的简单方法是至少进行 2 次堆转储,然后使用 jhat 之类的工具与语法
进行比较jhat -J-Xmx2G -baseline ${path-to-the-first-heap-dump} ${path-to-the-second-heap-dump}
它将在端口 7000
上启动一个小型 HTTP 服务器,因此:
- 启动http://localhost:7000/
- 然后点击
Show instance counts for all classes (including platform)
然后您将看到 类 的列表,按创建的新实例总数排序。然后,您可以使用 VisualVM 执行我在回答的第一部分中描述的操作,以找到内存泄漏的根本原因。
您也可以使用 jhat
- 通过 select 的顶部 类 然后对每个
- 点击一个"Reference to this Object"
- 然后点击
Exclude weak refs
然后您将看到每个实例的 GC 根,如下图所示:
另一种方法是使用 Eclipse Memory Analyzer
也称为 MAT
。
- 用它打开第二个快照
- Select 视图
histogram
- 然后对每个顶部类右键单击
- 选择
Merge Shortest Paths To GC Roots
/Exclude All references
然后您将看到类似下一个屏幕截图的内容:
您是否尝试过 yourkit profiler? It's not free, but you can evaluate it for 30 days. In this case if you dump contains all object (not only live), you will be able to check roots for them as well. Because it could be that you don't have memory leak, but too big memory footprint. Also it would be great to enable GC logs 并解析您有多少 FullGC 暂停:
grep "Full GC" jvm_gc.log | wc -l
在理想世界中它应该是 0 :)
顺便说一句,整个 article 可能对您有所帮助。
JDK 的“jmap -histo”命令会将所有 class 的对象 counts/bytes 转储到文本文件中。如果随着时间的推移 capture/compare 这些转储中的一些转储,您将看到哪些转储不断增长——您的内存泄漏。 -histo 的开销比捕获完整堆转储的开销低得多。
仅比较在后台(每隔一段时间)运行此 jmap -histo 命令的几个转储(如 python 脚本 detailed here) seems like too small of a sample, so I wrote an open-source tool (here)。它具有实时显示并跟踪每个 class 的字节数上升的时间百分比。