java - Xpath内存泄漏?

标签 java xpath memory-leaks

使用标准 Java 库 (1.6.0_27) 计算 XPath 表达式时似乎存在内存泄漏。

请参阅下面的一些代码来重现此问题:

public class XpathTest {

    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        docFactory.setNamespaceAware(true);
        DocumentBuilder builder = docFactory.newDocumentBuilder();
        Document doc = builder.parse("test.xml");

        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        XPathExpression expr = xpath.compile("//Product");

        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            System.out.println(node.getAttributes().getNamedItem("id"));

            XPathExpression testExpr = xpath.compile("Test");
            Object testResult = testExpr.evaluate(node, XPathConstants.NODE);
            Node test = (Node) testResult;
            System.out.println(test.getTextContent());
        }
        System.out.println(nodes.getLength());
    }
}

下面给出了一个示例 XML 文件:

<Products>
  <Product id='ID0'>
    <Test>0</Test>
  </Product>
  <Product id='ID1'>
    <Test>1</Test>
  </Product>
  <Product id='ID2'>
    <Test>2</Test>
  </Product>
  <Product id='ID3'>
    <Test>3</Test>
  </Product>
  ...
</Products>

当我使用 NetBeans 分析器运行此示例时,com.sun.org.apache.xpath.internal.objects.XObject 类的分配似乎一直在增加,即使在垃圾回收之后也是如此。

我是否以错误的方式使用了 XPath 库?这是 Java 库中的错误吗?是否有潜在的解决方法?

最佳答案

在这种情况下不存在“内存泄漏”。内存泄漏定义为应用程序无法回收内存的情况。在这种情况下,没有泄漏,因为所有 XObject(和 XObject[])实例都可以在某个时间点回收。

从 VisualVM 获得的内存分析器快照产生以下观察结果:

  • 所有 XObject(和 XObject[])实例都是在调用 XPathExpression.evaluate 方法时创建的。
  • XObject 实例在无法从 GC 根访问时被回收。在您的情况下,GC 根是 resulttestResult 局部变量,它们在主线程的堆栈中是本地的。

根据以上所述,我假设您的应用程序正在或可能会遇到内存耗尽,而不是内存泄漏。当您有大量来自 XPath 表达式求值的 XObject/XObject[] 实例时,这是正确的,因为

  • 它们仍然可以从 GC root 访问,
  • 或者垃圾收集器还没有来回收它们。

第一个问题的唯一解决方案是在需要时将对象保留在内存中。您似乎并没有在代码中违反这一点,但是您的代码肯定可以变得更有效率——您保留了第一个 XPath 表达式的结果,供第二个表达式使用,当然它可以更有效地执行。 //Product/Test可以用来获取Test节点,也可以获取父Product节点的id值如下所示代码片段(仅计算一个 XPath 表达式而不是两个):

expr = xpath.compile("//Product/Test");
nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++)
{
    Node node = nodes.item(i);
    System.out.println(node.getParentNode().getAttributes().getNamedItem("id"));
    System.out.println(node.getTextContent());
}
System.out.println(nodes.getLength());

就第二个观察而言,您应该获取 GC 日志(使用 verbose:gc JVM 启动标志)。然后你可以决定调整年轻一代的大小,如果你有太多的短命对象被创建,因为有可能将可达的对象移动到老年代,导致可能需要一个主要的集合来回收对象实际上本质上是短暂的。在理想情况下(考虑您发布的代码),应在 for 循环的每几次迭代中执行一次年轻代收集循环,因为循环本地的 XObject 实例应尽快回收因为 block 的局部变量超出范围。

关于java - Xpath内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7343744/

相关文章:

java - Selenium 多浏览器测试。只有最后一个浏览器会收到 cookie。测试NG

java - 保持文件句柄打开,还是根据需要重新打开?

java - 如何更新包中的参数

python - lxml etree 和 xpath 返回编码图像而不是 src 的 URL

xpath - Java Xpath 命名空间解析

php - DOMDocument/Xpath-如何从表中获取特定行

C++ BoundsChecker 跟进

Java 打印 : Windows vs Linux

java - 避免 JNI C 与 Java 之间的内存泄漏

android - 替换没有 onDestroy 的 fragment