jvm不会为已处理的resultset对象释放内存

2hh7jdfx  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(473)

我需要将从jdbc结果集获取的约5000万行写入csv文件。
写入csv文件的150万行约为1GB。

jdbcTemplate.query(new CustomPreparedStatementCreator(arg), new ResultSetExtractor<Void>() {
    @Override
    public Void extractData(ResultSet rs) {
    while (rs.next()) {
        // transform each row's data (involves creation of objects)
        // write the transformed strings to csv file
    }
}

问题是我有一个8gb的堆,它很快就被填满了。
因此,在到达1000万行之前,我遇到了java.lang.outofmemoryerror。
我的另一个限制是查询读/写超时,设置为30分钟。
如何回收和重用jvm堆内存?
尤其是分配给我不再需要的对象的内存。
我读到强制gc运行并不能保证内存会被回收。
我有什么选择?我应该把责任推给非gc语言吗
像c,c++通过jna或jni来处理结果集?
[编辑]看来我处境艰难:d添加更多信息,正如@rzwitserloot所指出的
我正在从连接到数据湖的数据虚拟化工具中读取(仅限部分查询)数据。
数据虚拟化工具的jdbc驱动程序确实支持limit,但查询是由业务部门设计的,用于返回大量数据。因此,我有一个镜头拉数据,并生成一个csv-这意味着,我不能避免巨大的选择或把一个限制条款
我需要检查这些属性: resultSetType , resultSetConcurrency , resultSetHoldability .
我已经做了:
首先,我使用producer-consumer模式将jdbc获取操作与慢速文件写入操作分开。这有助于在30分钟超时前创建包含100-500万行的csv文件。
第二,我增加了用户线程的数量,让它们写入自己的单独部分文件,然后合并到单个csv文件中。这加快了文件写入速度,并在30分钟超时前创建了一个包含1000-2000万行的csv文件。
我在resultTextRactor中创建对象,并通过有界队列将其传递给使用者线程。一旦这些对象中的数据写入文件,就不需要这些对象了。

xuo3flqw

xuo3flqw1#

你粘贴了很少的代码;其中一个关键的线索是,通过设计,您粘贴的代码没有内存问题-resultset是有意设计为游标式的,从理论上讲,这意味着 .next() 调用会导致tcp/ip通信,要求数据库获取另一行。这就是为什么需要关闭resultsets(因为数据库正在维护一个单独的“版本”),这样,假设您使用的是serializable或其他一些clean reads隔离级别,那么任何其他启动的事务(或者更确切地说,当你打开一个你正在使用的*时,对你正在浏览的数据没有任何影响 .next() 电话。
现在,jdbcapi也非常灵活。例如,这需要大量的数据包、通信量和工作,因此在实践中,许多dbjdbc驱动程序要么一次发送所有数据,resultset.close什么也不做,要么至少会以更大的批处理发送数据,而且大多数 .next() 除了每100次呼叫或诸如此类的情况外,所有呼叫都不会产生db通信量。
因此,我们有两个主要的选择:
内存泄漏与您粘贴的内容无关;例如,您正在将csv数据写入一个不断增长的缓冲区,而您根本不将其流式传输到磁盘。再检查一下。用limit子句替换giant select,并在其周围添加giant for循环,以模拟编写大量记录,而实际上不需要从jdbc循环中查询太多内容。如果内存仍然不足,那就不是数据库层了。
尽管如此,jdbc驱动程序仍然在用一些持续占用内存的东西来实现它的resultset实现。
如果是#2,则有两种解决方案:
使数据库引擎不这样做。结果集具有“功能”,您可以在制作它们时询问需要哪些功能。例如,您可以告诉系统您希望结果集是所谓的“仅向前”。最有可能导致非内存咀嚼结果集的3个属性是使用 resultSetType = FORWARD_ONLY , resultSetConcurrency = CONCUR_READ_ONLY ,和 resultSetHoldability = CLOSE_CURSORS_AT_COMMIT . 实际上,我不知道如何告诉jdbctemplate这样做,但应该不会太难——jdbctemplate正在调用 java.sql.ConnectionprepareStatement 方法-确保它调用将所有属性设置为这些值的方法。
如果这样做不起作用,就解决它,使用offset/limit(很遗憾,这种语法取决于db引擎)一次获取页面。当然,如果在执行此操作时正在编辑表,除非您设置了可序列化事务级别,否则这会弄乱您的内容,并且您必须添加某种形式的order by子句,否则您将无法获得实际的保证结果以相同的顺序返回(如果没有这些,偏移量/限制分页并不能满足您的需要)。这有点奇怪-如果这种情况发生在您身上,您使用的是哪种奇特的三流糟糕的db引擎和/或jdbc驱动程序?

  • )“但是,我没有使用事务!”是的,你是,“auto commit=true”是通常所说的“no transactions”,但事实并非如此;它只是每个sql语句发送一个事务。唯一真正的“无事务”模式是db明确表示存在于事务之外的事物,而不是像myisam表类型的mysql这样的真正安全的dbs,myisam表类型本来就不是db,或者故意放宽隔离级别。

相关问题