Java PreparedStatement Java.lang.OutOfMemoryError:超出GC开销限制
我知道类似的问题以前已经被问过很多次了,但是即使尝试了很多给出的解决方案,我仍然看到这个问题 我们的应用程序允许技术用户创建参数化原始SQL查询,以从数据库中提取数据,并将其下载到excel电子表格中 对于较小的数据集,这很好,但是,当文件大小开始接近10mb+时,我开始遇到这个问题 数据集可能有10万行或80-90mb大小。如果可能的话,我不想增加JVM堆的大小 希望我的代码中有一个我没有发现的明显错误。resultSet.next()循环似乎是问题的根源。有没有更有效的方法来写这个来停止占用堆空间 非常感谢您的帮助。谢谢Java PreparedStatement Java.lang.OutOfMemoryError:超出GC开销限制,java,oracle,jdbc,prepared-statement,Java,Oracle,Jdbc,Prepared Statement,我知道类似的问题以前已经被问过很多次了,但是即使尝试了很多给出的解决方案,我仍然看到这个问题 我们的应用程序允许技术用户创建参数化原始SQL查询,以从数据库中提取数据,并将其下载到excel电子表格中 对于较小的数据集,这很好,但是,当文件大小开始接近10mb+时,我开始遇到这个问题 数据集可能有10万行或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;
}
/*
*
*查询是采用参数的原始sql查询(使用Mybatis)
*Criteria将我们替代的参数映射到查询中
*
*/
JsonWithoutMapping的公共列表查询(最终字符串查询,最终映射标准映射){
SqlSession SqlSession=getSqlSessionInstance();
字符串sql=“”;
连接=空;
PreparedStatement pstmt=null;
ResultSet ResultSet=null;
试一试{
最终配置=getSqlSessionInstance().getConfiguration();
SqlSourceBuilder=新的SqlSourceBuilder(配置);
SqlSource src=builder.parse(查询,Map.class,null);
BoundSql BoundSql=src.getBoundSql(标准映射);
sql=boundSql.getSql();
List parameterMappings=boundSql.getParameterMappings();
connection=sqlSession.getConnection();
pstmt=connection.prepareStatement(sql,java.sql.ResultSet.TYPE_FORWARD_ONLY,java.sql.ResultSet.CONCUR_READ_ONLY);
//此函数将参数细分到preparedStatement查询中
buildParams(参数应用、标准地图、pstmt);
resultSet=pstmt.executeQuery();
//此函数中的while循环是开始挂起的地方
列表结果=getObjectFromResultSet(resultSet);
返回结果;
}捕获(例外e){
LOG.error(e.getMessage(),e);
LOG.error(ExceptionUtils.getStackTrace(e));
抛出新的非法状态异常(sql+“”+e.getMessage(),e);
}最后{
试一试{
connection.close();
pstmt.close();
resultSet.close();
}捕获(SQLE异常){
e、 printStackTrace();
}
sqlSession.close();
}
私有列表getEntitiesFromResultSet(ResultSet ResultSet)引发SQLException{
ArrayList entities=新的ArrayList(resultSet.getFetchSize());
int指数=0;
映射jsonObject;
while(resultSet.next()){
jsonObject=getEntityFromResultSet(resultSet);
添加(索引,jsonObject);
索引++;
}
resultSet.close();
返回实体;
}
私有列表getObjectFromResultSet(ResultSet ResultSet)引发SQLException{
ArrayList entities=新的ArrayList(resultSet.getFetchSize());
int指数=0;
映射jsonObject;
while(resultSet.next()){
jsonObject=getEntityFromResultSet(resultSet);
添加(索引,jsonObject);
索引++;
}
resultSet.close();
返回实体;
}
DB是oracle在这种设计中,如果查询结果返回大量数据,您将不可避免地在某个时候耗尽内存,因为您正在将整个结果集加载到内存中。相反,您可以简单地声明getXXXFromResultSet API在数据量方面有一个阈值。对于每一行,您都计算其大小并决定是否可以将其添加到JSON文档中。如果已超过阈值,则停止并关闭结果集(这将取消服务器上的执行)。另一个选项可能涉及对结果进行流式处理,但这更为复杂。在这种设计中,如果查询结果返回大量数据,您将不可避免地在某个时候耗尽内存,因为您正在将整个结果集加载到内存中。相反,您可以简单地声明getXXXFromResultSet API中有一个阈值数据量。对于每一行,计算其大小并决定是否可以将其添加到JSON文档中。如果超过阈值,则停止并关闭结果集(这将取消服务器上的执行)。另一个选项将涉及流式处理结果,但这更复杂。一次性获取和处理DB表中的所有行是一个坏主意。您需要实现分页的一般想法,即一次读取和处理一页(n=页面大小行) 您的页面大小应该足够优化,这样您就不会有太多的数据库点击,同时也不会在内存中有太多的记录 Spring批处理API实现了这个概念 请参阅本文,了解有关JDBC分页的更多想法 除此之外,您不应该不断增加映射结果的大小。您需要循环刷新此映射
希望这能有所帮助!!一次性获取和处理数据库表中的所有行是个坏主意。您需要实现分页的一般思想,即一次读取和处理一页(n=页面大小行) 您的页面大小应该足够优化,这样您就不会有太多的数据库点击,同时也不会在内存中有太多的记录 Spring批处理API实现了这个概念 请参阅本文,了解有关JDBC分页的更多想法 除此之外,您不应该不断增加映射结果的大小。您需要循环刷新此映射 希望这有帮助!!看起来像是传统的“rea”