Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何克服大文件写入过程中的OutOfMemory错误_Java_Jdbc_Out Of Memory - Fatal编程技术网

Java 如何克服大文件写入过程中的OutOfMemory错误

Java 如何克服大文件写入过程中的OutOfMemory错误,java,jdbc,out-of-memory,Java,Jdbc,Out Of Memory,我正在用java编写一个完整的数据库提取程序。数据库是Oracle,它是巨大的。有些表格有2.6亿条记录。该程序应以特定格式为每个表创建一个文件,因此不能使用Oracle datapump等。此外,一些公司的安全策略不允许编写PL/SQL过程来在DB server上为此需求创建文件。我必须使用Java和JDBC 我面临的问题是,由于表中某些部分的文件很大(~30GB),即使使用20GB的Java堆,我几乎每次都会耗尽内存。在创建文件的过程中,当文件大小超过堆大小时,即使使用最激进的GC策略之一,

我正在用java编写一个完整的数据库提取程序。数据库是Oracle,它是巨大的。有些表格有2.6亿条记录。该程序应以特定格式为每个表创建一个文件,因此不能使用Oracle datapump等。此外,一些公司的安全策略不允许编写PL/SQL过程来在DB server上为此需求创建文件。我必须使用Java和JDBC

我面临的问题是,由于表中某些部分的文件很大(~30GB),即使使用20GB的Java堆,我几乎每次都会耗尽内存。在创建文件的过程中,当文件大小超过堆大小时,即使使用最激进的GC策略之一,该过程似乎也会挂起。例如,如果文件大小大于20GB,而堆大小为20GB,则一旦堆利用率达到最大堆大小,其每分钟写入2MB的速度就会减慢,以这种速度,将需要数月时间才能完全提取

我正在寻找克服这个问题的方法。任何帮助都将不胜感激

以下是我的系统配置的一些详细信息: Java-JDK1.6.0_14

系统配置-运行在4 X Intel Xeon E7450(6核)上的RH Enterprise Linux(2.6.18)@2.39GH

内存-32GB

oracle11g数据库

文件wirting部分代码如下所示:

private void runQuery(Connection conn, String query, String filePath,
        String fileName) throws SQLException, Exception {
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
        stmt = conn.prepareStatement(query,
                ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_READ_ONLY);
        stmt.setFetchSize(maxRecBeforWrite);
        rs = stmt.executeQuery();
        // Write query result to file
        writeDataToFile(rs, filePath + "/" + fileName, getRecordCount(
                query, conn));
    } catch (SQLException sqle) {
        sqle.printStackTrace();
    } finally {
        try {
            rs.close();
            stmt.close();
        } catch (SQLException ex) {
            throw ex;
        }
    }
}

private void writeDataToFile(ResultSet rs, String tempFile, String cnt)
        throws SQLException, Exception {
    FileOutputStream fileOut = null;
    int maxLength = 0;
    try {
        fileOut = new FileOutputStream(tempFile, true);
        FileChannel fcOut = fileOut.getChannel();

        List<TableMetaData> metaList = getMetaData(rs);
        maxLength = getMaxRecordLength(metaList);
        // Write Header
        writeHeaderRec(fileOut, maxLength);
        while (rs.next()) {
            // Now iterate on metaList and fetch all the column values.
            writeData(rs, metaList, fcOut);
        }
        // Write trailer
        writeTrailerRec(fileOut, cnt, maxLength);
    } catch (FileNotFoundException fnfe) {
        fnfe.printStackTrace();
    } catch (IOException ioe) {
        ioe.printStackTrace();
    } finally {
        try {
            fileOut.close();
        } catch (IOException ioe) {
            fileOut = null;
            throw new Exception(ioe.getMessage());
        }
    }
}

private void writeData(ResultSet rs, List<TableMetaData> metaList,
        FileChannel fcOut) throws SQLException, IOException {
    StringBuilder rec = new StringBuilder();
    String lf = "\n";
    for (TableMetaData tabMeta : metaList) {
        rec.append(getFormattedString(rs, tabMeta));
    }
    rec.append(lf);
    ByteBuffer byteBuf = ByteBuffer.wrap(rec.toString()
            .getBytes("US-ASCII"));
    fcOut.write(byteBuf);
}

private String getFormattedString(ResultSet rs, TableMetaData tabMeta)
        throws SQLException, IOException {
    String colValue = null;
    // check if it is a CLOB column
    if (tabMeta.isCLOB()) {
        // Column is a CLOB, so fetch it and retrieve first clobLimit chars.
        colValue = String.format("%-" + tabMeta.getColumnSize() + "s",
                getCLOBString(rs, tabMeta));
    } else {
        colValue = String.format("%-" + tabMeta.getColumnSize() + "s", rs
                .getString(tabMeta.getColumnName()));
    }
    return colValue;
private void runQuery(连接连接、字符串查询、字符串文件路径、,
字符串文件名)引发SQLException,异常{
PreparedStatement stmt=null;
结果集rs=null;
试一试{
stmt=conn.prepareStatement(查询,
ResultSet.TYPE\u SCROLL\u不敏感,
结果集CONCUR_只读);
stmt.setFetchSize(maxRecBeforWrite);
rs=stmt.executeQuery();
//将查询结果写入文件
WriteDataofile(rs,文件路径+“/”+文件名,getRecordCount(
查询(康涅狄格州);
}捕获(SQLException sqle){
printStackTrace();
}最后{
试一试{
rs.close();
stmt.close();
}catch(SQLException-ex){
掷骰子;
}
}
}
私有void writedataofile(结果集rs、字符串tempFile、字符串cnt)
抛出SQLException,异常{
FileOutputStream fileOut=null;
int maxLength=0;
试一试{
fileOut=新的FileOutputStream(tempFile,true);
FileChannel fcOut=fileOut.getChannel();
列表metaList=getMetaData(rs);
maxLength=getMaxRecordLength(metaList);
//写头
writeHeaderRec(文件输出,最大长度);
while(rs.next()){
//现在迭代metaList并获取所有列值。
写入数据(rs、metaList、fcOut);
}
//写预告片
writeTrailerRec(文件输出、cnt、maxLength);
}捕获(FileNotFoundException fnfe){
fnfe.printStackTrace();
}捕获(ioe异常ioe){
ioe.printStackTrace();
}最后{
试一试{
fileOut.close();
}捕获(ioe异常ioe){
fileOut=null;
抛出新异常(ioe.getMessage());
}
}
}
私有void writeData(结果集rs、列表金属列表、,
FileChannel(fcOut)抛出SQLException、IOException{
StringBuilder rec=新的StringBuilder();
字符串lf=“\n”;
for(TableMetaData tabMeta:metaList){
rec.append(getFormattedString(rs,tabMeta));
}
记录附加(lf);
ByteBuffer byteBuf=ByteBuffer.wrap(rec.toString())
.getBytes(“US-ASCII”);
fcOut.write(byteBuf);
}
私有字符串getFormattedString(结果集rs,TableMetaData选项卡Meta)
抛出SQLException,IOException{
字符串colValue=null;
//检查它是否是CLOB列
if(tabMeta.isCLOB()){
//列是CLOB,所以获取它并检索第一个clobLimit字符。
colValue=String.format(“%-”+tabMeta.getColumnSize()+“s”,
getCLOBString(rs,tabMeta));
}否则{
colValue=String.format(“%-”+tabMeta.getColumnSize()+“s”,rs
.getString(tabMeta.getColumnName());
}
返回colValue;
}

编辑: 使用JPA将数据库表映射到类。

现在,使用Hibernate以一定的允许大小批量加载DB中的对象集合,并将其序列化为文件。

您的算法如下所示吗?这是假设DB行和文件中的行之间存在直接映射:

// open file for writing with buffered writer.
// execute JDBC statement
// iterate through result set
    // convert rs to file format
    // write to file
// close file
// close statement/rs/connection etc

尝试使用SpringJDBC模板来简化JDBC部分。

我相信这在默认的32MB java堆上是可能的。只需获取每一行,将数据保存到文件流中,完成后刷新并关闭。

这可能是由于您调用的方式,请参阅以了解类似的问题。您不需要可滚动性,
ResultSet
将是只读的,因此只需调用

stmt = conn.prepareStatement(query);

maxRecBeforWrite的值是多少


通过强制JDBC扫描整个结果中的记录长度,最大记录长度的查询可能会破坏setFetchSize?也许您可以延迟写入头并动态记录最大记录大小。

您没有提供任何代码,因此很难准确说出您做错了什么……如果您将记录流式传输到文件,您应该不会有问题,在将整个2GB表写入文件之前,没有理由将其保留在内存中。还要检查您的代码中如何使用字符串。没有任何代码,很难提出任何解决方案。是的,我们需要一些代码来审查。伪代码(如有必要)。@Amit,我可能会分离出有问题的代码,尝试在不写入文件的情况下遍历表,您是否仍在耗尽内存?您确定文件大小不会影响堆大小的使用吗。我正按照你说的去做。我正在一条记录一条记录地写。Spring JDBC如何帮助减少巨大的文件写入过程所导致的内存使用?使用
SimpleJdbcTemplate
w