重用PreparedStatement时可能出现资源泄漏?

时间:2014-02-28 13:23:25

标签: java database jdbc resources

假设您有以下代码:

    Connection connection = null;
    PreparedStatement ps = null;

    try {
        Connection = connectionFactory.getConnection();

        ps = statement.prepareStamement(someQuery);
        // execute and read and stuff

        // now you want to use the ps again, since you don't want ps1, ps2, ps3, etc.
        ps = statement.prepareStatement(someOtherQuery); // DOES THIS FORM A POTENTIAL LEAK?
    } catch (a lot of exceptions) {
        // process exceptions
    } finally {

        // close the resources (using util class with null-checks and everything)
        SomeUtilClass.close(ps);
        SomeUtilClass.close(connection);
    }

重复使用ps变量潜在泄漏吗?

如果是这样,我会讨厌声明多个这样的预处理语句(ps1,ps2,ps3等)。我该如何重构这个?

有人想到吗?

修改

收到几个答案,说明这无关紧要。我想指出的是,我正在经历一个开放时间过长的开放游标,并且想知道这种模式是否与它有关。

我的想法是:

第一个语句被取消引用并且GC',所以在这个例子中第一个PreparedStatement如何关闭(数据库方式)?

5 个答案:

答案 0 :(得分:4)

这不一定会造成经典的,永久的",意义上的泄漏。垃圾收集器最终将到达预准备语句的第一个实例,并调用其终结器。

但是,让垃圾收集器处理释放潜在关键资源(例如DB句柄)是不好的做法:在重用预准备语句变量之前应该调用close方法,或者不重用变量一点都不。

try {
    Connection = connectionFactory.getConnection();

    ps = statement.prepareStamement(someQuery);
    // execute and read and stuff

    // now you want to use the ps again, since you don't want ps1, ps2, ps3, etc.
    // v v v v v v v v v v v
    SomeUtilClass.close(ps);
    // ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
    ps = statement.prepareStatement(someOtherQuery); // DOES THIS FORM A POTENTIAL LEAK?
} catch (a lot of exceptions) {
    // process exceptions
} finally {

    // close the resources (using util class with null-checks and everything)
    SomeUtilClass.close(ps);
    SomeUtilClass.close(connection);
}

答案 1 :(得分:3)

通常是的,这个可以形成内存泄漏(非托管资源)。不希望陈述明显的,如果资源应该关闭,那么你应该关闭它。在您的示例中,您将丢失对第一个准备好的语句的引用,因此无法关闭它们。

泄漏引用的GC和终结器可能会执行从任何非托管资源中重新计算内存的必要步骤,但是,最佳实践要求您应该确定性地执行此步骤。

在您的示例中,为了关闭所有预准备语句,您可以使用Iterable集合,如下所示:

Deque<PreparedStatement> statements = new ArrayDeque<PreparedStatement>();
try {
  statements.addFirst(statement.prepareStamement(someQuery));
  PreparedStatement statement = statements.getFirst();
  ..
}...
finally {
  // enumerate statements and close them.
}

答案 2 :(得分:0)

不,不是。这是因为Java运行时足够明智,可以在将其分配给其他内容之前从ps引用的前一个对象中删除引用。

如果引用计数降为零,则该对象是垃圾收集的候选对象,然后将调用其终结器并释放资源。

答案 3 :(得分:0)

重新使用预准备语句变量OF ITSELF不会造成资源泄漏。这里的问题是,在重新将其分配给第二个语句之前,您没有关闭第一个语句。

您可以将每个预准备语句包装在自己的try / catch块中,并在finally块中使用close,这样您就可以确保在继续下一个之前它已关闭。

如果所需的逻辑是在第一个查询失败时中止后续查询,则可以使用嵌套的try / catch块,内部块关闭预准备语句,然后重新抛出异常或抛出新异常。但在那时,坦率地说,我认为声明多个变量会更容易也更清晰。

一般来说,通过重用变量获得的收益很少。你是否想要节省内存?我不知道PreparedStatement对象占用了多少内存,但我怀疑它超过了几十个字节。除非你有数千或数百万的阵列,否则不值得担心。如果你必须编写额外的代码来支持重用变量 - 比如一个额外的关闭语句 - 该代码可能比额外的变量占用更多的内存。重复使用变量通常会使代码变得不那么清晰,因为现在读者必须弄明白,“哦,此时它保留了第四个查询”或者其他什么。

(我曾经和一个不遗余力地重复使用变量的人一起工作,并且他会将它们重新用于完全不同的东西。就像在函数的开头一样,某个字符串变量可能会占用客户数字,但然后一半下来,他用它来保存邮政编码。当然,这使得任何名称毫无意义,所以他称他所有的字符串变量s1,s2,s3等等;所有整数i1,i2,i3,试图阅读他的节目是一场噩梦。)

答案 4 :(得分:-1)

只要Connection正确关闭,就没有问题。顺便说一下:在这种情况下,不需要显式关闭(last)语句,因为关闭Connection会为你做这件事(并关闭通过它获取的所有其他资源)。

<强>更新 这取决于你对“有点太长”的定义。关闭Connection时,将尽快释放通过它分配的所有资源(包括所有语句,通过这些语句创建的可能ResultSet等)。根据GC何时启动,丢失的PreparedStatements(包括其ResultSet等)可能会提前关闭,但没有人可以保证这一点。因此,如果您真的需要依赖于语句在超出范围时立即关闭,是的,那么您必须在创建新方法之前调用相应的close方法。