我们的某个程序有时会在一个用户的计算机上出现OutOfMemory
错误,但当然不是在我测试它时。我只是用JProfiler运行它(在10天的评估许可证上,因为我之前从未使用它),并且对我们的代码前缀进行过滤,总大小和实例数量中最大的块是特定简单类的8000多个实例。
我点击了JProfiler上的“Garbage Collect”按钮,我们其他类的大部分实例都消失了,但不是这些特殊的。我再次运行测试,仍然在同一个实例中,它创建了4000多个类的实例,但是当我点击“垃圾收集”时,那些离开了8000多个原始实例。
这些实例会在各个阶段陷入各种集合中。我认为它们不是垃圾收集的事实必然意味着某些东西保留了对其中一个集合的引用,以便保留对对象的引用。
我有什么建议可以弄清楚什么是参考?我正在寻找代码中寻找内容的建议,以及在JProfiler中找到这些内容的方法。
答案 0 :(得分:19)
转储堆并检查它。
我确信不止一种方法可以做到这一点,但这里有一个简单的方法。此描述适用于MS Windows,但可以在其他操作系统上执行类似的步骤。
以下是一个例子:
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.
要解释这一点,了解一些array type nomenclature Java用法是很有用的 - 比如知道类[Ljava.lang.Object; 实际上是指类型的对象对象[] 强>
答案 1 :(得分:9)
尝试Eclipse Memory Analyzer。它将向您显示每个对象如何连接到GC根 - 一个非垃圾收集的对象,因为它由JVM保存。
有关Eclipse MAT如何工作的更多信息,请参阅http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-heap-dump-analysis-finding-memory-leaks-with-one-click/。
答案 2 :(得分:5)
我会在你的课程中看到集合(尤其是静态集合)(HashMaps是一个很好的起点)。以此代码为例:
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或其他一些集合具有对该对象的引用,它就不会被垃圾回收。
答案 3 :(得分:3)
那里没有银弹,您必须使用探查器来识别容纳那些不需要的对象的集合,并在代码中找到它们应该被移除的位置。正如JesperE所说,静态集合是第一个看待它的地方。
答案 4 :(得分:2)
留意静电容器。只要加载了类,静态容器中的任何对象都将保留。
编辑:删除了WeakReference上的错误备注。
答案 5 :(得分:2)
一个明显的候选人是具有终结者的对象。他们可以在调用finalize方法时逗留。需要收集它们,然后最终确定(通常仅使用一个终结器线程),然后再次收集。
还要注意你可以获得一个OOME,因为gc无法收集足够的内存,尽管实际上已经有足够的内容来创建对象请求了。否则,表演就会陷入困境。
答案 6 :(得分:1)
我刚读了一篇关于此的文章,但很抱歉我不记得在哪里。我认为它可能出现在“Effective Java”一书中。如果我找到了参考文献,我会更新我的答案。
它概述的两个重要课程是:
1)最终方法告诉gc当它剔除对象时要做什么,但是它没有要求它这样做,也没有办法要求它这样做。
2)现代的非托管内存环境中的“内存泄漏”等同于被遗忘的参考资料。如果在完成对象时未将对象的所有引用都设置为 null ,则该对象将永远被剔除。这在实现您自己的Collection或您自己的管理Collection的包装器时最为重要。如果您有一个池,一个堆栈或一个队列,并且在从集合中“删除”一个对象时没有将存储桶设置为 null ,那么该对象所在的存储桶将保留该对象直到该桶设置为引用另一个对象为止。
免责声明:我知道其他提到的答案,但我想提供更多细节。答案 7 :(得分:1)
我使用了Yourkit Java分析器(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
答案 8 :(得分:1)
已经提到了收藏品。另一个难以找到的位置是你使用多个ClassLoader,因为在所有引用都消失之前,旧的类加载器可能无法被垃圾收集。
同时检查静力学 - 这些都是令人讨厌的。日志框架可以保持打开状态,这可以保留自定义appender中的引用。
你解决了这个问题吗?
答案 9 :(得分:1)
一些建议:
答案 10 :(得分:0)
如果您在垃圾收集语言中收到OOM错误,通常意味着收集器不会记录某些内存。也许你的对象持有非java资源?如果是这样,那么他们应该有某种“关闭”方法,以确保即使没有尽快收集Java对象也会释放资源。