【问题标题】:How to test if a PreparedStatement is going to run out of memory如何测试 PreparedStatement 是否会耗尽内存
【发布时间】:2014-10-09 15:50:03
【问题描述】:

所以我有一些使用 JDBC 的 java 代码,使用准备好的语句将数据插入 PostgreSQL 数据库,看起来有点像这样:

PreparedStatement statement = // prep statement

for (int value: values) {

    statement.setInt(1, value);
    statement.addBatch();
}

statement.executeBatch();

问题是,我偶尔会遇到异常java.lang.OutOfMemoryError: Java heap space。我环顾四周,但找不到任何东西;如何测试语句是否即将耗尽内存以便我可以执行批处理。像这样的:

PreparedStatement statement = // prep statement

for (int value: values) {

    statement.setInt(1, value);
    statement.addBatch();

    if (statement.currentSize() + sizeof(int) > statement.mazSize()) {

        statement.executeBatch();
    }
}

statement.executeBatch();

感谢您的帮助。

【问题讨论】:

  • 您是否正在关闭您创建的所有资源 - 准备好的语句、结果集等?
  • 当我批处理语句时,我会查看内存消耗并在% x == 0 之后刷新它们。你可以监控可用的堆空间,但我只是懒惰了。
  • 使用较小的批量大小,例如 500 并迭代执行。
  • 我假设你之后关闭statement?那可能是泄漏。是setAutocommit(false), ... commit() 还是异常rollback()?很难想象那个OutOfMemoryError。除了你看到我假设的堆栈跟踪的异常。
  • 大家好,对不起,我错过了;是的,我确实关闭了资源。这实际上不是代码的样子,它只是一个简化。我的问题更多,过去我只是使用常量来确定批量大小,但我不知道我的代码部署到的目标机器的细节,所以我希望能够应对不同的可用内存量。

标签: java postgresql jdbc prepared-statement


【解决方案1】:

没有可靠的方法可以提前确定实例化是否会失败。

但是,更重要的是:您可能误用了 JDBC 批处理工具,该工具用于解决插入多行时的网络往返开销问题。任何超过 100 的批次大小都会显示收益递减,实际上可能会导致速度减慢。

因此,请更改您的批处理策略以使用较小的两位数整数的固定批处理大小。

【讨论】:

  • 在一个完全隔离的事务中写入 10m 个条目,我发现,基于使用的 db 配置,在刷新之前少于 1k 个条目会减慢整个过程。
  • @hannes 在网络方面,数据库有多远?在任何合理的生产系统中,它都非常接近。往返时间越长,所需的批大小就越大。
  • 只有一跳之遥,但使用量很大。这是我的第一个 mariadb,所以我最终花时间对它进行微调。但是您的语句适用于 db2、oracle 和 sqlserver。
  • 这是唯一的选择吗?我很乐意将批量大小限制为更小,但我没有选择目标机器,所以我不知道我分配的任意数字是否会导致内存不足异常。有什么办法可以计算出我必须使用的东西吗?我不知道这是否有所不同,但在此调用时它是单线程的。
  • 要获得 OOME,您确实需要非常大的批量。只要给它一个相当小的尺寸。
【解决方案2】:

我在使用与您的代码类似的代码时遇到了同样的问题,因为假设输入数组/列表 (values) 的大小最大为三位数 - 这是编写它的开发人员在代码级别的假设。

有一天,数组/列表接近 14k,这段代码导致了 OOM,尽管我注意到数据成功提交到数据库(它的 Db2 + Oracle Weblogic 设置)和几秒钟后几秒钟后OOMs ,系统出现故障。

为了解决这个问题,我只是将statement.executeBatch(); 设置为预先确定的固定大小,如下所示,假设固定批量大小为 100,

int counter = 0;
for (int value: values) {
    statement.setInt(1, value);
    statement.addBatch();
    ++counter;
    if(counter >= 100 ){
     statement.executeBatch();
     counter =0;
   }
}
statement.executeBatch();

我不确定这是否也与驱动程序有关,但我会假设这个固定数量的批次会因设置不同而有所不同(我有一个多节点功能强大的 DB2 集群和 Web 应用程序是也位于同一个数据中心)并且我已经测试了多种尺寸,并且 1000 的速度相当快,没有任何问题,但我减小了尺寸,因为此代码是从在线 UI 应用程序中踢出的,因此同一代码可能有多个触发器。

我想这取决于两者——一个可以保留在内存中的 Statement 对象的数量以及 DB 需要多长时间来处理一个提交的批次,因为代码将等待那么长时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 2015-08-23
    • 2012-11-10
    • 2012-08-19
    • 2011-02-09
    相关资源
    最近更新 更多