包含2000 + 1枚举常量的枚举类是否达到了限制?

以下代码在main (map==null)出现NullPointerException失败。 只有在我定义2001或更多Enum常量时才会出现问题,2000工作正常。

为什么不执行静态代码块?

我们是否达到编译器的任何静默限制(没有警告,没有错误)或JVM?

编译后的类文件超过172KB,

 import java.util.HashMap; public enum EnumTest { E(1),E(2),...,E(2001); private static HashMap map = new HashMap(); static { for ( EnumTest f : EnumTest.values() ) { map.put( (int) f.id, f ); } } short id; private EnumTest(int id) { this.id = (short) id; }; public short getId() { return id; } public static final EnumTest fromInt(int id) { EnumTest e = map.get( id ); if ( e != null ) { return e; } throw new IllegalArgumentException( "" + id ); } public static void main(String[] args) { System.out.println( "size:" + map.size() ); } } 

运行环境:

 java version "1.7.0_01" Java(TM) SE Runtime Environment (build 1.7.0_01-b08) Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode) 

也发生在:

 java version "1.6.0_32" Java(TM) SE Runtime Environment (build 1.6.0_32-b05) Java HotSpot(TM) Client VM (build 20.7-b02, mixed mode, sharing) 

这些问题来自于一些(通常是编译器生成的)初始化代码超过65536字节的字节代码。 单个方法不能包含多个要执行的字节码字节(由于类文件格式的限制 )。

像这样的常见问题来源是这样的大型数组:

 byte someBytes = { 1, 2, 3, ..., someBigValue }; 

这里的问题是这些字段实际上是在生成的初始化器(构造函数或静态初始化器)中使用someBigValue赋值语句初始化的。

实际上,枚举值以类似的方式初始化。

给出以下枚举类:

 public enum Foo { CONSTANT(1); private Foo(int i) { } } 

我们查看javap -v的输出并查看以下代码块:

  static {}; flags: ACC_STATIC Code: stack=5, locals=0, args_size=0 0: new #4 // class Foo 3: dup 4: ldc #7 // String CONSTANT 6: iconst_0 7: iconst_1 8: invokespecial #8 // Method "":(Ljava/lang/String;II)V 11: putstatic #9 // Field CONSTANT:LFoo; 14: iconst_1 15: anewarray #4 // class Foo 18: dup 19: iconst_0 20: getstatic #9 // Field CONSTANT:LFoo; 23: aastore 24: putstatic #1 // Field $VALUES:[LFoo; 27: return 

正如您所看到的,有很多字节码操作可以处理使用正确值实例化CONSTANT 。 如果你有许多这样的枚举值,那么静态初始化程序块的大小很容易超过64k字节的代码,从而使类不可编译。

一种可能的解决方法是通过减少参数的数量来减小初始化代码的大小(例如,通过计算基于枚举值的索引而不是使用参数传入的数字)。 这可能只是给你足够的摆动空间来进一步扩展这一点。

或者,您可以尝试将枚举分成几个通过实现公共接口连接的枚举。 枚举可按区域/意图/类别/ …分组:

 public interface MessageType { int getId(); } public enum ConnectionMessage implements MessageType { INIT_CONNECTION(1), LOGIN(2), LOGOUT(3), CLOSE_CONNECTION(4); // getId code, constructor, ... } public enum FrobnicationMessage implements MessageType { FROBNICATE_FOO(5), FROBNICATE_BAR(6), DEFROB_FOO(7), DEFROB_BAR(8), ... // getId code, constructor, ... } 

我假设枚举值实际上是在代码中的某个地方引用的,而不仅仅是纯值持有者,如果它们只保存值并且代码中的各个值没有区别对待,那么将它们替换为每个数据项实例化一次的单个类存储在中央资源中可能是最好的方法。

我怀疑你编译时应该看到的是一个错误

 error: code too large 

也许您的编译器版本有一个错误,并没有显示这一点。

当我创建2500个枚举值时,它会因此错误而失败,但是有2400个枚举值,它会正确运行。

任何方法的字节代码都有64 KB的限制,并且在静态初始化程序块的一个方法中初始化枚举。

问题是许多字节码指令使用字节偏移作为16位值,这将限制放在整个方法上(即使在方法结束时没有这样的指令)

javac不够聪明,无法将静态初始化程序块分解为多个子方法,但是再次拥有数千个enums建议您可以执行另一种方式所需的操作。