【问题标题】:Maximum number of enum elements in JavaJava中的最大枚举元素数
【发布时间】:2011-05-26 23:39:47
【问题描述】:

Java 中枚举中允许的最大元素数是多少?

我想找出 switch 语句中的最大 case 数。由于 switch 中允许的最大原始类型是 int,因此我们有从 -2,147,483,648 到 2,147,483,647 的情况和一种默认情况。但是也允许使用枚举...所以问题..

【问题讨论】:

  • 我不知道,但想知道你在想什么类型的代码,这需要知道枚举的最大大小 lol
  • 如果看到 20 亿个案例的开关,我可能会杀死任何接触过该代码的人。
  • 考虑一种情况下的字节码将占用 10 个字节的堆。那么从 -2,147,483,648 到 2,147,483,647 的案例将占用 40 GB 的 RAM。如果每个 case 语句需要 3 行源代码,那么你就有 12884901885 行代码。这是一个沉重的课程:-)
  • 另外值得注意的是,Java 7 将允许在字符串上切换,因此您找到“最大切换案例数”的目标可能比看起来更加误导。
  • 是的,将每个元素生成为 print "A"+str(i)+"," ,其中 i 在循环中的索引中...

标签: java enums switch-statement


【解决方案1】:

Java 15+ 更新

在 JDK 15 中,枚举中常量的最大数量提高到大约 4103:https://bugs.openjdk.java.net/browse/JDK-8241798

这是通过将静态初始化程序分成两部分来实现的:

Java 15 之前(伪代码):

enum E extends Enum<E> {
    ...

    static {
        C1 = new E(...);
        C2 = new E(...);
        ...
        CN = new E(...);
        $VALUES = new E[N];
        $VALUES[0] = C1;
        $VALUES[1] = C2;
        ...
        $VALUES[N-1] = CN;
    }
}

从 Java 15 开始:

enum E extends Enum<E> {
    ...

    static {
        C1 = new E(...);
        C2 = new E(...);
        ...
        CN = new E(...);
        $VALUES = $values();
    }

    private static E[] $values() {
        E[] array = new E[N];
        array[0] = C1;
        array[1] = C2;
        ...
        array[N-1] = CN;
        return array ;
    }
}

这允许静态初始化程序包含更多代码(直到达到 64 KB 限制),因此可以初始化更多枚举常量。

【讨论】:

    【解决方案2】:

    枚举肯定有限制,主要(硬)限制约为 32K 值。它们受 Java 类最大值的限制,包括“常量池”(64K 条目)和 - 在某些编译器版本中 - 受静态初始化程序上的方法大小限制(64K 字节码)的限制。

    'Enum' 在内部初始化,每个值使用两个常量——一个 FieldRef 和一个 Utf8 字符串。这给出了大约 32K 值的“硬限制”。

    较旧的编译器(至少是 Eclipse Indigo)也遇到了静态初始化方法大小的问题。实例化每个值并将其添加到值数组中需要 24 个字节的字节码。可能会遇到大约 2730 个值的限制。

    较新的编译器(至少 JDK 7)会自动将大型静态初始化器拆分为名为 " enum constant initialization$2"、" enum constant initialization$3" 等的方法,因此不受第二个限制的约束。

    您可以通过javap -v -c YourEnum.class 反汇编字节码,看看它是如何工作的。

    [理论上可以将“旧式”Enum 类编写为手工编码的 Java,以打破 32K 限制并接近 64K 值。该方法是通过 reflection 初始化枚举值,以避免在池中需要字符串常量。我测试了这种方法,它在 Java 7 中有效,但这种方法的可取性(安全问题)值得怀疑。]

    编者注:Utf8 是 Java 类文件 IIRC 中的内部类型,不是需要更正的错字。

    【讨论】:

    • Thomas,关于将枚举初始化器拆分为多个方法,我是否可以阅读一些错误/规范?我有一个(生成的)枚举,可以用 Eclipse Java 编译器编译,但在 JDK 8 的 javac 上失败。
    • 嗨@Gunnar,我认为这只是Eclipse ECJ 编译器的一个特性。他们的编译器比 Sun/Oracle 的要好。
    【解决方案3】:

    枚举元素的最大数量是 2746。阅读规范非常具有误导性,导致我创建了一个有缺陷的设计,假设我永远不会达到 64K 甚至 32K 的高水位线。不幸的是,这个数字比规范似乎表明的要低得多。作为测试,我对 Java 7 和 Java 8 都尝试了以下操作:运行以下代码将其重定向到文件,然后编译生成的 .java 文件。

        System.out.println("public enum EnumSizeTest {");
        int max = 2746;
        for ( int i=0; i<max; i++) {
            System.out.println("VAR"+i+",");
        }
        System.out.println("VAR"+max+"}");
    

    结果,2746 有效,2747 无效。

    2746条后,编译器抛出code too large错误,如

    EnumSizeTest.java:2:错误:代码太大

    反编译这个枚举类文件,这个限制似乎是由静态构造函数中为每个枚举值生成的代码引起的(大部分)。

    【讨论】:

    • 似乎语言规范中应该有一些关于该限制的内容。编译器不能决定它想要允许多少。您收到什么错误消息?
    • 我编辑了帖子以回答您的问题并进一步解释我的回复。希望对您有所帮助。
    • 您使用的是 32 位还是 64 位 JDK/JRE?
    • 我在 Linux 和 Windows 操作系统上使用了 64 位 JDK。我今天再次运行测试:java 版本 "1.8.0_111" Java(TM) SE Runtime Environment (build 1.8.0_111-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
    • 显然新版本的开销更高。对于 1.8.0_111,最大值现在是 2745 个枚举。
    【解决方案4】:

    来自class file format spec:

    ClassFile 结构(第 4.1 节)的 16 位 constant_pool_count 字段将每个类或每个接口的常量池限制为 65535 个条目。这作为对单个类或接口的总复杂性的内部限制。

    我相信这意味着在一个类中不能有超过 65535 个命名的“事物”,这也会限制枚举常量的数量。

    如果看到一个有 20 亿个案例的开关,我可能会杀死任何接触过该代码的人。

    幸运的是,这不可能发生:

    每个非本地、非抽象方法的代码量被限制为 65536 字节,这取决于 Code 属性 (§4.7.3) 的 exception_table 和 LineNumberTable 属性 (§4.7.8) 中的索引大小) 和 LocalVariableTable 属性 (§4.7.9)。

    【讨论】:

    • LineNumberTable 不是一个拦截器 - 可以在一行中编写完整的 switch/20 亿 case 块;)
    • @Andreas_D:它仍然说“每个方法 65536 字节(字节码)”。源格式不应该在那里产生任何影响。
    • (我的评论不过是个玩笑)
    • 不幸的是,这个答案似乎不正确。虽然逻辑看似合理,但在现实中却并非如此。在 Java 7 和 8 中,单个 java 文件中的 Enum 常量限制为 2746。任何大于 2746 的内容都会导致“错误:代码太大”
    • @Brayden 我尝试编译您的代码,但编译器以“代码太大”退出。如果我将您的代码中的条目删除到 2745 个或更小的条目,则代码将编译并运行。我在 Windows 10、RH Linux 7.2 和 MacOS 上得到了相同的行为。在这个测试中,我只使用了 JDK 1.8_u191。
    【解决方案5】:

    嗯,在 jdk1.6 上我达到了这个限制。有人在 xsd 中有 10,000 个枚举,当我们生成时,我们得到一个 60,000 行的枚举文件,我得到一个不错的 java 编译器错误

    [ERROR] 无法在项目框架上执行目标 org.apache.maven.plugins:maven-compiler-plugin:2.0.2:compile (default-compile):编译失败 [错误] /Users/dhiller/Space/ifp-core/framework/target/generated-sources/com/framework/util/LanguageCodeSimpleType.java:[7627,4] 代码太大

    因此很可能该限制远低于此处的其他答案,或者可能生成的 cmets 等占用了太多空间。请注意 java 编译器错误中的行号是 7627,但如果行限制是 7627,我想知道行长度限制是什么;)可能相似。 IE。限制可能不是基于枚举数,而是基于行长度限制或文件限制中的行数,因此您可以将枚举重命名为 A、B 等,使其非常小,以便将更多枚举放入枚举中。

    我不敢相信有人写了一个包含 10,000 个枚举的 xsd。他们一定是生成了这部分 xsd。

    【讨论】:

      【解决方案6】:

      出于任何实际目的,本身没有最大数量。如果你需要在一个类中定义数千个枚举,你需要重写你的程序。

      【讨论】:

      • 有一个“实际目的”的最大数量。那是有效的数字。我认为您要表达的观点是,定义具有接近该数量值的枚举是不好的做法/不好的风格。
      • 哈哈。什么论据。就好像人们总是可以控制数据结构一样。在许多情况下,它们是外部提供的,与您的代码无关。如果有一个庞大的数据库随着时间的推移而发展起来,并且您想将一个属性映射到 java 中以使其类型安全/编译安全或其他任何东西,这是一件非常合理的事情。而且您不能指望数据库维护者/管理员/开发者考虑所有语言的内部限制。
      【解决方案7】:

      Java 中任何方法的最大大小为 65536 字节。虽然理论上您可以拥有较大的 switch 或更多枚举值,但它是您可能首先遇到的方法的最大大小。

      【讨论】:

        【解决方案8】:

        Enum 类使用int 来跟踪每个值的序数,因此最大值最多与 int 相同,如果不是更低的话。

        正如其他人所说,如果你不得不问你做错了

        【讨论】:

          猜你喜欢
          • 2010-10-17
          • 1970-01-01
          • 2012-01-13
          • 2019-07-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多