我如何找出未释放对象的持有情况?
我们的一个程序有时会在一台用户的计算机上出现 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
转储堆并检查它。
我确信有不止一种方法可以做到这一点,但这里有一种简单的方法。 此描述适用于 MS Windows,但可以在其他操作系统上执行类似的步骤。
下面是一个示例:
要解释这一点,了解一些 数组类型命名法 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.
Here is an example:
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[].
尝试 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.
我会查看类中的集合(尤其是静态集合)(HashMap 是一个很好的起点)。 以此代码为例:
只要 HashMap 或其他集合具有对该对象的引用,它就不会被垃圾收集。
I would look at Collections (especially static ones) in your classes (HashMaps are a good place to start). Take this code for example:
As long as the HashMap or some other collection has a reference to that object it won't be garbage collected.
那里没有灵丹妙药,您必须使用探查器来识别保存这些不需要的对象的集合,并找到代码中应该删除它们的位置。 正如 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.
一个明显的候选者是具有终结器的对象。 当调用 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.
留意静态容器。 只要类被加载,静态容器中的任何对象都将保留。
编辑:删除了对 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.
我刚刚读过一篇关于此的文章,但很抱歉我不记得在哪里了。 我想可能是《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.
我使用 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
集合已经提到了。 另一个很难找到的位置是如果您使用多个类加载器,因为旧的类加载器可能无法被垃圾收集,直到所有引用都消失。
还要检查静态数据——这些很糟糕。 日志框架可以保持开放,这可以在自定义附加程序中保留引用。
您解决问题了吗?
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?
一些建议:
Some suggestions:
如果您在垃圾收集语言中遇到 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.