1

我一直在与一个使用 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(?)

4

3 回答 3

1

我发现的最佳解决方案是避免 ERXObjectStoreCoordinatorSynchronizer(这意味着您还需要避免 ERXObjectStoreCoordinatorPool,因为它使用同步器),或者禁用同步器,如下所示:

ERXObjectStoreCoordinatorSynchronizer.synchronizer().setDefaultSettings(new SynchronizerSettings(false, false, false, false));

或者,您可能只需禁用 InsertSnapshotProcessor 就可以逃脱:

ERXObjectStoreCoordinatorSynchronizer.synchronizer().setDefaultSettings(new SynchronizerSettings(false, true , true , true ));

因为那似乎是内存泄漏发生的地方(其他的也可能导致问题,但我还没有特别看到)。

发布到 Project Wonder 邮件组后,似乎没有人有比上述更好的解决方案。

于 2012-12-31T19:00:47.470 回答
0

我希望您可能已经验证了代码的所有部分并对其进行了分析。但我仍然觉得问题仅存在于您的代码中。

值得再次检查以下内容:对 EO、EC、NSData 对象、组件的引用,看看是否偶然发现了你的巨大对象,更重要的是,EC 不允许被 GC-ed。

如果问题仍然存在,我们可能需要更多信息来帮助您调试此问题!

于 2012-04-28T02:40:20.477 回答
0

很抱歉将此作为答案发布,但我是 StackOverflow 的新手,没有足够的积分来添加评论。只是想在 Wonder Github 存储库中添加对解决此问题的问题的引用,希望它有助于找到解决方案:

https://github.com/wocommunity/wonder/issues/130——来自用户'nullterminated'(我相信这是Ramsey Gurley,他确认了这个问题——见http://comments.gmane.org/gmane.comp .web.webobjects.wonder-disc/19078 )

“似乎 ERXObjectStoreCoordinatorPool 泄漏 EODatabase._DatabaseRecord 对象,只要池大小大于 1 并保存 EO。在调试器中经过数小时后,我想我明白为什么......”

“通常,当 EC 保存更改时,会触发 ObjectsChangedInStore 通知,使用 EODatabase 的 _fastHashInsert 插入快照,然后 EC 在处理更改(更新)或在 finalize(插入)时释放快照时拒绝 EO。这些操作会触发相应的_fastHashRemove 释放快照。”

“问题似乎是 ERXObjectStoreCoordinatorSynchronizer 将 ObjectChangedInStore 通知重新广播到池中的其他 OSC。这会导致插入快照,但没有 EC 可以清理,快照永远不会被删除。”

于 2013-10-05T11:53:37.027 回答