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

假设您有以下代码:

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如何关闭(数据库方式)?

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

但是,让垃圾收集器处理释放可能关键的资源(例如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. // vvvvvvvvvvv 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); } 

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

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

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

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

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

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

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

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

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

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

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

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

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