【问题标题】:memory leak when executing sql query using postgres and java使用 postgres 和 java 执行 sql 查询时内存泄漏
【发布时间】:2019-08-28 14:56:40
【问题描述】:

我需要在 postgres DB 上使用 java 运行一些查询。问题是当我使用此代码执行查询时,我的内存增加了,即使我关闭了语句并且结果集内存仍在增加。当然,GC 会被执行,但问题是有时 GC 是在查询执行过程中执行的,它会改变我查询的执行时间。我如何解决这个问题以防止在 SQL 查询中间执行 GC 并在我的情况下防止内存泄漏?

 public long getStatementExecutionTimeInMilliseconds( final String sqlQuery, 
final int fetchSize, final boolean fetchAttributesFlag,
            Integer numberOfSelectedFields) throws SQLException {

        Statement stmt = null;
        ResultSet rs = null;
        int numberOfResult = 0;
        if (numberOfSelectedFields == null) {
            numberOfSelectedFields = getNumberOfSelectedFields(sqlQuery);
        }
        long start = 0;
        try {
            stmt = createStatement (fetchSize);
            stmt.executeQuery (sqlQuery);
            start = System.currentTimeMillis ();
            rs = stmt.executeQuery (sqlQuery);
            while (rs.next ()) {
                if (fetchAttributesFlag) {
                    for (int i = 1; i <= numberOfSelectedFields; i++) {
                        rs.getInt (i);
                    }
                }
            }

            stmt.close ();
        }

        finally {
            DatabaseUtils.tryClose(rs);
            DatabaseUtils.tryClose(stmt);
        }
        //System.out.println (numberOfResult);
        final long executionTimeInMilliseconds = System.currentTimeMillis() - start;


        return executionTimeInMilliseconds;
    }

【问题讨论】:

  • 为什么要执行两次语句?为什么您认为内存配置文件表示内存泄漏而不仅仅是正常的系统使用情况?
  • 我关闭语句只是为了确保语句将被释放,即使我关闭语句并且结果集内存仍然增加,并且仅在我执行查询时才会发生。为什么执行(查询)会消耗大量内存而没有释放它。
  • 清除旧对象是垃圾收集器的工作,它会在确定需要运行时执行。在它运行之前,对象将停留在等待收集的堆上。这并不意味着存在内存泄漏。
  • 好的。但是例如我需要执行相同的查询3次,问题是在某些情况下GC会在执行过程中执行,这会影响执行时间。
  • 减少 SQL 执行的内存占用虽然有用,但不会阻止 GC 进行完整收集,只会降低它们的常见性,从而降低在执行期间发生这种情况的可能性一个问题。您可能应该查看 CMS 和 G1 垃圾收集器,看看它们是否更适合您。还值得研究对从数据库中拉回的结果进行分页。更大的页面意味着内存中保存的结果更多,但返回数据库以获取下一页数据的往返次数更少。

标签: java postgresql jdbc memory-management garbage-collection


【解决方案1】:

默认情况下,PostgreSQL JDBC 驱动程序会将整个结果集存储到 RAM 中。

要改变这一点,请将 fetch size 设置为不同于 0 的值。然后将使用游标,并且内存消耗应该是稳定的。

来自the documentation

// make sure autocommit is off
conn.setAutoCommit(false);
Statement st = conn.createStatement();

// Turn use of the cursor on.
st.setFetchSize(50);

【讨论】:

  • 感谢@laurenz 我修复了 fetchsize 并对其进行了调整,但消耗量仍在增加。似乎对于每个查询,即使您关闭所有内容,数据仍保留在 RAM 中。
  • 那么这是在 Java 程序的整个生命周期中积累的普通垃圾。您可以尝试编程以减少垃圾的产生,但这仅在一定程度上有效。您可以尝试给 Java 更少的内存,那么垃圾收集将不得不更频繁,并且可能会造成更少的伤害。或者您切换到可以控制内存管理的编程语言,例如 C。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-06
  • 1970-01-01
  • 2014-12-24
  • 1970-01-01
  • 2020-07-13
  • 1970-01-01
  • 2016-02-20
相关资源
最近更新 更多