简体   繁体   English

Java PreparedStatement java.lang.OutOfMemoryError:超出了GC开销限制

[英]Java PreparedStatement java.lang.OutOfMemoryError: GC overhead limit exceeded

I know similar questions to this have been asked many times before, but even having tried many of the solutions given, I'm still seeing this problem. 我知道类似的问题曾经被问过很多次,但是即使尝试了给出的许多解决方案,我仍然看到这个问题。

Our application allows tech users to create parameterised raw SQL querys to extract data from the DB which is downloaded to an excel spreadsheet. 我们的应用程序允许技术用户创建参数化的原始SQL查询,以从数据库中提取数据,并将其下载到excel电子表格中。

For smaller datasets this works fine, however, when the file size starts approaching 10mb+ I start hitting this issue. 对于较小的数据集,此方法工作正常,但是,当文件大小开始接近10mb +时,我开始遇到此问题。

The datasets could potentially be 100k rows or 80-90mb in size. 数据集的大小可能为100k行或80-90mb。 I don't want to increase the JVM heap size if possible. 如果可能,我不想增加JVM堆大小。

Hopefully there is a glaring error in my code that I haven't spotted. 希望我的代码中有一个明显的错误,但尚未发现。 The resultSet.next() loop seems to be the source of the issue. resultSet.next()循环似乎是问题的根源。 Is there a more efficient way to write this to stop gobbling heap space? 有没有更有效的方式编写此代码来停止吞噬堆空间?

Any help much appreciated. 任何帮助,不胜感激。 Thanks 谢谢

/*
*
 * query is a raw sql query that takes parameters (using Mybatis)
 * criteriaMap the arguments that we subsitute into the query
 * 
*/

public List<Map<String, Object>> queryForJsonWithoutMapping(final String query, final Map<String, Object> criteriaMap){

SqlSession sqlSession = getSqlSessionInstance();

    String sql = "";
    Connection connection = null;
    PreparedStatement pstmt = null;
    ResultSet resultSet = null;

    try {

        final Configuration configuration = getSqlSessionInstance().getConfiguration();

        SqlSourceBuilder builder = new SqlSourceBuilder(configuration);

        SqlSource src = builder.parse(query, Map.class, null);

        BoundSql boundSql = src.getBoundSql(criteriaMap);

        sql = boundSql.getSql();

        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

        connection = sqlSession.getConnection();

        pstmt = connection.prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,  java.sql.ResultSet.CONCUR_READ_ONLY);

        // this function subs the params into the preparedStatement query
        buildParams(parameterMappings, criteriaMap, pstmt);

        resultSet = pstmt.executeQuery();

        // the while loop inside this function is where things start to hang
        List<Map<String, Object>> results = getObjectFromResultSet(resultSet);

        return results;

    } catch (Exception e) {
        LOG.error(e.getMessage(), e);
        LOG.error(ExceptionUtils.getStackTrace(e));
        throw new IllegalStateException(sql + " " + e.getMessage(), e);
    } finally {
        try{
            connection.close();
            pstmt.close();
            resultSet.close();
        }catch (SQLException e){
            e.printStackTrace();
        }
        sqlSession.close();
    }

private List<Map<String, ?>> getEntitiesFromResultSet(ResultSet resultSet) throws SQLException {
        ArrayList<Map<String, ?>> entities = new ArrayList<>(resultSet.getFetchSize());
        int index = 0;
        Map<String, Object> jsonObject;
        while (resultSet.next()) {
            jsonObject = getEntityFromResultSet(resultSet);
            entities.add(index, jsonObject);
            index ++;
        }
        resultSet.close();
        return entities;
    }

    private List<Map<String, Object>> getObjectFromResultSet(ResultSet resultSet) throws SQLException {
        ArrayList<Map<String, Object>> entities = new ArrayList<>(resultSet.getFetchSize());
        int index = 0;
        Map<String, Object> jsonObject;
        while (resultSet.next()) {
            jsonObject = getEntityFromResultSet(resultSet);
            entities.add(index, jsonObject);
            index ++;
        }
        resultSet.close();
        return entities;
    }

DB is oracle DB是甲骨文

In such a design, you will inevitable run out of memory at some point if the result of the query returns large amount of data because you're loading the entire ResultSet in memory. 在这种设计中,如果查询的结果返回大量数据,则在某个时候不可避免地会耗尽内存,因为您正在将整个ResultSet加载到内存中。 Instead you could simply state that you getXXXFromResultSet APIs have a threshold in terms of amount of data. 相反,您可以简单地声明getXXXFromResultSet API在数据量方面具有阈值。 For every row you calculate its size and decide if you can add it to your JSON doc. 对于每一行,您都要计算其大小并决定是否可以将其添加到JSON文档中。 If you've passed the threshold you stop there and close the ResultSet (which will cancel the execution on the server). 如果您已超过阈值,则在此处停止并关闭ResultSet(这将取消服务器上的执行)。 Another option would involve streaming the results but that's more complex. 另一种选择是涉及流式传输结果,但这更加复杂。

Getting and processing all rows from a DB table in one go is a bad idea. 一次性获取和处理数据库表中的所有行是一个坏主意。 You need to implement generic idea of Pagination ie you read and process one page ( n = page-size rows) at a time. 您需要实现一般的分页思想,即您一次读取并处理一页( n =页面大小的行)。

Your page size should be optimal enough that you don't make too many DB hits and at the same time not to have too many records in memory. 您的页面大小应达到最佳状态,以免造成数据库命中次数过多,并且同时在内存中没有太多记录。

JdbcPagingItemReader of Spring Batch API implements this concept. Spring Batch API的JdbcPagingItemReader实现了这个概念。

Refer this SO Question to get more ideas on pagination with JDBC. 请参阅此SO问题以获取有关JDBC分页的更多想法。

In addition to that, you shouldn't keep increasing the size of your Map results . 除此之外,您不应该继续增加Map results的大小。 You need to flush this map in cycles. 您需要循环刷新此映射。

Hope this helps !! 希望这可以帮助 !!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 詹金斯 java.lang.OutOfMemoryError:超出 GC 开销限制 - Jenkins java.lang.OutOfMemoryError: GC overhead limit exceeded java.lang.OutOfMemoryError:GC开销限制超出了android studio - java.lang.OutOfMemoryError: GC overhead limit exceeded android studio Gridgain:java.lang.OutOfMemoryError:超出了GC开销限制 - Gridgain: java.lang.OutOfMemoryError: GC overhead limit exceeded Spark失败了java.lang.OutOfMemoryError:超出了GC开销限制? - Spark fails with java.lang.OutOfMemoryError: GC overhead limit exceeded? SonarQube java.lang.OutOfMemoryError:超出了GC开销限制 - SonarQube java.lang.OutOfMemoryError: GC overhead limit exceeded Tomcat java.lang.OutOfMemoryError:超出了GC开销限制 - Tomcat java.lang.OutOfMemoryError: GC overhead limit exceeded java.lang.OutOfMemoryError:超出 GC 开销限制 - java.lang.OutOfMemoryError: GC overhead limit exceeded java.lang.OutOfMemoryError:在PersistenceUnit的预部署中超出了GC开销限制 - java.lang.OutOfMemoryError: GC overhead limit exceeded on predeployment of PersistenceUnit Jmeter java.lang.OutOfMemoryError:超出GC开销限制 - Jmeter java.lang.OutOfMemoryError: GC overhead limit exceeded SPARK SQL java.lang.OutOfMemoryError:超出GC开销限制 - SPARK SQL java.lang.OutOfMemoryError: GC overhead limit exceeded
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM