Java 使用ResultSet时mysql内存(RAM)使用量增加?
我正在使用MySQL和Java选择大约50000条记录。 奇怪的是,当我使用ResultSet和next()方法读取数据时,我发现java应用程序的RAM使用在抓取过程中增加了。它从255MB开始,增加到379MB! 我使用的代码如下:Java 使用ResultSet时mysql内存(RAM)使用量增加?,java,mysql,memory-management,resultset,Java,Mysql,Memory Management,Resultset,我正在使用MySQL和Java选择大约50000条记录。 奇怪的是,当我使用ResultSet和next()方法读取数据时,我发现java应用程序的RAM使用在抓取过程中增加了。它从255MB开始,增加到379MB! 我使用的代码如下: try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/#mysql5
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/#mysql50#crawler - used in report?" + "user=root&password=&useUnicode=true&characterEncoding=UTF-8");
Statement st = conn.createStatement();
ResultSet rsDBReader = st.executeQuery("SELECT Id, Content FROM DocsArchive");
while (rsDBReader.next()) {
int docId = rsDBReader.getInt(1);
String content = rsDBReader.getString(2);
. . .
}
rsDBReader.close();
st.close();
conn.close();
} catch (Exception e) {
System.out.println("Exception in reading data: " + e);
}
我确信内存使用是针对ResultSet的,而不是程序的其他部分。
在这个程序中,我不需要更新记录,所以我希望在完成工作后删除所有记录。
我的猜测是,已经读取的记录不会被删除,程序也不会释放它们的内存。因此,我使用了一些技巧来避免这种情况,例如使用以下代码:
Statement st = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
st.setFetchSize(500);
rsDBReader.setFetchSize(500);
但他们没有改变任何事情(
所以我需要一些方法来移除(释放)已读取行的内存
另一个有趣的点是,即使在完成函数并关闭ResultSet、语句和连接,并转到程序的其他部分之后,程序内存使用量仍然没有减少!
谢谢我建议限制您在查询中检索的行数。50000行太多了,那么为什么不创建一个每次检索1000行的循环呢 如前所述,您可以使用
limit
语句来实现这一点。对于您正在处理的数据量,最好始终保持务实。您当前的select今天可能返回50000行,但如果明天它增长到一百万行会怎么样?您的应用程序将阻塞。因此,请逐步进行处理。用于向e驱动程序,它应该为包含一定行数的数据流传输ResultSet
。据我所知,MySQL Connector-J驱动程序确实理解提示并流传输ResultSet
s(但在MySQL的情况下,一次只能流一行)
默认值为0,将确保Connector-J驱动程序将获取完整的ResultSet
,而不进行流式处理。这就是为什么在MySQL中需要提供一个显式值-Integer.MIN_值
声明:
Statement st = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
不会导致流式传输结果集
(至少不是自动进行的)。它仅确保结果集不可“滚动”(即只能向前移动)且不可“更新”,并且在事务提交时,基础游标将关闭
如中所述,上述语句(不带ResultSet.CLOSE\u CURSORS\u AT\u COMMIT
参数)必须与语句.setFetchSize(Integer.MIN\u VALUE)
调用一起调用,才能逐行进行流式处理。此类场景中涉及的相关注意事项也已记录在案
请注意,MySQL文档中提到的示例中没有指定游标的可保持性,同样,此建议可能不适用。您看到的实际上是预期行为,不一定表示内存泄漏。对象实例在无法访问后不会立即在Java中进行垃圾收集,大多数Java虚拟机在分配内存后都不愿意返回操作系统。 如果您使用的是Oracle Java VM的最新版本,并且确实需要更具攻击性的垃圾收集器,则可以通过向Java命令添加以下参数来尝试G1GC实现: -XX:+UnlockExperimentalVMOptions-XX:+UseG1GC
G1GC垃圾回收器回收对象的速度通常比默认垃圾回收器快,未使用的内存也会被进程释放。请注意,最新版本的Postgres也存在类似问题。为了实现游标处理,需要禁用连接时的自动提交
connection.setAutoCommit(false)
并在SQL语句中使用一条语句(即只包含一个分号的语句)。这对我很有用
MySQL缓存查询结果。好的,谢谢,但是我如何清空cach?您可以做些什么来节省内存(不过需要更多时间)是限制每个SELECT语句获得的结果数否,MySQL JDBC驱动程序对控制获取大小的支持非常有限。默认行为是一次获取SELECT的整个结果。若要流式传输结果,必须将获取大小设置为Integer.MIN_值,并使用ResultSet.TYPE_创建语句仅WARD_,ResultSet.CONCUR_只读。这样做,必须考虑文档中指出的其他一些限制:我使用了以下代码:st.setFetchSize(500);rsDBReader.setFetchSize(500);但这一点没有改变thing@Soheil,文档很清楚500不会触发提示。我建议先阅读它们。如果你想快速回答,使用
Integer.MIN\u值而不是500可能有效。@Vinet Reynolds,你是对的。我用Integer.MIN\u值测试了它。它有效。