【问题标题】:Database connections and OutOfMemoryError: Java Heap Space数据库连接和 OutOfMemoryError:Java 堆空间
【发布时间】:2012-03-05 04:56:30
【问题描述】:

去年夏天,我制作了一个 Java 应用程序,它可以解析一些 PDF 文件并获取其中包含的信息以将它们存储在 SQLite 数据库中。

一切都很好,我每周左右都在向数据库中添加新文件,没有任何问题。

现在,我正在尝试提高我的应用程序的速度,我想看看如果我在一个新数据库中解析过去两年的所有文件,它会如何运行。那时我开始收到此错误:OutOfMemoryError: Java Heap Space。我之前没搞明白,因为我每周只解析大约 25 个新文件,但似乎一个接一个地解析 1000 多个文件要求更高。

我部分解决了这个问题:我确保在每次调用数据库后关闭我的连接并且错误消失了,但代价是巨大的。解析文件现在慢得令人难以忍受。至于我的 ResultSets 和 Statements / PreparedStatements,我已经在每次通话后关闭它们。

我想我不明白什么时候应该关闭我的连接,什么时候应该继续重复使用同一个连接。我认为由于自动提交已打开,它会在每个事务(选择、更新、插入等)之后提交,并且连接会释放它正在使用的额外内存。我可能错了,因为当我解析太多文件时,我最终得到了我提到的错误。

一个简单的解决方案是在每次调用 x 后关闭它,但我又不明白为什么,以后可能会遇到同样的错误。谁能解释我什么时候应该关闭我的连接(如果有的话,除非我完成了)?如果我只应该在完成后才这样做,那么有人可以解释我应该如何避免这个错误吗?

顺便说一句,我没有将其标记为 SQLite,因为当我尝试在我的在线 MySQL 数据库上运行我的程序时遇到了同样的错误。

编辑 正如 Deco 和 Mavrav 所指出的,问题可能不是我的 Connection。可能是文件的问题,所以我把我用来调用函数解析文件的代码一一贴出来:

public static void visitAllDirsAndFiles(File dir){
    if (dir.isDirectory()){
        String[] children = dir.list();

        for (int i = 0; i < children.length; i++){
            visitAllDirsAndFiles(new File(dir, children[i]));
        }
    }
    else{
        try{
            // System.out.println("File: " + dir);
            BowlingFilesReader.readFile(dir, playersDatabase);
        }
        catch (Exception exc){
            System.out.println("Other exception in file: " + dir);
        }
    }
}

因此,如果我使用目录调用该方法,它会使用我刚刚创建的 File 对象再次递归调用该函数。然后我的方法检测到它是一个文件并调用 BowlingFilesReader.readFile(dir, playerDatabase);

我认为方法完成后应该释放内存?

【问题讨论】:

  • 连接可能不是错误的确切原因。 PDF 文件的平均大小是多少?您是否一次将所有 1000 个文件加载到内存中?尝试一一加载。
  • 确保每次将 PDF 文件的引用存储在新数据库中时都会发布它。您可能一次将 1000 个文件全部加载到内存中,这会导致您的 OOME。您是否也使用分析器检查过它?
  • @mavrav 有关我如何加载文件的详细信息,请参阅已编辑的问题。平均可能是 300kb(从 175kb 到大约 1.2mb)
  • @Deco 有关我如何加载文件的详细信息,请参阅已编辑的问题。至于探查器,我不知道如何使用它,我尝试查看它,当我尝试一段时间后,我最终看到了数字和所有内容,但我不知道它们如何指示问题可能出在哪里。 ..
  • BowlingFilesReader.readFile(); 中,您是否使用.close() 方法关闭BufferedReader(或您正在使用的任何东西)? File 对象不应该是问题,因为这只是文件的路径而不是文件本身。如果有的话,那就是您用来读取未正确关闭的文件的文件

标签: java database connection database-connection


【解决方案1】:

您对开放结果集和连接的第一直觉很好,尽管可能不完全是原因。让我们先从您的数据库连接开始。

数据库

尝试使用数据库连接池库,例如 Apache Commons DBCP(BasicDataSource 是一个不错的起点):http://commons.apache.org/dbcp/ 您仍然需要关闭数据库对象,但这将使数据库前端的工作顺利进行。


JVM 内存

增加分配给 JVM 的内存大小。您可以通过在后面添加 -Xmx 和内存量来实现,例如:

  • -Xmx64m
  • -Xmx512m

不过,请注意您的数字,向 JVM 投入更多内存不会修复内存泄漏。您可以使用 JConsole 或 JVisualVM(包含在您的 JDK 的 bin/ 文件夹中)之类的东西来观察您正在使用多少内存。

线程

假设您为解析这些记录而执行的操作是线程化的,您可以通过线程化来提高操作速度。但可能需要更多信息才能回答这个问题。

希望这会有所帮助。

【讨论】:

  • 这都是很好的建议,但我不明白它如何帮助我解决内存泄漏问题?我将研究所有这些以帮助我提高应用程序的速度,但就像我刚才所说的,即使速度更快,内存泄漏仍然存在,对吧?我知道增加 JVM 内存会有所帮助,但如果它适用于 200 个文件而不适用于 1000 个(大概数字),那么在某个地方仍然存在问题。除非我在你的回答中遗漏了什么?
  • @abehrens - 好建议! Adam Smith:我肯定会尽快启动 JConsole(或您的 JDK 的等价物):java.sun.com/developer/technicalArticles/J2SE/monitoring
  • @paulsm4 我错过了那部分,我将尝试看看它是如何工作的
【解决方案2】:

正如垃圾收集所发生的那样,我认为内存不会立即被后续进程和线程重新收集。所以我们不能完全把鸡蛋放在那个篮子里。首先将所有文件放在一个目录中,而不是放在子目录中父目录。然后像这样迭代一个一个地加载文件

File f = null;
 for (int i = 0; i < children.length; i++){      
     f = new File(dir, children[i]);     
     BowlingFilesReader.readFile(f, playersDatabase); 
     f = null;
} 

所以我们使引用无效,以便文件对象被释放并在随后的 GC 中被拾取。并通过增加编号来检查限制测试它。的文件以 100、200 ..... 开头,然后我们将知道 OME 在什么时候被抛出。 希望这可以帮助。

【讨论】:

    猜你喜欢
    • 2013-01-24
    • 2019-03-06
    • 2015-08-25
    • 2021-02-13
    • 2012-10-09
    • 2020-09-14
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    相关资源
    最近更新 更多