我有一些代码可以使用JDBC访问SQLite数据库。
我注意到,每次进行查询时,内存使用量都会增加-即使关闭连接后,内存使用量也不会下降。
这就是我在做什么:
1)关闭PreparedStatement
2)关闭ResultSet
3)关闭连接
这是heapdump分析的屏幕截图:
它显示了很多java.lang.ref.Finalizer
和很多PreparedStatement
和ResultSet
对象。
这是代码(在scala中,但应该可以与Java轻松比较):
val conn: Connection = DriverManager.getConnection(url)
// Gets strings by a query like SELECT .. WHERE foo = ?
def getStringsByQuery(query: String, param: String, field: String):Seq[String] = {
val st = conn.prepareStatement(query)
st.setString(1, param) //value of foo = ?
st.setFetchSize(Integer.MAX_VALUE)
st.setMaxRows(Integer.MAX_VALUE)
//Holder of results
var results = collection.mutable.Seq.empty[String]
val rs: ResultSet = st.executeQuery()
//add results to holder
while (rs.next())
results :+= rs.getString(field)
rs.close() //closing ResultSet
st.close() //closing PreparedStatement
results
}
这是我为此编写的测试:
test("detect memory leak") {
log.info("Starting in 10 sec")
Thread.sleep(10.seconds.toMillis)
//Calls a method over and over to see if there's a memory leak or not..
(1 to 1000).par.foreach(i => {
val randomWord = getRandomWord() //this produces a random word
val sql = "SELECT foo FROM myTable where bar = ?"
val results = getStringsByQuery(sql, randomWord, "bar")
})
conn.close() //close the connection
log.info("Closed conn, closing in 30 sec")
Thread.sleep(1.minutes.toMillis)
}
运行测试时-内存使用量从24.6 GB稳定增加到33 gb,并且从未下降(即使ResultSet + PreparedStatement被关闭),甚至在结束时关闭conn并且线程休眠1分钟-内存使用率仍然没有下降。
有人知道这是怎么回事吗?我将不胜感激。
答案 0 :(得分:3)
我们在生产环境中运行sqlite-jdbc,并且在检查堆转储时注意到类似的行为。
这些对象仅在堆上,因为它们尚未通过垃圾收集器运行。在https://blog.nelhage.com/post/three-kinds-of-leaks/的意义上,这是“类型2内存泄漏”,因为对象已分配并且寿命比您预期的长。
这对我们造成的问题是,机器将在Java堆上保留内存意外长的时间,从而在高峰请求时间内陷入内存压力。我们做了一些不同的事情,以确保更频繁地收集这些对象。
这些可能与您的用例不符,但是您可以根据所遇到的情况考虑一些选项。
答案 1 :(得分:0)
我关闭了连接和数据库对象,创建了一个具有相同参数和新连接的新对象数据库,最后调用了垃圾收集。
根据需要重复该过程。当我用完计算机内存的一半时,我会做一个计数器来重复该过程。
现在我可以正常运行了。
答案 2 :(得分:-1)
我通过将Sqlite数据集移植到CQEngine解决了此问题。再开心不过了。