【问题标题】:How to free memory after fetching data via jdbc通过 jdbc 获取数据后如何释放内存
【发布时间】:2012-01-11 16:46:50
【问题描述】:

我在 weblogic 上使用 spring jdbc。我将 fetch size 设置为 500,以便更快地从 db 中获取数据。但这会导致内存问题。这是一个例子:

http://webmoli.com/2009/02/01/jdbc-performance-tuning-with-optimal-fetch-size/

我的问题是如何释放这个内存?运行 GC 不工作,我猜它不工作,因为连接池中的连接是活动的。

代码:

public List<Msisdn> getNewMsisdnsForBulkSmsId(String bulkSmsId,String scheduleId,final int msisdnCount) throws SQLException {
            JdbcTemplate jdbcTemplate = getJdbcTemplate();
            jdbcTemplate.setFetchSize(500);
            jdbcTemplate.setMaxRows(msisdnCount);
            jdbcTemplate.query("select BULKSMS_ID, ? as , STATUSSELECTDATE, DELIVERYTIME, ID, MESSAGE from ada_msisdn partition (ID_"+bulkSmsId+") where bulksms_id = ? and status = 0 and ERRORCODE = 0 and  SCHEDULEID is null for update skip locked", new Object[]{scheduleId,bulkSmsId}, MsisdnRowMapper.INSTANCE);

  //Also i tried to close connection and run gc, this does not free the memory too.
            //jdbcTemplate.getDataSource().getConnection().close();
            //System.gc();

            return null;
}

当我将 fetch size 设置为 10 时,堆大小为 12 MB 如果我将 fetch size 设置为 500,则堆大小为 206 MB

感谢

【问题讨论】:

  • 如果您使用分析器,您应该能够看到为什么要保留此内存。
  • 似乎连接正在分配内存,在我的代码中没有内存泄漏:60.9% - 206 MB - 561,359 分配。 org.springframework.jdbc.core.JdbcTemplate.query
  • 如果您将 fetch size 设置为 10,那么问题真的会消失吗?
  • 是的,但此时查询时间过长。获取 500 x 500 的数据要快得多。

标签: java sql memory jdbc fetch


【解决方案1】:

添加示例代码等的更新:

听起来您只需要使用小于 500 的值,但这让我认为您返回的数据比结果集映射器实际使用的数据多 很多

现在我看到您将所有映射结果存储在List 中,我想说的是提取大小的问题可能是次要问题。 List&lt;Msisdn&gt; 和一组提取的 ResultSet 行所需的组合内存空间使您超出可用内存。

msisdnCount 的值是多少?如果它大于 500,那么您在 list 中使用的内存可能比在 ResultSet 的 500 条记录中使用的内存更多。如果它小于 500,那么我预计当您将提取大小设置为 msisdnCount 时也会出现内存问题,并且错误会在 min(msisdnCount, 500) 和 10 之间的某个值处消失。

将所有结果加载到列表中然后对其进行处理是一种经常会导致内存耗尽的模式。常见的解决方案是使用 streaming。如果您可以在每一行输入时对其进行处理并且将所有映射结果存储在您的list 中,那么您可以避免内存问题。

我在 Spring JDBC 核心包中没有看到任何流式支持,但如果我找到了,我会更新。

--

如果您要检索的行中的数据足够大,以致提取 500 行会用完您的堆,那么您必须要么每行返回更少的数据,要么一次提取更少的行。

您可能会发现您将获取的行存储在代码中的某个位置,这意味着它不是 ResultSet 占用了您的内存。例如,您可能会将所有行复制到某个集合实例。

我会查看每行中数据的大小并尝试减少可能包含大型数据类型的不需要的列,然后尝试简单地加载数据并迭代结果而不进行正常处理,这可能会存储某处的数据,以查看您可以使用您拥有的内存一次加载多少行。如果您在获取 500 行时内存不足,那么您必须提取大量数据。如果您实际上没有使用这些数据,那么您就是在浪费 CPU 和网络资源以及内存。

edit:您可能还想设置游标行为,以便为您的 JDBC 驱动程序提供更多帮助,以了解它可以丢弃什么。例如,您可以使用 ResultSet.TYPE_FORWARD_ONLY、ResultSet.CONCUR_READ_ONLY 准备语句。 http://docs.oracle.com/javase/6/docs/api/index.html?java/sql/ResultSet.html

【讨论】:

  • 我试过ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY 没有效果
  • 首先感谢您的回答。我更改了代码,但没有将查询结果分配给任何变量。我分析并看到分配 206 MB 的 java.sql.Statement 类。然后去weblogic控制台和挂起的连接池,运行GC。我看到记忆恢复正常。 (206 MB 已释放)。所以,我的问题是如何在不关闭连接的情况下释放内存。
  • 如果 GC 返回了内存,那么这表明 java.sql.Statement 不是占用内存的对象。我怀疑是来自上述证据的List&lt;Msisdn&gt;,除非“暂停”连接池意味着您关闭了连接,根据Weblogic文档,这只发生在“forceSuspend()”而不是“suspend()”中:@987654322 @ 如果你可以使用 Spring RowCallbackHandler 来处理每一行而不是创建一个List,你也许可以减少你的内存使用量。
猜你喜欢
  • 2012-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
  • 1970-01-01
  • 1970-01-01
  • 2014-12-20
  • 1970-01-01
相关资源
最近更新 更多