我一直在与一个使用 Apple 的 EOF 框架有一段时间的问题作斗争。似乎有时,当创建 EOEnterpriseObject 或从 DB 拉入编辑上下文时,EOF 不会释放对象消耗的内存,即使在相关企业对象、编辑上下文和对象存储被释放后也是如此并删除。似乎大多数对象都由 EOF 处理得很好,但是我有 2 个对象,其中 EOF 始终保持对象使用的内存,直到重新启动应用程序。这两个 EO 都可能非常大(它们包含一个用于保存文件附件的 NSData 对象)。
使用 JProfiler,我发现对问题 EOs 的引用由 EODatabase._snapshots 数组保存。
我想知道是否其他人可能对 EOF 和/或项目 Wonder 有类似的问题。由于我一直在两种不同的情况下看到问题,我希望它有点普遍,因此有一个解决方案。
我正在使用最新的 WebObjects 库 (5.4.3) 和最新的 Wonder 库。
下面不是我的确切代码,但它是仍然存在内存泄漏的最小可能示例:
public WOActionResults createEmailHistoryEntry() throws MessagingException, IOException {
File emailFile = new File("Email_with_large_attachment.eml");
javax.mail.Message message = EmailUtils.convertEmlToMessage( emailFile );
EOObjectStore osc = new ERXObjectStoreCoordinator(true);
EOEditingContext ec = ERXEC.newEditingContext(osc);
ec.lock();
try {
EmailHistoryEntry historyEntry = (EmailHistoryEntry) EOUtilities.createAndInsertInstance( ec, EmailHistoryEntry.class.getSimpleName() );
EmailDataObject emailData = (EmailDataObject) EOUtilities.createAndInsertInstance( ec, EmailDataObject.class.getSimpleName() );
emailData.setEmailHistoryEntry( historyEntry );
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
message.writeTo( byteStream );
NSData rawEmail = new NSData( byteStream.toByteArray() );
emailData.setRawEmail( rawEmail );
ec.saveChanges();
}
finally {
ec.unlock();
ec.dispose();
osc.dispose();
}
return null;
}
我不知道我在那里做了什么不寻常的事情。如果我多次运行它,每次内存消耗会增加大约 140MB,最终会遇到 OutOfMemory 错误。
2012-12-26 编辑
我对此进行了更多调查。看来问题出在 Project Wonder 库中,而不是 EOF 库中。我理解“问题”可能是我和/或我的理解,而不是 Wonder lib。:)
我创建了一个测试应用程序,它复制了我一直看到的问题并将其发布在 github 上:https ://github.com/t-evans/memory-leak-test.git 。
测试应用程序大多只是 Eclipse 在您添加新的 Wonder 应用程序时创建的默认应用程序。更改是在 Application.java 中添加了一行,在 Main.java 中添加了大部分代码,当然还有模型文件。目前,它被配置为连接到名为“memleaktest”的 postgres 数据库。
我的应用程序的运行配置只有两个 VM 参数:“-Xmx5m -Xmx50m”。如果我启动应用程序并单击“创建对象”链接大约 5 次,它将遇到 OutOfMemory 错误。使用 jConsole 监控内存显示,内存消耗每次增加大约 5MB,而应用程序永远不会放过这 5MB。
到目前为止,我的发现指出 ERXObjectStoreCoordinatorSynchronizer 是罪魁祸首。在测试应用程序中,Application.java 开启了同步。Main.java 的构造函数只是执行一个虚拟查询,最终导致 Main._osc 被传递给 ERXObjectStoreCoordinatorSynchronizer.addObjectStore()(同步器需要超过 1 个 OSC 才能同步任何内容)。Main.createDataStore() 创建一个 OSC 和 EC,将一个 DataStore 对象添加到 DB,然后核对 OSC 和 EC。
在新对象、OSC 和 EC 被销毁、处置并超出范围后,同步器运行并将新创建的(但现在已过时)对象添加到另一个 OSC,最终重新添加新对象到 EODatabase._snapshots 数组,它一直保留到另一个 OSC 被处置。
新的 EO 与它之后的另一个OSC同步似乎很奇怪,它的 EC 和 OSC 已经死了,消失了,超出了范围。同步器不应该同步 EO 超出范围的事实并将其从所有其他 OSC 中删除(或者首先不将其添加到其他 OSC)?
我知道可以通过调用关闭同步
ERXObjectStoreCoordinatorSynchronizer.synchronizer().setDefaultSettings(
new SynchronizerSettings(false, false, false, false));
这将避免该问题,但同步器的默认设置已打开所有内容,这会导致相当大的泄漏。
这是一个错误,还是我做错了什么?我很困惑为什么其他人似乎没有遇到这种情况。或者他们可能遇到了它,但没有注意到内存泄漏,因为他们没有使用大型 EO(?)