问题描述
我有一堂课,看起来像这样:
public enum Animal {
BEAR,SHEEP,LION;
private static final Map<String,Animal> MAPPING = new ImmutableMap.Builder<String,Animal>()
.put("BLACK_BEAR",BEAR)
.put("WHITE_BEAR",BEAR)
...
.put("African Lion",LION)
.build()
}
大约1500个地图条目。
我尝试构建我的应用程序,但出现以下错误:
[javac] An annotation processor threw an uncaught exception.
[javac] Consult the following stack trace for details.
[javac] java.lang.StackOverflowError
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
我尝试将地图的尺寸减小到15ish,并且可以正常显示。我们可以编码静态不可变地图的大小是否有限制?我在文档中没有看到任何限制。问题可能是什么?
解决方法
它不是ImmutableBuilder;这是事实,因为您的源文件现在是一堆巨大的AST节点,以至于lombok的处理最终占用了太多内存,我想,鉴于javac可以处理它,从技术上讲,这在lombok中是一个错误。换句话说,编译器无法将您的Java文件转换为类文件,但是如果可以的话,它将可以正常工作*。
通常,我会将任何试图将源中的大量数据定义为代码气味的源文件视为
。我建议您获取所有数据并将其放在文本文件中。
将此文本文件与源文件一起放置;如果您使用maven /或多或少的标准源目录结构,则必须说:
src/main/java/com/foo/yourpkg/Animal.java
src/main/resources/com/foo/yourpkg/AnimalData.txt
这将自动导致txt文件与类文件位于同一位置,即使是在jar中也是如此。然后,从您的动物文件中读取它:
public enum Animal {
private static final Map<String,Animal> MAPPING;
static {
try {
Map<String,String> example = new HashMap<String,String>();
try (var in = Animal.class.getResourceAsStream("AnimalData.txt")) {
BufferedReader br = new BufferedReader(new InputStreamReader(
in,StandardCharsets.UTF_8));
for (String line = br.readLine(); line != null; line = br.readLine()) {
String[] p = line.split("\t",2);
map.put(p[0],p[1]);
}
}
MAPPING = Collections.unmodifiableMap(map);
} catch (IOException e) {
throw new InternalError("data corrupted");
}
}
}
此假设示例要求您将制表符分隔的字符串对放入文本文件中。您可以使用此示例,并根据需要进行调整。
免责声明:我是lombok的核心贡献者,但是这比我想象的要低得多,在您遇到内存不足的问题之前,我们有很多错误报告需要深入研究。尝试在源代码中创建一个1500大小的不可变地图构建器。
*)可能;类文件也有限制,如果您删除lombok或添加内存,则编译器可能会在以后的阶段中仍然最终拒绝编译该代码,因为它会超出初始化的64k字节码限制。
,我同意上面的@rzwitserloot,但是您可以尝试以下操作:
- 只需在运行构建的jvm中添加
-Xss4m
(或更高版本)来增加堆栈大小。 - edit 由于枚举本身很小,您可以这样做:
package org.example;
import java.util.HashMap;
import java.util.Map;
public enum Animal {
BEAR("white bear","black bear","brown bear"),DOG("mongrel","cross"),CAT("blah3");
private final String[] blah1;
private static final Map<String,Animal> MAPPING = new HashMap<>();
static {
for (Animal animal: Animal.values()) {
for (String tag : animal.blah1) {
MAPPING.put(tag,animal);
}
}
}
Animal(String... blah1) {
this.blah1 = blah1;
}
public static void main(String... s) {
String animal = "black bear";
System.out.println(" A " + animal + " is a " + MAPPING.get(animal));
}
}