【问题标题】:What is the best way to recycle Domino objects in Java Beans在 Java Beans 中回收 Domino 对象的最佳方法是什么
【发布时间】:2012-06-22 15:35:51
【问题描述】:

我使用一个函数来访问一个配置文件:

private Document lookupDoc(String key1) {
    try {
        Session sess = ExtLibUtil.getCurrentSession();
        Database wDb = sess.getDatabase(sess.getServerName(), this.dbname1);
        View wView = wDb.getView(this.viewname1);
        Document wDoc = wView.getDocumentByKey(key1, true);
        this.debug("Got a doc for key: [" + key1 + "]");
        return wDoc;
    } catch (NotesException ne) {
        if (this.DispLookupErrors)
            ne.printStackTrace();
        this.lastErrorMsg = ne.text;
        this.debug(this.lastErrorMsg, "error");
    }
    return null;
}

在另一种方法中,我使用这个函数来获取文档:

Document wDoc = this.lookupDoc(key1);

if (wdoc != null) {
    // do things with the document
    wdoc.recycle();
}

我应该在回收 Document 对象时回收 Database 和 View 对象吗?还是应该在函数返回 Document 之前回收它们?

【问题讨论】:

  • 除了 Tim 的出色回应之外,还有一条评论:检索特定文档的(更快)方法是使用 db.getDocumentByUNID() 调用。因此,如果您需要多次检索同一个文档,在第一次调用时,您可以从视图中检索它并将其 UNID 存储在私有变量中。在随后的调用中,您可以使用该 UNID 来检索它。
  • @Mark:一旦读取文档,我已经在适当的范围变量中实现了数据缓存,因此在范围变量消失之前很少需要重新读取文档......在某些情况下,作用域变量中的信息包括 UNID,以便尽可能快地重新访问文档。 /新人

标签: xpages


【解决方案1】:

最佳实践是在创建对象的范围内回收所有 Domino 对象。但是,回收任何对象会自动回收它“下方”的所有对象。因此,在您的示例方法中,您无法回收 wDb,因为这会导致 wDoc 也被回收,因此您将返回一个回收的 Document 句柄。

因此,如果您想确保没有泄漏内存,最好以相反的顺序回收对象(例如,首先是文档,然后是视图,然后是数据库)。这往往需要对您的方法进行结构化,以便您对 Domino 对象inside做任何你需要的事情。

例如,我假设您定义获取配置文档的方法的原因是您可以从中提取配置设置的值。因此,与其定义返回文档的方法,不如定义一个返回item值的方法:

private Object lookupItemValue(String configKey, itemName) {
    Object result = null;
    Database wDb = null;
    View wView = null;
    Document wDoc = null;
    try {
        Session sess = ExtLibUtil.getCurrentSession();
        wDb = sess.getDatabase(sess.getServerName(), this.dbname1);
        wView = wDb.getView(this.viewname1);
        wDoc = wView.getDocumentByKey(configKey, true);
        this.debug("Got a doc for key: [" + configKey + "]");
        result = wDoc.getItemValue(itemName);
    } catch (NotesException ne) {
        if (this.DispLookupErrors)
            ne.printStackTrace();
        this.lastErrorMsg = ne.text;
        this.debug(this.lastErrorMsg, "error");
    } finally {
        incinerate(wDoc, wView, wDb);
    }
    return result;
}

上面有几点值得解释:

  • 通常在 Java 中,我们在第一次使用时声明变量,而不是目录样式。但是对于 Domino 对象,最好恢复到 TOC,这样无论是否抛出异常,我们都可以在完成后尝试回收它们……因此使用 finally。
  • 在 TOC 中也声明了返回对象(应该是项目值,而不是文档本身),因此我们可以在方法结束时返回该对象 - 同样,无论是否遇到异常(如果有一个异常,大概还是 null)。
  • 此示例调用一个实用方法,该方法允许我们将所有 Domino 对象传递给单个方法调用以进行回收。

这是该实用方法的代码:

private void incinerate(Object... dominoObjects) {
    for (Object dominoObject : dominoObjects) {
        if (null != dominoObject) {
            if (dominoObject instanceof Base) {
                try {
                    ((Base)dominoObject).recycle();
                } catch (NotesException recycleSucks) {
                    // optionally log exception
                }
            }
        }
    }
}

它是私有的,因为我假设您将在同一个 bean 中定义它,但最近我倾向于将其定义为 Util 类的公共静态方法,允许我在几乎任何地方遵循相同的模式.

最后一点:如果您要从配置文档中检索大量项目值,显然为您要返回的每个项目值建立一个新的数据库、视图和文档句柄会很昂贵。因此,我建议重写此方法以接受项目名称的 List(或 String[])并返回结果值的 Map。这样您就可以为数据库、视图和文档建立一个句柄,检索您需要的所有值,然后在实际使用返回的项目值之前回收 Domino 对象。

【讨论】:

  • Tim,您一如既往地是这项技术的最佳人选。感谢此答案的卓越性和完整性。非常非常有帮助。 /新人
  • 我的荣幸。很高兴您发现它很有用。
  • 当前数据库不应被回收,因为任何视图或其他子对象都将在调用代码中消失。当然,这同样适用于所有 Domino 对象。如果您在调用代码回收时碰巧有一个 View 对象指向同一个视图,则此处的视图也会破坏该视图对象。在大多数情况下,不回收当前的数据库和会话就足够了。
  • 确实如此,并在此答案中提到:stackoverflow.com/a/17216904/1171761 这可能有点令人困惑,因为可能有多个 Java 指针指向同一个后端对象,每个指针都有一个单独的 C++ 句柄.所以如果按照Newbs这里的方式获取当前数据库,即使是当前数据库也应该被回收,因为即使database变量引用同一个Domino数据库,这里获取的指针有一个单独的C++句柄。这就是为什么您应该始终使用变量解析器来获取当前会话和数据库的原因。
  • ...或者只使用 OpenNTF Domino API,这样您就不必再担心回收问题了。 ;)
【解决方案2】:

这是我正在尝试的一个想法。蒂姆的回答非常好,但对我来说,我确实需要将文档用于其他目的,所以我尝试了这个..

Document doc = null;
            View view = null;
            try {
                Database database = ExtLibUtil.getCurrentSessionAsSigner().getCurrentDatabase();
                view = database.getView("LocationsByLocationCode");
                doc = view.getDocumentByKey(code, true);
                //need to get this via the db object directly so we can safely recycle the view
                String id = doc.getNoteID();
                doc.recycle();
                doc = database.getDocumentByID(id);

            } catch (Exception e) {
                log.error(e);
            } finally {
                JSFUtils.incinerate(view);
            }
            return doc;

然后,您必须确保在调用此方法的任何方法中安全地回收 doc 对象

【讨论】:

  • 如果可以的话,你真的应该使用OpenNTF Domino API,因为它会为你处理所有这些;-) ...你需要在 9.0.x 上。实际上-在您当前的示例中,我没有看到您有任何收获?你释放一个手柄并在之后使用一个新的?
  • 是的,我在 9 个环境中使用 ODA,但对于某些客户端仍然必须使用 8.53 :( 在我的示例中,第二个文档句柄不是视图的子句柄,因此可以安全地回收视图 obj。而且您不必担心通过 ExtLibUtils 检索 db 对象
  • 我知道有人(我认为是 Paul Withers,或者他会知道)已将 ODA“反向移植”到 8.5.3。可能不是最新版本 - 但它仍然会给你回收魔法。也许值得一试? ;-)
【解决方案3】:

我有一些临时文档作为配置文档存在了一段时间,然后不再需要,所以请删除。这是由现有的 Notes 客户端应用程序强制执行的:它们必须存在才能保持快乐。

我编写了一个类,它有一个 Java 日期、字符串和双精度的 HashMap,项目名称作为键。所以现在我有了文档的可序列化表示,加上原始文档 noteID,以便在不再需要时可以快速找到并修改/删除。

这意味着可以收集配置文档,标准例程会为 Java 表示创建所有项目的映射,同时考虑项目类型。然后可以立即回收 doc 对象。

返回对象是文档的 Java 类表示,getValue(String name)setValue(String name, val) 其中 val 可以是 Double String 或 Date。注意:这个结构不需要富文本或附件,所以它保持简单的字段值。

虽然配置文档有很多项目,但它运行良好,这可能意味着在内存中不必要地保存大量信息。但在我的特定情况下并非如此。

关键是:Java 对象现在是可序列化的,因此它可以保留在内存中,并且正如 Tim 的精彩回复所暗示的,可以立即回收该文档。

【讨论】:

    猜你喜欢
    • 2010-11-25
    • 2023-03-24
    • 2010-09-15
    • 2017-10-27
    • 2011-09-10
    • 2011-12-19
    • 1970-01-01
    相关资源
    最近更新 更多