假设一个闭包已经在JVM中运行并在这个闭包范围内创建了一个对象,在JVM没有终止的情况下关闭执行后该对象是否仍然保留在JVM堆空间中?
一旦情况出现在Spark中 我使用下面的代码在执行程序中创建HBase连接:
rdd.foreachPartition(par => {
# create a connection here.
val connection = ConnectionFactory.createConnection(conf)
par.foreach(item => {
...
})
# close this connection.
connection.close()
})
显然,在执行程序中创建了连接。
完成这项任务后,如果我从未关闭此连接,会发生什么事?
如果火花推测在连接激活时杀死此任务怎么办? JVM仍将保持连接?
答案 0 :(得分:2)
我认为这不是关于Spark的,而是一般的JVM。
首先,在函数范围内创建的变量只不过是局部变量。因此,在范围结束后,该局部变量没有引用,并且符合GC的条件。因此,在您的特定情况下,如果您的connection
项目没有正确调用close()
(由于,可能之间发生了一些异常),connection
对象将被收集GC但连接本身并未关闭。我们将此情况称为连接泄漏。
处理此问题的最佳做法之一是确保每个连接都应该由try...finally
关闭,Java8可以通过try-with-resource进行简短的手动调用。对于Scala,可以创建一个等效结构,有关详细信息/所学课程,请参阅this post。
关于你的上一个问题,
如果火花猜测在连接激活时杀死此任务怎么办? JVM还会保持连接吗?
JVM将不再适当地保持连接。当此连接未被对等方明确关闭时,另一个对等方(服务器端)不知道它是否应该相应地关闭它直到某个空闲超时。无论如何,try finally
可以涵盖所有情况,以确保正确清理所有连接。
答案 1 :(得分:1)
对象生命周期不与闭包的生命周期绑定,也就是说,它可以比它更长。一旦没有从其他对象引用,垃圾收集器就会回收该对象(考虑到,从Scala 2.11开始,闭包被实现为匿名内部类)。
为了防止资源泄漏,您可以使用所谓的贷款模式,您可以在其中管理资源的生命周期并确保在使用后(或之后)将其释放发生错误):
def withConnection[A](manage: () => Connection)(use: Connection => A): A = {
val connection = manage()
try {
use(connection)
} finally {
connection.close()
}
}
rdd.foreachPartition(p =>
withConnection(ConnectionFactory.createConnection(conf)) { conn =>
p.foreach(item => {
??? // your code
})
}
Here您可以找到有关贷款模式的更深入的解释。