【问题标题】:Memory leak in simple code? Can't find an answer anywhere简单代码中的内存泄漏?在任何地方都找不到答案
【发布时间】:2019-08-19 10:54:18
【问题描述】:

此代码从大约 130 MB 开始逐渐消耗内存(由于依赖关系),并在我不得不杀死它并重新启动它之前不断攀升至 800+ MB(因为服务器内存不足)。

它与 OpenJDK 11 一起运行。我在 Java 8 服务器上运行此代码的旧版本,其内存使用量保持稳定且永不增加。所以我不确定它是否与新的JDK有关?

我在这里修改了很多代码以确保它尽可能简单 - 但仍然存在问题。

基本要点 - 它是否每隔几秒钟查询一次数据库以获取待处理的发票。但是,没有待处理的发票(日志也证明了这一点),因此它永远不会进入复杂的代码位置,只会每隔几秒钟重复一次。

public static void main(String[] args) {
    ...

    final int interval = Constants.INTERVAL;
    QuickBooksInvoices qbInvoices = new QuickBooksInvoices(filename);
    qbInvoices.testConnection();

    log.log(Level.INFO, "Checking invoices with an interval of " + interval + " seconds...");

    while (isRunning == true) {
        qbInvoices.process();

        try {
            Thread.sleep(interval * 1000);
        } catch (InterruptedException e) {

        }
    }
}

public void process() {
    errorBuffer.clear();  // These are array lists
    successBuffer.clear(); // These are array lists

    try (Connection conn = DriverManager.getConnection(dbURI, dbUser, dbPassword)) {
        ArrayList<com.xxx.quickbooks.model.wdg.Invoice> a = getInvoices(conn);
        OAuthToken token = null;

        if (a.size() > 0) {
            // Never gets here - no results
        }

        for (com.xxx.quickbooks.model.wdg.Invoice invoice : a) {
            // Never gets here - no results
        }
    } catch (Exception e) {        
        writeLog(Level.ERROR, ExceptionUtils.getStackTrace(e));
    }

}

private ArrayList<com.xxx.quickbooks.model.wdg.Invoice> getInvoices(Connection conn) {
    ArrayList<com.xxx.quickbooks.model.wdg.Invoice> invoices = new ArrayList<com.xxx.quickbooks.model.wdg.Invoice>();  

    String sql = 
        "select " +
        "id," +
        "type," +
        "status," +
        "business_partner_id," +
        "invoice_number," +
        "total," +
        "nrc," +
        "szrc," +
        "trans_ts," +
        "warehouse_id," +
        "due_date," +
        "ref_number," +
        "payment_type " +
        "FROM dv_invoice " +
        "WHERE exported_ts is NULL AND exported_msg is NULL ; ";

    try (
        PreparedStatement stmt = conn.prepareStatement(sql);
        ResultSet rs = stmt.executeQuery();
    ) {
        while (rs.next()) {
            // Never gets here - no results
        }
    } catch (SQLException e) {
        writeLog(Level.ERROR, ExceptionUtils.getStackTrace(e));
    }
    return invoices;
}

【问题讨论】:

  • 你给Java多少内存(-Xmx选项)?
  • Profiler (YourKit, JProfiler) 或使用分析工具 (stackoverflow.com/questions/9154785/…) 以不同间隔进行堆转储。它实际上是在抛出 OOME,还是堆只是增长到允许的阈值,然后被 GC 丢弃?
  • 乍一看你永远不会关闭你的ResultSet rs,还要检查你的testConnection中是否没有任何东西可以在不关闭它们的情况下消耗资源
  • 内存增长不一定是问题,但无法回收它才是问题。您是否通过多个 GC 周期长期监控它?
  • 查明内存泄漏的标准过程是进行堆转储,并将其加载到 eclipse MAT 等分析工具中,以找出哪些对象被保留。然后,通常很容易找到有问题的代码。

标签: java


【解决方案1】:

驱动程序可能会产生内存泄漏。我在统一 SQL 数据库上遇到了这个问题。将驱动程序更新到最新版本。

See this in postgres

【讨论】:

  • 这是个好主意——它是一个 postgres 数据库。我们多年来一直使用旧驱动程序,而这台服务器上的数据库是不同的版本(Postgres 的最新版本)。我已经着手更新了驱动程序,看看是否会有所作为。
  • 就是这样!!!超级棒——你不知道我有多激动。这个我什至没有想到。但我猜旧的驱动程序对新的数据库版本有一些错误,这会导致泄漏。
  • @BrianCothran 我喜欢引用Tao of programming 中的一位睿智大师:您不知道错误,因为错误在您不知道的代码中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多