首页 > 解决方案 > 为什么“while(Scanner.hasNext())”会在 java 中导致 OutOfMemoryError?

问题描述

import java.util.*;
import java.io.*;
public class Main {
    public static void problem1 () {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int n = scanner.nextInt();
            int[][] nums = new int[n][2];
            for (int i = 0; i < n; i++) {
                nums[i][0] = scanner.nextInt();
                nums[i][1] = scanner.nextInt();
            }
            Arrays.sort(nums, (a, b) -> {
                return a[0] - b[0];
            });
            int[] dp = new int[n];
            Arrays.fill(dp, 1);
            int res = 1;
            for (int i = 1; i < n; i++) {
                for (int j = 0; j < i; j++) {
                    if (nums[i][1] >= nums[j][1]) {
                        dp[i] = Math.max(dp[i], dp[j] + 1);
                    }
                }
                if (dp[i] > res)
                    res = dp[i];
            }
            System.out.println(res);
        }
    }

    public static void main(String[] args) throws IOException {
        problem1();
    }
}

在此处输入链接描述

在对上述代码进行编码时,我发现while(scanner.hasNext())会导致"OutOfMemoryError: Java heap space"输入数据超过1000000;并且这个bug可以通过去掉while循环来解决;但在我有限的 JVM 经验中,我不知道为什么;有任何想法吗?

标签: javajvm

解决方案


我们确实需要查看输入以确定失败的原因。但是,如果您在 OOME 中获得了 OOME hasNext(),那么正在发生的事情是您的应用程序的输入包含一个非常长的令牌。

hasNext()调用将从当前位置对输入流进行预读,直到遇到指示下一个标记结束的字符(或 EOF)。预先读取的字符需要在内存中缓冲。OOME 意味着您在缓冲字符时已设法填满堆。

一个可能的“快速而肮脏”的解决方法是使堆大小足够大以缓冲整个输入。 但我认为这不会奏效。

为什么?

假设您设法缓冲了一个大得惊人的令牌,以便它hasNext()可以返回true。您要做的下一件事是调用nextInt()以读取n. 但这很可能会失败,因为令牌(由 找到hasNext())不是数字,或者数字太大而无法作为int. 所以nextInt()会抛出异常。

解决这个问题的真正方法是弄清楚这个可怕的令牌到底是什么。这需要查看您的应用程序正在读取的输入。


并且这个bug可以通过去掉while循环来解决;

嗯。

我认为这意味着如果您删除外循环,OOME 就会消失。你实际上并没有解决问题。现在您的代码将只处理一个数据集。


推荐阅读