如何查找 Java 内存泄漏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40119/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:30:35  来源:igfitidea点击:

How to find a Java Memory Leak

javamemorymemory-leaksjhat

提问by jwiklund

How do you find a memory leak in Java (using, for example, JHat)? I have tried to load the heap dump up in JHat to take a basic look. However, I do not understand how I am supposed to be able to find the root reference (ref) or whatever it is called. Basically, I can tell that there are several hundred megabytes of hash table entries ([java.util.HashMap$Entry or something like that), but maps are used all over the place... Is there some way to search for large maps, or perhaps find general roots of large object trees?

您如何发现 Java 中的内存泄漏(例如,使用 JHat)?我尝试在 JHat 中加载堆转储以进行基本查看。但是,我不明白我应该如何找到根引用(ref)或它的名字。基本上,我可以说有几百兆字节的哈希表条目([java.util.HashMap$Entry 或类似的东西),但是到处都在使用地图......有没有办法搜索大地图,或者可能找到大对象树的一般根?

[Edit] Ok, I've read the answers so far but let's just say I am a cheap bastard (meaning I am more interested in learning how to use JHat than to pay for JProfiler). Also, JHat is always available since it is part of the JDK. Unless of course there is no way with JHat but brute force, but I can't believe that can be the case.

[编辑] 好的,到目前为止我已经阅读了答案,但让我们说我是一个廉价的混蛋(这意味着我对学习如何使用 JHat 比为 JProfiler 付费更感兴趣)。此外,JHat 始终可用,因为它是 JDK 的一部分。除非当然 JHat 没有办法只能使用蛮力,但我不敢相信情况会如此。

Also, I do not think I will be able to actually modify (adding logging of allmap sizes) and run it for long enough for me to notice the leak.

此外,我认为我无法实际修改(添加所有地图大小的日志记录)并运行它足够长的时间让我注意到泄漏。

采纳答案by Dima Malenko

I use following approach to finding memory leaks in Java. I've used jProfiler with great success, but I believe that any specialized tool with graphing capabilities (diffs are easier to analyze in graphical form) will work.

我使用以下方法来查找 Java 中的内存泄漏。我已经使用 jProfiler 取得了巨大的成功,但我相信任何具有图形功能(差异更容易以图形形式分析)的专业工具都可以使用。

  1. Start the application and wait until it get to "stable" state, when all the initialization is complete and the application is idle.
  2. Run the operation suspected of producing a memory leak several times to allow any cache, DB-related initialization to take place.
  3. Run GC and take memory snapshot.
  4. Run the operation again. Depending on the complexity of operation and sizes of data that is processed operation may need to be run several to many times.
  5. Run GC and take memory snapshot.
  6. Run a diff for 2 snapshots and analyze it.
  1. 启动应用程序并等待它进入“稳定”状态,此时所有初始化都已完成且应用程序处于空闲状态。
  2. 多次运行可能产生内存泄漏的操作,以允许进行任何缓存、与数据库相关的初始化。
  3. 运行 GC 并拍摄内存快照。
  4. 再次运行该操作。根据操作的复杂性和处理的数据大小,操作可能需要运行几次到多次。
  5. 运行 GC 并拍摄内存快照。
  6. 运行 2 个快照的差异并对其进行分析。

Basically analysis should start from greatest positive diff by, say, object types and find what causes those extra objects to stick in memory.

基本上分析应该从最大的正差异开始,比如对象类型,并找出导致这些额外对象粘在内存中的原因。

For web applications that process requests in several threads analysis gets more complicated, but nevertheless general approach still applies.

对于在多个线程中处理请求的 Web 应用程序,分析变得更加复杂,但通用方法仍然适用。

I did quite a number of projects specifically aimed at reducing memory footprint of the applications and this general approach with some application specific tweaks and trick always worked well.

我做了很多专门旨在减少应用程序内存占用的项目,这种带有一些特定于应用程序的调整和技巧的通用方法总是很有效。

回答by Mike Stone

Well, there's always the low tech solution of adding logging of the size of your maps when you modify them, then search the logs for which maps are growing beyond a reasonable size.

好吧,总是有一种低技术的解决方案,即在修改地图时添加地图大小的日志记录,然后搜索地图增长超出合理大小的日志。

回答by McKenzieG1

You really need to use a memory profiler that tracks allocations. Take a look at JProfiler- their "heap walker" feature is great, and they have integration with all of the major Java IDEs. It's not free, but it isn't that expensive either ($499 for a single license) - you will burn $500 worth of time pretty quickly struggling to find a leak with less sophisticated tools.

您确实需要使用跟踪分配的内存分析器。看看JProfiler- 他们的“堆漫步器”特性很棒,并且它们与所有主要的 Java IDE 集成。它不是免费的,但也不是那么昂贵(单个许可证 499 美元) - 您将很快消耗价值 500 美元的时间,努力使用不太复杂的工具寻找泄漏。

回答by Tnilsson

There are tools that should help you find your leak, like JProbe, YourKit, AD4J or JRockit Mission Control. The last is the one that I personally know best. Any good tool should let you drill down to a level where you can easily identify what leaks, and where the leaking objects are allocated.

有一些工具可以帮助您找到漏洞,例如 JProbe、YourKit、AD4J 或 JRockit Mission Control。最后一个是我个人最了解的。任何好的工具都应该让您深入到可以轻松识别泄漏的级别以及泄漏对象的分配位置。

Using HashTables, Hashmaps or similar is one of the few ways that you can acually leak memory in Java at all. If I had to find the leak by hand I would peridically print the size of my HashMaps, and from there find the one where I add items and forget to delete them.

使用 HashTables、Hashmaps 或类似的方法是在 Java 中完全泄漏内存的少数几种方法之一。如果我必须手动找到泄漏,我会定期打印我的 HashMap 的大小,然后从那里找到我添加项目并忘记删除它们的那个。

回答by wbkang

NetBeans has a built-in profiler.

NetBeans 有一个内置的分析器。

回答by erickson

A tool is a big help.

一个工具是一个很大的帮助。

However, there are times when you can't use a tool: the heap dump is so huge it crashes the tool, you are trying to troubleshoot a machine in some production environment to which you only have shell access, etc.

但是,有时您无法使用工具:堆转储如此之大以致于使工具崩溃,您正在尝试对某些生产环境中的机器进行故障排除,而您只能对其进行 shell 访问,等等。

In that case, it helps to know your way around the hprof dump file.

在这种情况下,了解 hprof 转储文件的方法会很有帮助。

Look for SITES BEGIN. This shows you what objects are using the most memory. But the objects aren't lumped together solely by type: each entry also includes a "trace" ID. You can then search for that "TRACE nnnn" to see the top few frames of the stack where the object was allocated. Often, once I see where the object is allocated, I find a bug and I'm done. Also, note that you can control how many frames are recorded in the stack with the options to -Xrunhprof.

寻找网站开始。这会显示哪些对象使用的内存最多。但是对象并不仅仅按类型混在一起:每个条目还包括一个“跟踪”ID。然后,您可以搜索该“TRACE nnnn”以查看分配对象的堆栈的前几帧。通常,一旦我看到对象的分配位置,我就会发现一个错误,然后我就完成了。另请注意,您可以使用 -Xrunhprof 选项控制堆栈中记录的帧数。

If you check out the allocation site, and don't see anything wrong, you have to start backward chaining from some of those live objects to root objects, to find the unexpected reference chain. This is where a tool really helps, but you can do the same thing by hand (well, with grep). There is not just one root object (i.e., object not subject to garbage collection). Threads, classes, and stack frames act as root objects, and anything they reference strongly is not collectible.

如果您查看分配站点,并且没有发现任何错误,则必须开始从其中一些活动对象到根对象的反向链接,以找到意外的引用链。这是工具真正有用的地方,但您可以手动执行相同的操作(好吧,使用 grep)。不只有一个根对象(即不受垃圾回收影响的对象)。线程、类和堆栈帧充当根对象,并且它们强烈引用的任何内容都不可收集。

To do the chaining, look in the HEAP DUMP section for entries with the bad trace id. This will take you to an OBJ or ARR entry, which shows a unique object identifier in hexadecimal. Search for all occurrences of that id to find who's got a strong reference to the object. Follow each of those paths backward as they branch until you figure out where the leak is. See why a tool is so handy?

要进行链接,请在 HEAP DUMP 部分查找具有错误跟踪 ID 的条目。这将带您到一个 OBJ 或 ARR 条目,它以十六进制显示唯一的对象标识符。搜索所有出现的该 id 以找到谁对该对象有强引用。当它们分支时,沿着每条路径向后走,直到找出泄漏的位置。看看为什么一个工具如此方便?

Static members are a repeat offender for memory leaks. In fact, even without a tool, it'd be worth spending a few minutes looking through your code for static Map members. Can a map grow large? Does anything ever clean up its entries?

静态成员是内存泄漏的重犯。事实上,即使没有工具,花几分钟查看静态 Map 成员的代码也是值得的。地图可以变大吗?有什么东西可以清理它的条目吗?

回答by jwiklund

Questioner here, I have got to say getting a tool that does not take 5 minutes to answer any click makes it a lot easier to find potential memory leaks.

发问者在这里,我不得不说获得一个不需要 5 分钟来回答任何点击的工具可以更容易地找到潜在的内存泄漏。

Since people are suggesting several tools ( I only tried visual wm since I got that in the JDK and JProbe trial ) I though I should suggest a free / open source tool built on the Eclipse platform, the Memory Analyzer (sometimes referenced as the SAP memory analyzer) available on http://www.eclipse.org/mat/.

由于人们建议使用几种工具(自从我在 JDK 和 JProbe 试用版中获得它以来,我只尝试了 Visual wm),我虽然应该建议一个构建在 Eclipse 平台上的免费/开源工具,即内存分析器(有时称为 SAP 内存)分析器)在http://www.eclipse.org/mat/ 上可用。

What is really cool about this tool is that it indexed the heap dump when I first opened it which allowed it to show data like retained heap without waiting 5 minutes for each object (pretty much all operations were tons faster than the other tools I tried).

这个工具真正酷的是,当我第一次打开它时它索引了堆转储,这使得它可以显示保留堆等数据,而无需为每个对象等待 5 分钟(几乎所有操作都比我尝试过的其他工具快很多) .

When you open the dump, the first screen shows you a pie chart with the biggest objects (counting retained heap) and one can quickly navigate down to the objects that are to big for comfort. It also has a Find likely leak suspects which I reccon can come in handy, but since the navigation was enough for me I did not really get into it.

当您打开转储时,第一个屏幕会向您显示一个饼图,其中包含最大的对象(计算保留的堆),您可以快速向下导航到大到舒适的对象。它还有一个查找可能的泄漏嫌疑人,我认为它可以派上用场,但由于导航对我来说已经足够了,我并没有真正深入了解它。

回答by Alex Punnen

Most of the time, in enterprise applications the Java heap given is larger than the ideal size of max 12 to 16 GB. I have found it hard to make the NetBeans profiler work directly on these big java apps.

大多数情况下,在企业应用程序中,给定的 Java 堆大于最大 12 到 16 GB 的理想大小。我发现很难让 NetBeans 分析器直接在这些大型 Java 应用程序上工作。

But usually this is not needed. You can use the jmap utility that comes with the jdk to take a "live" heap dump , that is jmap will dump the heap after running GC. Do some operation on the application, wait till the operation is completed, then take another "live" heap dump. Use tools like Eclipse MAT to load the heapdumps, sort on the histogram, see which objects have increased, or which are the highest, This would give a clue.

但通常这不是必需的。您可以使用 jdk 附带的 jmap 实用程序进行“实时”堆转储,即 jmap 将在运行 GC 后转储堆。对应用程序进行一些操作,等到操作完成,然后再进行另一个“实时”堆转储。使用 Eclipse MAT 之类的工具加载 heapdumps,对直方图进行排序,查看哪些对象增加了,或者哪些对象最高,这将提供线索。

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

There is only one problem with this approach; Huge heap dumps, even with the live option, may be too big to transfer out to development lap, and may need a machine with enough memory/RAM to open.

这种方法只有一个问题;巨大的堆转储,即使使用 live 选项,也可能太大而无法转移到开发圈,并且可能需要一台具有足够内存/RAM 的机器才能打开。

That is where the class histogram comes into picture. You can dump a live class histogram with the jmap tool. This will give only the class histogram of memory usage.Basically it won't have the information to chain the reference. For example it may put char array at the top. And String class somewhere below. You have to draw the connection yourself.

这就是类直方图出现的地方。您可以使用 jmap 工具转储实时类直方图。这将只给出内存使用的类直方图。基本上它不会有链接引用的信息。例如,它可能将 char 数组放在顶部。和下面某处的 String 类。您必须自己绘制连接。

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

Instead of taking two heap dumps, take two class histograms, like as described above; Then compare the class histograms and see the classes that are increasing. See if you can relate the Java classes to your application classes. This will give a pretty good hint. Here is a pythons script that can help you compare two jmap histogram dumps. histogramparser.py

不是采用两个堆转储,而是采用两个类直方图,如上所述;然后比较类直方图,查看增加的类。看看您是否可以将 Java 类与您的应用程序类相关联。这将给出一个很好的提示。这是一个 pythons 脚本,可以帮助您比较两个 jmap 直方图转储。直方图解析器.py

Finally tools like JConolse and VisualVm are essential to see the memory growth over time, and see if there is a memory leak. Finally sometimes your problem may not be a memory leak , but high memory usage.For this enable GC logging;use a more advanced and new compacting GC like G1GC; and you can use jdk tools like jstat to see the GC behaviour live

最后,像 JConolse 和 VisualVm 这样的工具对于查看内存随时间增长以及查看是否存在内存泄漏至关重要。最后,有时您的问题可能不是内存泄漏,而是内存使用率过高。为此启用 GC 日志记录;使用更先进的新压缩 GC,如 G1GC;并且您可以使用 jdk 工具(例如 jstat)来实时查看 GC 行为

jstat -gccause pid <optional time interval>

Other referecences to google for -jhat, jmap, Full GC, Humongous allocation, G1GC

google for -jhat、jmap、Full GC、Humongous allocation、G1GC 的其他参考资料

回答by Amir Fo

You can find out by measuring memory usage size after calling garbage collector multiple times:

可以通过多次调用垃圾收集器后测量内存使用大小来找出:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

If the output numbers were equal, there is no memory leak in your application, but if you saw difference between the numbers of memory usage (increasing numbers), there is memory leak in your project. For example:

如果输出数量相等,则您的应用程序中没有内存泄漏,但如果您看到内存使用数量(增加的数量)之间存在差异,则说明您的项目中存在内存泄漏。例如:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

Notethat sometimes it takes some time to release memory by some actions like streams and sockets. You should not judge by first outputs, You should test it in a specific amount of time.

请注意,有时通过某些操作(如流和套接字)释放内存需要一些时间。你不应该通过第一次输出来判断,你应该在特定的时间内对其进行测试。

回答by Vaibhav Jain

Checkout this screen castabout finding memory leaks with JProfiler. It's visual explanation of @Dima Malenko Answer.

查看有关使用 JProfiler 查找内存泄漏的屏幕截图。这是@Dima Malenko Answer 的视觉解释。

Note: Though JProfiler is not freeware, But Trial version can deal with current situation.

注意:虽然 JProfiler 不是免费软件,但试用版可以应对当前的情况。