假设您有以下代码:
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如何关闭(数据库方式)?
答案 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方法。