Java准备陈列的java.lang.outofmemoryerror:GC间接费用限制超过了



我知道与此相似的问题很多次,但是即使尝试了许多解决方案,我仍然看到了这个问题。

我们的应用程序允许技术用户创建参数化的RAW SQL查询,以从下载到Excel电子表格的DB中提取数据。

对于较小的数据集,这可以正常工作,但是,当文件大小开始接近10MB 时,我开始遇到此问题。

数据集可能是100k行或80-90MB的大小。如果可能的话,我不想增加JVM堆的大小。

希望我的代码中有一个明显的错误,但我尚未发现。ResultSet.next()循环似乎是问题的根源。是否有更有效的方法来停止吞噬堆空间?

任何帮助。谢谢

/*
*
 * 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是oracle

从一个go中获取和处理所有行中的所有行是一个坏主意。您需要实现分页的通用想法即可一次阅读和处理一个页面( n = page-size 行)。

您的页面大小应该足够最佳,以至于您不会发出太多的DB命中,同时又不会在内存中拥有太多记录。

Spring Batch API的JDBCPIGENEMRERERRERERRERERERENER实现了此概念。

请参考这个问题,以获取有关JDBC分页的更多想法。

除此之外,您不应该继续增加地图results的大小。您需要用周期冲洗这张地图。

希望这会有所帮助!

在这样的设计中,如果查询的结果返回大量数据,则在某个时候不可避免地会用完存储器,因为您将整个结果集加载到内存中。取而代之的是,您可以简单地说明您的数据量在数据量方面具有阈值。对于每行,您都可以计算其大小,并决定是否可以将其添加到JSON DOC中。如果您经过了阈值,则您停止并关闭结果集(这将取消服务器上的执行)。另一个选项将涉及流式传输结果,但这更复杂。

相关内容

最新更新