包含2000 + 1枚举常量的枚举类是否达到了限制? [英] Does an Enum Class containing 2000+1 Enum Constants hit any limit?

查看:201
本文介绍了包含2000 + 1枚举常量的枚举类是否达到了限制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The following code fails with a NullPointerException in main (map==null). The issue occurs only if I define 2001 or more Enum constants, 2000 work fine.

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

Why isn't the static code block not executed?

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

Do we hit any silent limit of the compiler (no warnings, no errors) or JVM?

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

The compiled class file exceeds 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() );
    }
}




运行时环境:

Runtime Environment:

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字节的字节代码。单个方法不能包含多个要执行的字节码字节(由于类文件格式的限制)。

These kinds of problems come from the fact that some (often compiler-generated) initializer code exceeds 65536 bytes of byte code. A single method can not contain more than that many bytes of bytecode to be executed (due to restrictions in the class file format).

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

A common source of problems like this are large arrays like this:

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

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

The problem here is that such fields are actually initialized with someBigValue assignment statements in a generated initializer (constructor or static initializer).

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

Enum values are actually initialized in a similar fashion.

给出以下枚举类:

public enum Foo {
  CONSTANT(1);

  private Foo(int i) {
  }
}

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

We look at the output of javap -v and see the following code block:

  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

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

As you can see there are quite a lot of bytecode operations that handle instantiating CONSTANT with the correct values. If you have many such enum values, then the size of that static initializer block can easily exceed 64k bytes of code and thus make the class uncompilable.

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

A possible workaround is to reduce the size of the initializing code by reducing the number of arguments (for example by calculating the number passed in based on the index of the enum value instead of using an argument). That might just give you enough wiggle room to extend this a bit further.

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

Alternatively you could try splitting your enum into several enums connected by implementing a common interface. The enums could be grouped by area/intention/category/...:

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, ...
}

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

I'm assuming that the enum values are actually referenced somewhere in your code and not just pure value holders, if they only hold values and individual values are not treated differently in your code, then replacing them with a single class instantiated once per data item stored in a central resource is probably the best approach.

这篇关于包含2000 + 1枚举常量的枚举类是否达到了限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆