繁体   English   中英

大尺寸的 HashSet 抛出 StackOverflow 错误

[英]Large size of HashSet throwing StackOverflow Error

我有 81K 长 object 记录,我正在尝试将其存储在 HashSet 中。 我的代码片段如下所示:

private static HashSet<Long> hashSet = new HashSet<>(Arrays.asList(*81K records*));

编译时给了我StackOverflow Error 我不明白为什么这里只有 81K 记录有问题? 解决方案表示赞赏。

Java 版本。

openjdk version "1.8.0_322"
OpenJDK Runtime Environment Corretto-8.322.06.1 (build 1.8.0_322-b06)
OpenJDK 64-Bit Server VM Corretto-8.322.06.1 (build 25.322-b06, mixed mode)

堆栈跟踪:

[javac] 
    [javac] 
    [javac] The system is out of resources.
    [javac] Consult the following stack trace for details.
    [javac] java.lang.StackOverflowError
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)
    [javac]     at com.sun.tools.javac.code.Type.map(Type.java:220)

Type的第 220 行:

 208     /**
 209      * Return the least specific subtype of t that starts with symbol
 210      * sym.  If none exists, return null.  The least specific subtype
 211      * is determined as follows:
 212      *
 213      * <p>If there is exactly one parameterized instance of sym that is a
 214      * subtype of t, that parameterized instance is returned.<br>
 215      * Otherwise, if the plain type or raw type `sym' is a subtype of
 216      * type t, the type `sym' itself is returned.  Otherwise, null is
 217      * returned.
 218      */
 219     public Type asSub(Type t, Symbol sym) {
 220         return asSub.visit(t, sym);
 221     }
 222     // where
 223         private final SimpleVisitor<Type,Symbol> asSub = new SimpleVisitor<Type,Symbol>() {

The specific issue is that Java type inference cannot deal with such a long constant -- as reflected by the stack overflow you got in the Java compiler itself -- but it's also the case that Java bytecode format does not allow you to put such large amounts数据到您的源代码。 方法代码的最大大小——这是 Java 在幕后初始化它的方式——是 64KB; 单独存储 81K long常量是这个限制的 10 倍。

您当然可以将此数据存储到HashSet等中,但您必须在运行时从文件中加载它。

HashSet在这里无关紧要。 有问题的部分是具有 81,000 个元素的Arrays.asList可变参数调用。

要重现该问题,我们可以使用以下代码

class Tmp {
  static final String ARGUMENTS = "<<INSERT ARGUMENTS HERE>>";

  static final List<String> TEMPLATE = Arrays.asList(
      "import java.util.Arrays;",
      "import java.util.List;",
      "",
      "class Tmp {",
      "  static final List<Integer> L = Arrays.asList(",
           ARGUMENTS,
      "  );",
      "}");

  public static void main(String[] args) throws IOException {
    Path p = Files.createTempFile("Test", ".java");
    Files.write(p, () -> TEMPLATE.stream()
        .flatMap(line -> line.equals(ARGUMENTS)? varargsArgument(): Stream.of(line))
        .iterator());
    JavaCompiler c = ToolProvider.getSystemJavaCompiler();
    c.run(System.in, System.out, System.err, p.toString());
  }

  static Stream<CharSequence> varargsArgument() {
    return IntStream.range(0, 8100).mapToObj(i -> IntStream.range(0, 10)
            .mapToObj(j -> i * 10 + j + (i < 8099 || j < 9? ", ": ""))
            .collect(Collectors.joining()));
  }
}

使用OpenJDK 8 ,它产生

java.lang.StackOverflowError
    at com.sun.tools.javac.code.Type.map(Type.java:220)
   …

在最近的 JDK 上,例如JDK 12 ,它产生

/tmp/Test14992292170362927520.java:6: error: code too large
  static final List<Integer> L = Arrays.asList(
                             ^

表明即使编译器错误已修复,此类代码也无法编译。

此类数据量应作为嵌入式资源包含在内,您在启动时读取一次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM