我如何找出未释放对象的持有情况?

发布于 2024-07-06 14:36:47 字数 472 浏览 13 评论 0原文

我们的一个程序有时会在一台用户的计算机上出现 OutOfMemory 错误,但当然不是在我测试它时。 我只是用 JProfiler 运行它(在 10 天评估许可证上,因为我以前从未使用过它),并根据我们的代码前缀进行过滤,总大小和实例数量中最大的块是特定简单类的 8000 多个实例。

我单击 JProfiler 上的“垃圾收集”按钮,我们其他类的大多数实例都消失了,但这些特定的实例却没有。 我再次运行测试,仍然在同一个实例中,它创建了 4000 多个该类的实例,但是当我单击“垃圾收集”时,这些实例消失了,留下了 8000 多个原始实例。

这些实例确实会在不同阶段陷入不同的集合中。 我认为它们没有被垃圾收集这一事实必定意味着某些东西持有对其中一个集合的引用,因此它持有对对象的引用。

有什么建议我可以找出参考文献中的内容吗? 我正在寻找有关在代码中查找内容的建议,以及在 JProfiler 中查找该内容的方法(如果有)。

One of our programs is sometimes getting an OutOfMemory error on one user's machine, but of course not when I'm testing it. I just ran it with JProfiler (on a 10 day evaluation license because I've never used it before), and filtering on our code prefix, the biggest chunk both in total size and number of instances is 8000+ instances of a particular simple class.

I clicked the "Garbage Collect" button on JProfiler, and most instances of other classes of ours went away, but not these particular ones. I ran the test again, still in the same instance, and it created 4000+ more instances of the class, but when I clicked "Garbage Collect", those went away leaving the 8000+ original ones.

These instances do get stuck into various Collections at various stages. I assume that the fact that they're not garbage collected must mean that something is holding onto a reference to one of the collections so that's holding onto a reference to the objects.

Any suggestions how I can figure out what is holding onto the reference? I'm looking for suggestions of what to look for in the code, as well as ways to find this out in JProfiler if there are.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

人疚 2024-07-13 14:36:47

转储堆并检查它。

我确信有不止一种方法可以做到这一点,但这里有一种简单的方法。 此描述适用于 MS Windows,但可以在其他操作系统上执行类似的步骤。

  1. 如果您还没有 JDK,请安装它。 它附带了一堆简洁的工具。
  2. 应用。
  3. 打开任务管理器并找到 java.exe(或您正在使用的任何可执行文件)的进程 ID (PID)。 如果默认情况下未显示 PID,请使用 View > 选择“列...”以添加它们。
  4. 使用jmap转储堆。
  5. 在您生成的文件上启动jhat服务器,然后打开浏览器http://localhost:7000 (默认端口为 7000)。 现在,您可以浏览您感兴趣的类型以及实例数量、对它们的引用等信息。

下面是一个示例:

C:\dump>jmap -dump:format=b,file=heap.bin 3552

C:\dump>jhat heap.bin
Reading from heap.bin...
Dump file created Tue Sep 30 19:46:23 BST 2008
Snapshot read, resolving...
Resolving 35484 objects...
Chasing references, expect 7 dots.......
Eliminating duplicate references.......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

要解释这一点,了解一些 数组类型命名法 Java 使用 - 就像知道 class [Ljava.lang.Object; 真正意味着 Object[] 类型的对象强>。

Dump the heap and inspect it.

I'm sure there's more than one way to do this, but here is a simple one. This description is for MS Windows, but similar steps can be taken on other operating systems.

  1. Install the JDK if you don't already have it. It comes with a bunch of neat tools.
  2. Start the application.
  3. Open task manager and find the process id (PID) for java.exe (or whatever executable you are using). If the PID's aren't shown by default, use View > Select Columns... to add them.
  4. Dump the heap using jmap.
  5. Start the jhat server on the file you generated and open your browser to http://localhost:7000 (the default port is 7000). Now you can browse the type you're interested in and information like the number of instances, what has references to them, etcetera.

Here is an example:

C:\dump>jmap -dump:format=b,file=heap.bin 3552

C:\dump>jhat heap.bin
Reading from heap.bin...
Dump file created Tue Sep 30 19:46:23 BST 2008
Snapshot read, resolving...
Resolving 35484 objects...
Chasing references, expect 7 dots.......
Eliminating duplicate references.......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

To interpret this, it is useful to understand some of the array type nomenclature Java uses - like knowing that class [Ljava.lang.Object; really means an object of type Object[].

烟凡古楼 2024-07-13 14:36:47

尝试 Eclipse 内存分析器。 它将向您展示每个对象如何连接到 GC 根 - 一个不会被垃圾回收的对象,因为它由 JVM 持有。

请参阅 http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-heap-dump-analysis-finding-memory-leaks-with-one-click/ 了解更多信息关于 Eclipse MAT 的工作原理。

Try Eclipse Memory Analyzer. It will show you for each object how it is connected to a GC root - an object that is not garbage collected because it is held by the JVM.

See http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-heap-dump-analysis-finding-memory-leaks-with-one-click/ for more information on how Eclipse MAT works.

输什么也不输骨气 2024-07-13 14:36:47

我会查看类中的集合(尤其是静态集合)(HashMap 是一个很好的起点)。 以此代码为例:

Map<String, Object> map = new HashMap<String, Object>(); // 1 Object
String name = "test";             // 2 Objects
Object o = new Object();          // 3 Objects
map.put(name, o);                 // 3 Objects, 2 of which have 2 references to them

o = null;                         // The objects are still being
name = null;                      // referenced by the HashMap and won't be GC'd

System.gc();                      // Nothing is deleted.

Object test = map.get("test");    // Returns o
test = null;

map.remove("test");               // Now we're down to just the HashMap in memory
                                  // o, name and test can all be GC'd

只要 HashMap 或其他集合具有对该对象的引用,它就不会被垃圾收集。

I would look at Collections (especially static ones) in your classes (HashMaps are a good place to start). Take this code for example:

Map<String, Object> map = new HashMap<String, Object>(); // 1 Object
String name = "test";             // 2 Objects
Object o = new Object();          // 3 Objects
map.put(name, o);                 // 3 Objects, 2 of which have 2 references to them

o = null;                         // The objects are still being
name = null;                      // referenced by the HashMap and won't be GC'd

System.gc();                      // Nothing is deleted.

Object test = map.get("test");    // Returns o
test = null;

map.remove("test");               // Now we're down to just the HashMap in memory
                                  // o, name and test can all be GC'd

As long as the HashMap or some other collection has a reference to that object it won't be garbage collected.

宣告ˉ结束 2024-07-13 14:36:47

那里没有灵丹妙药,您必须使用探查器来识别保存这些不需要的对象的集合,并找到代码中应该删除它们的位置。 正如 JesperE 所说,静态集合是首先要考虑的地方。

No silver bullet there, you have to use the profiler to identify collections that hold those unneeded objects and find the place in code where they should have been removed. As JesperE said, static collections are the first place to look at.

雨轻弹 2024-07-13 14:36:47

一个明显的候选者是具有终结器的对象。 当调用 Finalize 方法时,它们可能会徘徊。 它们需要被收集,然后最终确定(通常仅使用单个终结器线程),然后再次收集。

另请注意,您可能会收到 OOME,因为 gc 无法收集足够的内存,尽管实际上有足够的内存来创建对象请求。 否则,性能就会被磨平。

One obvious candidate is objects with finalisers. They can linger while their finalize method is called. They need to be collected, then finalised (usually with just a single finaliser thread) and then collected again.

Also be aware that you can get an OOME because the gc failed to collect enough memory, despite there actually being enough for the object request to be created. Otherwise performance would grind into the ground.

因为看清所以看轻 2024-07-13 14:36:47

留意静态容器。 只要类被加载,静态容器中的任何对象都将保留。

编辑:删除了对 WeakReference 的不正确评论。

Keep an eye out for static containers. Any objects in a static container will remain as long as the class is loaded.

Edit: removed incorrect remark on WeakReference.

征棹 2024-07-13 14:36:47

我刚刚读过一篇关于此的文章,但很抱歉我不记得在哪里了。 我想可能是《Effective Java》一书中的内容。 如果我找到参考,我会更新我的答案。

它概述的两个重要教训是:

1) Final 方法告诉 gc 在剔除对象时要做什么,但它没有要求它这样做,也没有办法要求它这样做。

2) 现代非托管内存环境中的“内存泄漏”相当于被遗忘的引用。 如果您在使用完某个对象后没有将所有对该对象的引用设置为null,则该对象永远不会被剔除。 当实现您自己的集合类型或您自己的管理集合的包装器时,这一点最为重要。 如果您有一个池、堆栈或队列,并且当您从集合中“删除”对象时没有将存储桶设置为 null,则该对象所在的存储桶将保留该对象直到该存储桶被设置为引用另一个对象为止。

免责声明:我知道其他答案提到了这一点,但我试图提供更多细节。

I just read an article on this, but I'm sorry I can't remember where. I think it might have been in the book "Effective Java". If I find the reference, I'll update my answer.

The two important lessons it outlined are:

1) Final methods tell the gc what to do when it culls the object, but it doesn't ask it to do so, nor is there a way to demand that it does.

2) The modern-day equivalent of the "memory leak" in unmanaged memory environments, is the forgotten references. If you don't set all references to an object to null when you're done with it, the object will never be culled. This is most important when implementing your own kind of Collection, or your own wrapper that manages a Collection. If you have a pool or a stack or a queue, and you don't set the bucket to null when you "remove" an object from the collection, the bucket that object was in will keep that object alive until that bucket is set to refer to another object.

disclaimer: I know other answers mentioned this, but I'm trying to offer more detail.

深陷 2024-07-13 14:36:47

我使用 Yourkit Java profiler(http://www.yourkit.com) 对 Java 进行性能优化1.5. 其中有一节介绍如何处理内存泄漏。 我觉得很有用。

http://www.yourkit.com/docs/75/ help/performance_problems/memory_leaks/index.jsp

您可以获得 15 天的评估:http://www.yourkit.com/download/yjp-7.5.7.exe

BR,
~A

I've used the Yourkit Java profiler(http://www.yourkit.com) for performance optimizations on java 1.5. It has a section on how to work on memory leaks. I find it useful.

http://www.yourkit.com/docs/75/help/performance_problems/memory_leaks/index.jsp

You can get a 15 day eval : http://www.yourkit.com/download/yjp-7.5.7.exe

BR,

~A

清晨说晚安 2024-07-13 14:36:47

集合已经提到了。 另一个很难找到的位置是如果您使用多个类加载器,因为旧的类加载器可能无法被垃圾收集,直到所有引用都消失。

还要检查静态数据——这些很糟糕。 日志框架可以保持开放,这可以在自定义附加程序中保留引用。

您解决问题了吗?

Collections was already mentioned. Another hard-to-find location is if you use multiple ClassLoaders, as the old classloader may be unable to be garbage collected until all references have gone.

Also check statics - these are nasty. Logging frameworks can keep things open which may keep references in custom appenders.

Did you resolve the problem?

阳光下的泡沫是彩色的 2024-07-13 14:36:47

一些建议:

  • 无限的映射用作缓存,特别是
  • 在服务器应用程序中使用静态ThreadLocals时,因为线程通常不会死亡,因此ThreadLocal不会被释放
  • 实习字符串(Strings.intern()),这会导致在服务器应用程序中产生一堆字符串永久空间

Some suggestions:

  • Unlimited maps used as caches, especially when static
  • ThreadLocals in server apps, because the threads usually do not die, so the ThreadLocal is not freed
  • Interning strings (Strings.intern()), which results in a pile of Strings in the PermSpace
灰色世界里的红玫瑰 2024-07-13 14:36:47

如果您在垃圾收集语言中遇到 OOM 错误,通常意味着收集器未占用一些内存。 也许你的对象持有非java资源? 如果是这样,那么他们应该有某种“关闭”方法来确保资源被释放,即使 Java 对象没有足够快地收集。

If you're getting OOM errors in a garbage collected language, it usually means that there's some memory not being accounted by the collector. Maybe your objects hold non-java resources? if so, then they should have some kind of 'close' method to make sure that resource is released even if the Java object isn't collected soon enough.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文