我一直在尝试诊断我正在编写的Android应用程序中的内存泄漏。我有一个堆转储加载到Eclipse中,但我看到的结果非常好奇。堆中有大约20,000个异常实例(特别是来自UnboundID LDAP库的LDAPException),没有入站引用。
也就是说,它们出现在支配者树的根部。 OQL SELECT objects e FROM com.unboundid.ldap.sdk.LDAPException e WHERE (inbounds(e).length = 0)
返回超过20,000个结果,总计几乎所有堆。然而,GC在堆转储之前运行,我可以看到它在执行泄漏代码期间反复运行在控制台中。如果这些实例没有入站引用,那么它们可以保持活着吗?
我也尝试过“最短的GC路径”查询。它显示一个LDAPConnectionReader行,保留2个实例,以及带有各种十六进制地址的~20k LDAPException @ <addr> unknown
行。
更新:自发布以来,我没有时间进一步诊断这个问题,我发布的奖金在我可能会结束之前就已经结束了。我现在尽可能地奖励它,以免浪费点数。感谢所有关注此事的人!我将在稍后回来并再次更新进一步诊断的结果,当生活稍微忙碌时。
答案 0 :(得分:4)
在内存使用方面是否抛出这些异常,这个细节几乎无关紧要。
虽然您希望在堆转储中看到谁拥有引用,但由于某种原因,您无法实现此目的。我想知道本机代码是否会在堆转储工具中得到正确的符号化?
无论哪种方式,作为尝试的新事物,我建议不要调试抛出这些异常的点但创建的地方。在类和/或其所有构造函数上放置断点。理想情况下,您只是从堆转储引用中获取此信息,但如果您可以看到谁重复构建这些对象,它仍然可以证明是有用的......我猜它们来自同一个地方。
答案 1 :(得分:1)
如果您使用的是Eclipse,则可以在LDAPException
上添加断点。
在这里,您可以找到有关如何设置一个的教程:Eclipse Tip: Breakpoint on Exception。
只要抛出所选类型的异常,这些断点就会暂停执行。 一旦找到引发如此多异常的条件,就可以修复错误。
这并没有完全调试为什么未引用的异常填满堆,但我希望它可以帮助。
答案 2 :(得分:1)
我不熟悉OQL,特别是Android平台,或者该平台上Java GC的内部工作原理,但对我来说最明显的是缺少LDAPException
元数据。它有错误代码,消息,方法等......它在哪里?这是未初始化的吗?你有没有发布所有这些东西? like a server redirecting to itself可能会让我说“哦,这很奇怪,但它有点意义。”
您是否尝试过将此库替换为the JDK one?看起来如果可能的话应该很容易。
然后我会开始挤压它所有的东西。 GC特征可以提供线索。是否存在以某种方式逃避收集的实例?每秒创建多少?每次通过gc'd会有多少陈旧的,或者它是不变的?他们是在像Danny谈论的忙碌循环中创建的吗?如果您在繁忙的循环中拨打System.gc()
该怎么办?
但是,那是我开始打印调试的地方。希望有更好的解决方案。 :-P