在我工作的地方,过去每个文件的行数超过一百万行。尽管服务器内存超过 10GB,其中 JVM 内存为 8GB,但有时服务器会挂起片刻并阻塞其他任务。

我分析了代码,发现文件读取时内存使用量频繁增加千兆字节(1GB 到 3GB),然后突然恢复正常。看来这种频繁的高内存和低内存使用挂起了我的服务器。当然,这是由于垃圾收集造成的。

我应该使用哪个 API 来读取文件以获得更好的性能?

现在我正在使用 BufferedReader(new FileReader(...)) 读取这些 CSV 文件。

过程 :我如何读取该文件?

  1. 我逐行读取文件。
  2. 每行都有几列。根据我相应地解析它们的类型(双精度型成本列、int 型访问列、String 型关键字列等)。
  3. 我将符合条件的内容(访问 > 0)推送到 HashMap 中,并最终在任务结束时清除该 Map

更新

我读取 30 或 31 个文件(一个月的数据)并将符合条件的文件存储在地图中。后来这张地图被用来在不同的表中找到一些罪魁祸首。因此,读取是必须的,存储数据也是必须的。虽然我现在已经将 HashMap 部分切换到 BerkeleyDB,但是读取文件时的问题是相同的甚至更糟。

有帮助吗?

解决方案

BufferedReader 是用于此目的的两个最佳 API 之一。如果您确实在文件读取方面遇到困难,另一种选择可能是使用 蔚来 对文件进行内存映射,然后直接从内存中读取内容。

但你的问题不在于读者。您的问题是,每次读取操作都会创建一堆新对象,很可能是您在读取后立即执行的操作。

您应该考虑清理输入处理,着眼于减少创建的对象的数量和/或大小,或者在不再需要时更快地删除对象。是否可以一次处理一行或一大块文件,而不是将整个文件吸入内存进行处理?

另一种可能性是摆弄垃圾收集。你有两种机制:

  • 每隔一段时间显式调用一次垃圾收集器,例如每 10 秒或每 1000 个输入行或其他。这将增加 GC 完成的工作量,但每次 GC 花费的时间会更少,您的内存不会膨胀太多,因此希望对服务器其余部分的影响较小。

  • 摆弄 JVM 的垃圾收集器选项。这些在 JVM 之间有所不同,但是 java -X 应该给你一些提示。

更新: 最有前途的方法:

您真的需要一次性将整个数据集放入内存中进行处理吗?

其他提示

  

我分析了代码并发现了   文件读取内存使用量上升   频繁的千兆字节(1GB到3GB)和   然后突然恢复正常。它   似乎这种频繁的高低   内存使用挂起我的服务器。的   当然这是由于垃圾   集合。

使用 BufferedReader(new FileReader(...))不会导致这种情况。

我怀疑问题是您正在将行/行读入数组或列表,处理它们然后丢弃数组/列表。这将导致内存使用量增加然后再次减少。如果是这种情况,您可以通过在读取时处理每一行/每行来减少内存使用量。

编辑:我们同意问题是关于用于表示内存中文件内容的空间。大内存哈希表的替代方案是返回旧的“排序合并”。计算机内存以千字节为单位时使用的方法。 (我假设处理由一个步骤决定,你用键K进行查找以获得相关的行R.)

  1. 如有必要,预处理每个输入文件,以便可以在密钥K上对其进行排序。

  2. 使用高效的文件排序实用程序将所有输入文件按顺序排序到K.您希望使用将使用经典合并排序算法的实用程序。这将 将每个文件拆分为可以在内存中排序的较小块,对块进行排序,将它们写入临时文件,然后合并已排序的临时文件。 UNIX / Linux sort 实用程序是一个不错的选择。

  3. 并行读取已排序的文件,从所有文件中读取与每个键值相关的所有行,处理它们,然后单步执行下一个键值。

  4. 实际上,我对使用BerkeleyDB没有帮助感到有些惊讶。但是,如果分析告诉您构建数据库的时间很长,那么在构建数据库之前,您可以通过将输入文件(如上所述!)排序为升序键来加快速度。 (当创建基于文件的大型索引时,如果按键顺序添加条目,则性能会更好。)

尝试使用以下vm选项来调整gc(并执行一些gc打印):

-verbose:gc -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top