【问题标题】:java mysql java.lang.OutOfMemoryError: Java heap spacejava mysql java.lang.OutOfMemoryError:Java堆空间
【发布时间】:2013-08-27 09:04:28
【问题描述】:

我开发了一个网络爬虫。 Web scraper 使用 6 个线程,每个线程打开一个网页,获取一篇文章的文本,然后(使用驱动程序)将文本的每个单词写入 mysql 数据库。

在程序执行期间,我得到一个 java mysql java.lang.OutOfMemoryError: Java heap space。我在Eclipse上安装了Memory Analyzer,发现问题是由mysql驱动连接引起的:当我运行这个程序时,5分钟后驱动占用的内存是6 MB,再过5分钟200MB,再过5分钟500Mb和然后我得到 java 错误堆空间。

我不明白为什么会这样。

这是我用于模型的代码(用于访问 mysql DB)

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class model {

    private Connection connect = null;

    public model(){
         try {

              Class.forName("com.mysql.jdbc.Driver");
              connect = DriverManager.getConnection("jdbc:mysql://localhost/system?user=keyword_tool&password=l0gripp0");

            } catch (Exception e) {
                System.out.println(e);
            }
    }

    public synchronized void insertCat(String parola, String categoria){

        try{
            PreparedStatement statement = connect.prepareStatement("insert into sostantivi (nome, categoria) values (?, ?)");
            statement.setString(1, parola);
            statement.setString(2, categoria);

            statement.executeUpdate();
            statement.close();

        } catch (Exception e){
            //System.out.println(e);
        }

    }

    public void closeDBConnection() {
        try {
            connect.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

每个线程只需调用方法 insertCat 并在数据库中插入一个带有类别的单词。

Eclipse 的 Memory Analyzer 插件说:

【问题讨论】:

  • 你怎么知道closeDBConnection() 被调用了?进行一些登录。由于您没有显示该代码,很可能是 model() 构造函数被重复调用,但由于您的代码中的某些缺陷,连接被泄露或未关闭。
  • 我把 closeDbConnection() 放在了抓取程序的末尾。我在主类中创建了一个模型,每个线程都使用该模型。在抓取了我需要的东西之后,我在那个单一的模型上调用了 closeDBConnection。有错吗?

标签: java mysql eclipse multithreading


【解决方案1】:

根据您的 cmets,您只创建了一个“模型”(这是一个糟糕的类名)并在 6 个线程中使用它。

这不是特别好的设计——它要么通过在单个数据库连接上同步而限制性能(当您可以使用每个线程一个时),或者遇到潜在的并发问题/错误。

我在你的堆转储中只看到一个com.mysql.jdbc.JDBC4Connection

这可能是由于误导性显示,或者(哪个理论符合您声称的“单一模型”方法)它充满了 PreparedStatements 或其他东西。

理论上,这些应该被缓存和重用——在实践中,你遇到了问题。可以尝试三个步骤:

  1. 更新 MySQL 驱动程序版本;
  2. 每隔 1000 条左右的语句关闭并重新打开连接;
  3. 为每个线程提供自己的连接,或使用连接池。

这看起来像是 Prepared Statement 缓存的某种问题。除非您可以在代码中发现 PreparedStmt 或 ResultSet 处理的其他错误——其前景并不明显——1) 和 2) 最有可能提供解决方案/或特定的解决方法。

【讨论】:

  • 那很好。我使用了修复号 2),我的程序现在摇滚!!! Kevin 的解决方案并不好,因为我的性能急剧下降(在我可以下载 6 页/秒之前,我可以下载 0.5 页/秒,因为每次写入操作的打开和关闭连接都需要时间)。谢谢
  • 没错。虽然 JDBC 驱动程序可能仍然存在错误。如果您尝试不同的版本,它可能会得到修复。
【解决方案2】:

如果您创建新模型而不破坏它,则会创建一个新连接,您的地图中有 2000000 个模型,因此您将有 2000000 个连接。

您应该将所有连接代码提取到连接管理器池中,并自己管理连接。

【讨论】:

    【解决方案3】:

    代码永远不会关闭与数据库的连接。

    尝试在insertCat 方法中创建/关闭连接。应尽快获得和释放连接。连接应该只在执行持久性操作所需的时间内打开。

    public class model {
    
        public synchronized void insertCat(String parola, String categoria){
            Connection connect = null;
            try{
                Class.forName("com.mysql.jdbc.Driver");
                connect = DriverManager.getConnection("jdbc:mysql://localhost/system?user=keyword_tool&password=l0gripp0");
                PreparedStatement statement = connect.prepareStatement("insert into sostantivi (nome, categoria) values (?, ?)");
                statement.setString(1, parola);
                statement.setString(2, categoria);
    
                statement.executeUpdate();
                statement.close();
    
            } catch (Exception e){
                //System.out.println(e);
            }finally{
               if(connect != null){
                   try {
                      connect.close();
                   } catch (Exception e) {
                      System.out.println(e);
                   }
               }
            }
    
        }
    
    }
    

    【讨论】:

    • 你在 connect.close() 上引入了一个潜在的 NPE,你必须检查 null。此外,关闭语句和结果集也是一种很好的做法,其顺序与获取它们的顺序相反,即使它通常是在关闭连接时自动完成的。忽略异常也不是一个好主意。
    • 一个明显的假设和常见错误跳转到,但显然不被“Eclipse Memory Analyzer”或“Accumulated Objects”堆报告支持——这些仅指示一个 JDBC4Connection 实例。他还在 cmets 中声明他只创建了一个具有一个连接的“模型”并分享了这个..
    • @ThomasW 我不认为凯文声称有多个连接实例,他只是说它没有关闭。在关闭连接时,从它获得的其他资源也应该被释放,所以我认为它应该解决语句堆积的问题。
    • 编辑后仍然不正确——尽快返回连接只有在它们被池化时才能提高性能。在没有池的情况下关闭和重新打开会在每条语句上产生连接设置开销(非常昂贵)。
    • @eis -- Kevin 相当不错,但这次他看到的是“明显”问题而不是“实际”问题。他所声称的就是他所声称的——不是关于关闭每个 stmt 的连接的好处的一般泡沫(这是不正确的)。开发人员需要解决“实际”问题,这就是为什么我的回答建议尝试关闭和重新打开 - 但仅每 1000 条语句。我还推荐 2 个其他可能的领域进行调查。我的解决方案旨在解决/解决精确问题,而不会造成不必要的性能损失。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 2015-05-14
    相关资源
    最近更新 更多