Enum遵循静态初始化程序的65535字节限制…最好的做法是什么?

我已经开始了一个相当大的所谓的Descriptors枚举,我想在我的模型中用作参考列表。 但是现在我第一次遇到编译器/ VM限制,所以我正在寻找最好的解决方案来处理这个问题。

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

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

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

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

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

任何想法如何处理这种情况?

简单。 不要使用enum 。 你不能。 它不会起作用。

可能是您的源代码没有明确引用许多枚举值。 相反,您使用枚举作为在唯一对象实例和字符串名称之间进行映射的便捷方式。 因此,只需使用显式管理映射的类型替换枚举类型,通过从文件或数据库读取来初始化它。 如果你做得对,你将获得枚举的计算属性和类型安全性。 你唯一丢失的是语法糖……和静电。

这种方法的另一个优点是,您可以修改“描述符”映射,而无需修改程序的源代码。


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

UPDATE

不幸的是,你的自我解决方案仍会遇到不同的64K限制……如果推得太远。 拆分initialize()方法会绕过方法大小限制,但类常量池中的条目数有64K限制。 每个String文字都需要一个常量池条目。

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

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

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

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

请注意,还有其他限制可能会对您产生影响。 对于每个枚举常量,静态代码( () )使用两个“常量”,它们是在已编译类的“常量池”部分中聚合的内部值。 这两个值是常量名称(作为字符串)和生成的静态字段引用。 65536个常量池条目有一个硬限制(索引在16位上),因此不超过32000个枚举常量。 修补后的Java编译器可以通过生成几个隐藏类来绕过该限制,每个类都有自己的常量池。 更严格的限制是静态字段的数量:每个枚举常量变为“enum”类中的静态字段,并且类中不能超过65535个字段(静态或非静态)。

您可以在他的书“Effective Java”的第一版中尝试Joshua Bloch描述的类型安全枚举模式 。

我们的想法是创建一个带有私有构造函数的类。 在这个类中,你可以定义你想要的任何数字的静态实例 – 就像在Java枚举中一样。

不过,我不知道Java语言中静态成员的数量是否有限制。

您可以尝试在顶级类中嵌套静态内部类

我最初的想法是使用@Enumerated annotion映射Enum。 这看起来像下面的例子:

 @Enumerated(STRING) private DescriptorEnum descriptor; 

数据库将有一个名为DESCRIPTOR的列,类型为varchar,Hibernate(在我的例子中)会将字符串映射到枚举。

但我有65k的限制(见问题),这在我的情况下很小。 但我找到了解决方案。 看看下面的例子:

 public final class Descriptor { public final String acronym; private static final Hashtable all = new Hashtable(); static { initialize(); } private static void initialize() { new Descriptor("example001"); new Descriptor("example002"); new Descriptor("example003"); } private Descriptor(String acronym) { this.acronym = acronym; if (all.contains(this.acronym)) { throw new RuntimeException("duplicate acronym: " + this.acronym); } all.put(this.acronym, this); } public static Descriptor valueOf(String acronym) { return all.get(acronym); } public String value() { return this.acronym; } } 

此Descriptor类模拟典型枚举的用法。 但是现在我能够将initialize()方法拆分为几个可以解决方法存在的65k限制的方法。 Enum不允许将初始化分成几个块 – 我的class级确实如此。

现在我必须使用稍微不同的映射:

 @Column(name = "DESCRIPTOR") private String descriptorAcronym = null; private transient Descriptor descriptor = null; public Descriptor getDescriptor() { return descriptor; } public void setDescriptor(Descriptor desc) { this.descriptor = desc; this.descriptorAcronym = desc != null ? desc.acronym : null; } public String getDescriptorAcronym() { return descriptorAcronym; } public void setDescriptorAcronym(String desc) { this.descriptorAcronym = desc; this.descriptor = desc != null ? Descriptor.valueOf(desc) : null; } @PostLoad private void syncDescriptor() { this.descriptor = this.descriptorAcronym != null ? Descriptor.valueOf(this.descriptorAcronym) : null; } 

这样我在大多数情况下都可以像Enum这样使用类。 这有点棘手……但它似乎有效。 感谢所有最终引导我进入该解决方案的输入。