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

sta*_*ker 14 java enums

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

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

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

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

import java.util.HashMap;

public enum EnumTest {
    E(1),E(2),...,E(2001);

    private static HashMap<Integer, EnumTest>   map = new HashMap<Integer, EnumTest>();

    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() );
    }
}
Run Code Online (Sandbox Code Playgroud)

运行环境:

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)
Run Code Online (Sandbox Code Playgroud)

也发生在:

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)
Run Code Online (Sandbox Code Playgroud)

Joa*_*uer 13

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

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

byte someBytes = { 1, 2, 3, ..., someBigValue };
Run Code Online (Sandbox Code Playgroud)

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

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

给出以下枚举类:

public enum Foo {
  CONSTANT(1);

  private Foo(int i) {
  }
}
Run Code Online (Sandbox Code Playgroud)

我们查看输出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 "<init>":(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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,有很多字节码操作可以处理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, ...
}
Run Code Online (Sandbox Code Playgroud)

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

  • @JoachimSauer感谢您的建议.我将巨大的枚举分成两个枚举,用于接收和发送数据. (2认同)

Pet*_*rey 6

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

error: code too large
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

javac是不够聪明,打破了静态初始化器块分成多个子方法,但再有成千上万的enums建议,你可以做什么是需要另一种方式.