【问题标题】:JDBC: OutOfMemoryError when running the `insert` query on MySQLJDBC:在 MySQL 上运行“插入”查询时出现 OutOfMemoryError
【发布时间】:2014-04-25 19:04:15
【问题描述】:

请看下面的代码

KeyWordTableCreator.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class KeyWordTableCreator 
{
    public KeyWordTableCreator(String str)
    {
        File file = new File(str);

        try
        {
            readAndUpdate(file);
        }
        catch(IOException io)
        {
            io.printStackTrace();
        }
    }

    public void readAndUpdate(File file) throws IOException
    {
        DatabaseConnector db = new DatabaseHandler();
        db.makeConnection();

        BufferedReader br = new BufferedReader(new FileReader(file));
        String str = "";
        int index=0;

        label1:
        while((str=br.readLine())!=null)
        {
            label2:
            for(int i=0;i<str.length();i=i+3)
            {

                String word = str.substring(i, i+3);
                //  System.out.println(word);
                int insert = db.insert(index, word);

                if(insert<0)
                {
                    System.out.println("Operation Broke");
                    break label1;
                }

            }


            System.out.println("Index Updated: "+index);
            //System.out.println(String.valueOf("Total words in this run: "+tempCounter));
            index++;

            if((index%100)==0)
            {
                System.gc();
            }

        }

        db.closeConnection();
        System.out.println("Completed");
    }
}

DatabaseConnector.java

public interface DatabaseConnector {

    public void makeConnection();
    public void closeConnection();
    public int insert(int index, String hashWord);

}

DatabaseHandler.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;



    public class DatabaseHandler implements DatabaseConnector
    {
        private Connection con;

        @Override
        public void makeConnection() 
        {
           try
            {
                 Class.forName("com.mysql.jdbc.Driver");

                 con = DriverManager.getConnection("jdbc:mysql://localhost:3306/xxx","xxx","");

                 //System.out.println("Connection Established");
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }

        @Override
        public void closeConnection() 
        {
            try
            {
                con.close();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }

        @Override
        public int insert(int index, String hashWord)
        {
            String str="";
            int result=0;
            int returnResult=0;

            try
            {
                con.setAutoCommit(false);
                PreparedStatement ps = con.prepareStatement("insert into Key_Word (indexVal,hashed_word) values(?,?)");
                ps.setInt(1, index);
                ps.setString(2, hashWord);

                result = ps.executeUpdate();
                con.commit();

                if(result>0)
                {
                    returnResult = 1;
                }
                else
                {
                    returnResult = -1;
                    System.out.println( index+" failed to update");
                }


            }
            catch(SQLException s)
            {
                returnResult = -1;
                s.printStackTrace();
                try {
                    con.rollback();
                    System.out.println(index+" Exception Occured. Update Failed");

                } catch (SQLException ex) {
                    ex.printStackTrace();
                    str = index+" Exception Occured. Update Failed. Rollback Failed";
                }
            }

            return returnResult;
        }

    }

数据库insert() 方法在KeyWordCreator 内部运行。基本上,在其循环内,数据库insert() 方法将被调用1.5 亿次。但是,这是在运行一段时间后给我OutOfMemory Exception

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at com.mysql.jdbc.MysqlIO.unpackField(MysqlIO.java:766)
        at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:431)
        at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3245)

        at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2413)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2836)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2828)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2777)
        at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1651)
        at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3966)
        at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3943)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java
:2399)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java
:2366)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java
:2350)
        at xxx.DatabaseHandler.insert(DatabaseHandler.java:64)
        at xxx.KeyWordTableCreator.readAndUpdate(KeyWordTableCreator.ja
va:51)
        at xxx.KeyWordTableCreator.<init>(KeyWordTableCreator.java:25)
        at xxx.Main.main(Main.java:26)

C:\Users\Dev\Documents\xxx\Key-Word Table Creator\dist>

我该如何解决这个问题?

PS:我已使用最大内存 (java -jar -Xmx1g) 运行此程序,但这不起作用。

【问题讨论】:

  • 我认为如此大量的inserts应该在事务的帮助下分批完成
  • 这可以帮助你...serverfault.com/questions/3952/…
  • 为什么要在 insert() 方法中创建准备好的语句?您正在无数次地创建/销毁该语句,这完全违背了准备好的语句的目的。在方法外创建一次并将其传入,然后只需更新参数值。
  • @OldProgrammer:好的,我刚刚把它变成了全球性的。

标签: java mysql sql jdbc sql-insert


【解决方案1】:

在你的 insert() 方法的实现中,一定要调用

ps.close();
result.close();

在你离开方法之前。否则,所有这些语句和结果集对象都会在内存中累积,直到您的代码中断。

或者,您可以在每个 insert() 中重用 PreparedStatement 对象。这不应该是防止 OutOfMemoryError 所必需的,但它可能会为您带来一些执行效率。有关这方面的建议,请参阅Reusing a PreparedStatement multiple times

【讨论】:

  • 我已将 setAutoCommit() 设置为 false。所以即使我使用批处理来完成这项工作,我也必须使用commit() 方法对吗?
  • 是的。在关闭连接之前,您只需要调用一次 conn.commit()。这将比每次插入后自动提交更有效。如果您实施批处理,自动提交的效率损失较小,但您可能仍希望确保您可以从中断的进程中正确恢复。如果您的某些批次已提交而其他批次未提交,则恢复可能会更加困难。
猜你喜欢
  • 1970-01-01
  • 2011-01-14
  • 2012-11-23
  • 2017-04-11
  • 1970-01-01
  • 1970-01-01
  • 2020-07-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多