简体   繁体   English

大尺寸的 HashSet 抛出 StackOverflow 错误

[英]Large size of HashSet throwing StackOverflow Error

I have 81K records of Long object and I am trying to store it in HashSet.我有 81K 长 object 记录,我正在尝试将其存储在 HashSet 中。 My code snippet looks like this:我的代码片段如下所示:

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

While compiling this is giving me StackOverflow Error .编译时给了我StackOverflow Error I am not understanding why only 81K records are being problem here?我不明白为什么这里只有 81K 记录有问题? Solutions are appreciated.解决方案表示赞赏。

Java version. 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)

Stack Trace:堆栈跟踪:

[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)

Line 220 of Type : 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 of data into your source code. 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数据到您的源代码。 The maximum size of the code for a method -- which is how Java initializes this behind the scenes -- is 64KB;方法代码的最大大小——这是 Java 在幕后初始化它的方式——是 64KB; storing 81K long constants alone is 10 times more than this limit.单独存储 81K long常量是这个限制的 10 倍。

You can certainly store this data into a HashSet and the like, but you must load it at runtime from a file.您当然可以将此数据存储到HashSet等中,但您必须在运行时从文件中加载它。

The HashSet is irrelevant here. HashSet在这里无关紧要。 The problematic part is the varargs invocation of Arrays.asList with 81,000 elements.有问题的部分是具有 81,000 个元素的Arrays.asList可变参数调用。

To reproduce the issue, we can use the following code要重现该问题,我们可以使用以下代码

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()));
  }
}

With OpenJDK 8 , it produces the使用OpenJDK 8 ,它产生

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

On recent JDKs, eg JDK 12 , it produces在最近的 JDK 上,例如JDK 12 ,它产生

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

showing that even when the compiler bug has been fixed, such code can't get compiled.表明即使编译器错误已修复,此类代码也无法编译。

Such amount of data should be included as embedded resource which you read in once at startup.此类数据量应作为嵌入式资源包含在内,您在启动时读取一次。

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

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