枚举添加静态初始化程序…的65535字节限制,最好做什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (8)

我开始创建一个相当大的所谓描述符的 Enum ,我想在我的模型中用作参考列表。但是现在我第一次遇到编译器/虚拟机的限制,所以我正在寻找解决这个问题的最佳解决方案。

这是我的错误:静态初始化程序的代码超过了65535字节的限制

很明显这是从哪里来的 - 我的Enum只是有很多元素。但是我需要这些元素 - 没有办法减少这个集合。

Initialy我已经计划使用一个枚举,因为我想确保Enum中的所有元素都是唯一的。它用于Hibernate持久性上下文中,其中对Enum的引用以String值存储在数据库中。所以这必须是独一无二的!

我的Enum的内容可以分成几组属于一组的元素。但是拆分Enum将会消除我在编译期间获得的独特安全性。或者可以通过某种方式实现多个枚举?

我目前唯一的想法是定义一个名为Descriptor的接口并编写一些实现它的Enums。这样我希望能够像使用单个Enum一样使用Hibernate Enum映射。但我甚至不确定这是否会奏效。我放松了独特的安全。

如何处理这种情况?

提问于
用户回答回答于

这不是一个简单的解决方案,但是你可以尝试......修补Java编译器。

当你编写时enum,Java编译器会生成一个扩展的java.lang.Enum类(如果有特定于常量的方法,可能会有几个类)。该类有一些(隐藏的)静态字段,它们在字节码级别用特殊的<clinit>()方法(JVM在首次使用类时调用)初始化。与其他方法一样,该方法的代码<clinit>()限制为65535字节。每个常量贡献了<clinit>()字节码中的大约20到22个字节(如果存在常量特定的构造函数,则更多),所以您在约3000个枚举常量处达到极限。

现在这个<clinit>()方法有一个有趣的名字,但没什么特别的; 它可以调用其他方法。Java编译器可以将庞然大物分割<clinit>()成几个隐藏的子方法,<clinit>()然后依次调用这些子方法。Java编译器目前没有这样做,但理论上可以。结果可以由任何JRE处理。

或者,综合综合你的枚举类,从一个专用程序生成字节码,可能它本身是用Java编写的。实质上,这就像编写自己的专用编译器,针对特定目标并使用自己的源语法。该BCEL库可能有帮助。

请注意,还有其他限制可能会跳到你身上。对于每个枚举常量,静态代码(在中<clinit>())使用两个“常量”,这是在编译的类的“常量池”部分中聚合的内部值。这两个值是常量名称(作为字符串)以及生成的静态字段引用。对65536个常量池条目有一个硬性限制(索引位于16位),所以不超过32000枚枚举常量。修补过的Java编译器可以通过生成几个隐藏类,每个隐藏类都有自己的常量池来绕过该限制。静态字段的数量越来越困难:每个枚举常量在“枚举”类中都成为静态字段,并且类中不能超过65535个字段(静态或非静态)。

用户回答回答于

简单。不要enum为此使用。你不能。它不会工作。

有可能你的源代码没有明确提到许多枚举值。相反,你使用枚举作为在唯一对象实例和字符串名称之间映射的便捷方式。因此,只需将enum类型替换为明确管理映射的类型,并通过从文件或数据库中读取来初始化。如果你做得对,你会得到一个枚举的计算属性和类型安全性。

这种方法将具有额外的优势,你可以在不修改程序的源代码的情况下修改“描述符”映射。

顺便说一句,你遇到的限制是由JVM类文件格式强加的。一个方法或构造函数的大小上限为2 ^ 16字节,而一个类的静态初始化代码被表示为一个具有时髦名字的特殊方法。

扫码关注云+社区