【问题标题】:Java heap space error at large files with string.split带有 string.split 的大文件中的 Java 堆空间错误
【发布时间】:2021-07-01 05:50:31
【问题描述】:

我在另一台机器上遇到堆空间错误,但它在我的机器上运行 我不能把握另一台机器的属性。 如何在不使用的情况下解决此问题 Scanner.java ?

string.split 的参数是否正确,用“”表示在空格后分割字符串以将字符串分割成碎片?

[文件:]

U 1 234.003 30 40 50 true
T 2 234.003 10 60 40 false
Z 3 17234.003 30 40 50 true
M 4 0.500 30 40 50 true

/* 1000000+ lines */
java.lang.OutOfMemoryError: Java heap space
    at java.base/java.util.Arrays.copyOfRange(Arrays.java:3821)
    at java.base/java.lang.StringLatin1.newString(StringLatin1.java:764)
    at java.base/java.lang.String.substring(String.java:1908)
    at java.base/java.lang.String.split(String.java:2326)
    at java.base/java.lang.String.split(String.java:2401)
    at project.FileR(Fimporter.java:99)
public static DataBase File(String filename) throws IOException {

   BufferedReader fs = new BufferedReader(new FileReader(filename),64 * 1024);

   String line;
   String[] wrds;
   String A; int hash; double B; int C; int D; boolean E; DataBase DB = new DataBase();

   while (true) {

        line = fs.readLine();
        if (line == null) {break;}
        wrds = line.split(" ");     /* this is line 99 in the error-message */

        hash  = Integer.parseInt(wrds[1]); 
        B     = Double.parseDouble(wrds[2]);
        C     = Integer.parseInt(wrds[3]); 
        D     = Integer.parseInt(wrds[4]); 
        E     = Boolean.parseBoolean(wrds[5]); 

        // hash is hashcode for all values B C D E in DataBase DB

        DB.listB.put(hash,B);
        DB.listC.put(hash,C);
        DB.listD.put(hash,D);
        DB.listE.put(hash,E);

   }

【问题讨论】:

  • 从您的代码来看,拆分似乎不是问题,但如果您在 DataBase 中保留大量数据,则可能会导致 OOM。您是否在机器上设置了不同的内存限制,例如通过-Xmx?您是否进行了堆转储并对其进行了分析,例如使用 Eclipse MAT?
  • 如果另一台机器上的 JVM 是 X86 32 位,它最多可能只有 1.4 到 1.6 GB 的堆空间。 X64 受限于使用的 RAM 数量。
  • @SamuelMarchant - 这是真的。但是,如果 OP 根本没有指定 -Xmx,那么 JVM 将具有 default 最大大小,通常比使用 -Xmx 成功请求的大小要小很多。 (此外,现在在野外找到 32 位机器变得越来越难......)
  • @Stephan C 没有多少通用 32 位,不,但如果你想在许多 X64 计算机上安装 Linux 或更旧的 MS Win 中的 X32 内核变体,那么它是 32(句号)。它也可能取决于另一台机器上的 JVM 类型 32 或 64,即使我认为这些天可能只是 X64 CPU,但这并不能阻止机器具有 x32(86 - i586) JVM,但是,很好指出如果它是 X86 jvm 二进制文件来增加程序命令行上的堆大小 * 类似点击 - 也很好。

标签: java file heap-memory space


【解决方案1】:

如何在不使用 Scanner.java 的情况下解决此问题?

Scanner 不是问题。

如果您使用此代码获得 OOME,最可能的根本原因如下:

DB.listB.put(hash,B);
DB.listC.put(hash,C);
DB.listD.put(hash,D);
DB.listE.put(hash,E);

您似乎将所有数据加载到 4 张地图中。 (您还没有向我们展示相关代码......但我在这里做出有根据的猜测。)

我的第二个猜测是您的输入文件非常大,在上述数据结构中保存它们所需的内存量对于“其他”机器的堆来说太大了。 p>

OOME 发生在String.split 调用中的事实并不表示split 本身存在问题。这只是俗话说的“压死骆驼的稻草”。问题的根本原因在于您在拆分数据后对数据的处理方式。


可能的解决方案/解决方法:

  1. 增加“其他”机器上的堆大小。如果您没有设置-Xmx-Xms 选项,JVM 将使用默认的最大堆大小...通常是物理内存的 1/4。

    阅读command documentation 以了解java 命令以了解-Xmx-Xms 的作用以及如何设置它们。

  2. 使用内存效率更高的数据结构:

    • 创建一个类来表示由 B、C、D、E 值组成的元组。然后将这 4 个映射替换为这些元组的映射。

    • 使用内存效率更高的Map 类型。

    • 考虑使用已排序的元组数组(包括哈希)并使用二进制搜索来查找它们。

  3. 重新设计您的算法,使其不需要同时使用内存中的所有数据;例如将输入拆分为较小的文件并分别处理它们。 (这可能是不可能的......)

【讨论】:

    【解决方案2】:

    如果我没记错的话,你可以在启动 jar 文件时分配更多的堆大小,例如:

    java -Xmx256M -jar MyApp.jar
    

    这意味着,您可以更改这些设置。

    但话又说回来,仅仅增加堆大小并不能解决这个问题,如果文件变大,oom 的机会就会增加。

    您可以考虑在处理前拆分大文件,例如只处理前 X 行,然后强制 GC 运行(通过清空)然后处理下一行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-16
      • 2013-06-28
      • 1970-01-01
      • 1970-01-01
      • 2013-11-27
      • 2012-10-18
      • 2015-12-15
      • 2018-03-30
      相关资源
      最近更新 更多