Web 对象中的内存泄漏



我一直在使用Apple的EOF框架时遇到的一个问题。似乎偶尔,当创建EOEnterpriseObject或从数据库拉入编辑上下文时,EOF不会释放该对象消耗的内存,即使在释放和删除相关的企业对象,编辑上下文和对象存储之后也是如此。EOF似乎可以很好地处理大多数对象,但是我有2个对象,其中EOF始终保留对象使用的内存,直到应用程序重新启动。这两个 EO 都可能非常大(它们包含一个用于保存文件附件的 NSData 对象)。

使用 JProfiler,我发现对问题 EO 的引用由 EODatabase._snapshots 数组保存。

我想知道是否有其他人可能对EOF和/或Wonder项目有类似的问题。由于我一直在 2 种不同的情况下看到问题,我希望它有些常见,因此有一个解决方案。

我正在使用最新的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,最终会遇到内存不足错误。

2012-12-26 编辑

我对此做了更多的调查。问题似乎出在奇迹项目库中,而不是在EOF库中。我知道"问题"可能是我和/或我的理解,而不是奇迹库。:)

我创建了一个测试应用程序,复制了我一直看到的问题并将其发布在github上:https://github.com/t-evans/memory-leak-test.git。

测试应用程序大多只是 Eclipse 在添加新的 Wonder 应用程序时创建的默认应用程序。这些更改是在 Application.java 中添加了一行,在 Main.java 中添加了大部分代码,当然还有模型文件。目前,它配置为连接到名为"memleaktest"的postgres数据库。

我的应用程序的运行配置只有两个虚拟机参数:"-Xmx5m -Xmx50m"。 如果我启动应用程序并单击"创建对象"链接大约 5 次,它将遇到内存不足错误。使用 jConsole 监控内存显示,每次内存消耗都会增加约 5MB,并且应用程序永远不会放弃这 5MB。

到目前为止,我的发现指出ERXObjectStoreCoordinatorSyncer是罪魁祸首。在测试应用中,"应用程序.java"打开同步。Main.java 的构造函数只执行一个虚拟查询,最终导致Main._osc传递给 ERXObjectStoreCoordinatorSynchronizer.addObjectStore()(同步器需要 1 个以上的 OSC 来同步任何内容)。Main.createDataStore() 创建一个 OSC 和 EC,将一个数据存储对象添加到数据库中,然后删除 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(?

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

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

或者,您可能只需禁用InsertSnapshotProcessor:

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

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

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

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

值得再次检查以下内容:对EO,EC,NSData对象,组件的引用,看看是否偶然在任何地方您的巨大对象,更重要的是EC不被允许GC-ed。

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

很抱歉将此作为答案发布,但我是 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泄漏了 每当池大小> 1 和EODatabase._DatabaseRecord EO 已保存。在调试器中花费了几个小时后,我想我明白了 为什么..."

"通常,当EC保存更改时,ObjectsChangedInStore会 通知被触发,快照入到EODatabase的 _fastHashInsert,然后 EC 在处理更改(更新)时重新处理 EO 或在完成(插入)时释放快照。这些 操作会触发相应的_fastHashRemove以释放快照。

"问题似乎是ERXObjectStoreCoordinatorSyncer 将 ObjectChangedInStore 通知重新广播到其他 OSC 在游泳池里。这会导致插入快照,但周围没有 EC 为了进行清理,快照永远不会被删除。

最新更新