我已经开始了一个相当大的名为Descriptors的枚举,我想在我的模型中将其用作一个引用列表。但是现在我第一次遇到了编译器/VM的限制,所以我正在寻找最好的解决方案来处理这个问题。
下面是我的错误:静态初始化器的代码超过了65535字节的限制
很清楚这是从哪里来的-我的枚举只是有很多元素。但我需要这些元素--没有办法减少这些元素的数量。
最初,我计划使用单个Enum,因为我想确保Enum中的所有元素都是唯一的。它用在Hibernate持久性上下文中,其中对枚举的引用作为字符串值存储在数据库中。所以这必须是唯一的!
my Enum的内容可以分为几组属于同一元素的元素。但是拆分Enum会消除我在编译时获得的独特安全性。或者,这可以通过多个枚举以某种方式实现吗?
我目前唯一的想法是定义一些称为描述符的接口,并编写几个实现它的枚举。这样,我希望能够像使用单个Enum一样使用Hibernate Enum映射。但我甚至不确定这是否会奏效。我失去了独一无二的安全感。
有什么办法处理这种情况吗?
发布于 2010-03-31 18:21:49
我最初的想法是使用@Enumerated注解来映射Enum。这看起来像下面的示例:
@Enumerated(STRING)
private DescriptorEnum descriptor;
数据库将有一个名为DESCRIPTOR的列,类型为varchar,Hibernate (在我的示例中)将字符串映射到枚举。
但我有65k的限制(见问题),这在我的情况下是很小的。但我找到了一个解决方案。看一下下面的例子:
public final class Descriptor {
public final String acronym;
private static final Hashtable<String, Descriptor> all = new Hashtable<String, Descriptor>();
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;
}
}
这个描述符类模拟典型枚举的用法。但现在我可以将initialize()方法拆分成几个方法,以绕过同样存在于方法的65k限制。Enum不允许将初始化分成几个块-我的类就是这么做的。
现在我必须使用一个稍微不同的映射:
@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一样使用该类。有点棘手..。但它似乎起作用了。感谢您的所有投入,最终使我找到了那个解决方案。
发布于 2010-03-30 23:41:20
很简单。请不要使用enum
完成此操作。你不能,它不会起作用的。
您的源代码可能没有显式引用许多枚举值。相反,使用枚举可以方便地在唯一对象实例和字符串名称之间进行映射。因此,只需将枚举类型替换为显式管理映射的类型,通过从文件或数据库读取来初始化它。如果操作得当,您将获得枚举的计算属性和类型安全性。你唯一失去的就是语法糖..。还有静力学。
这种方法有一个额外的优点,那就是你可以修改“描述符”映射,而不需要修改程序的源代码。
顺便说一句,您遇到的限制是由JVM类文件格式施加的。方法或构造函数的大小上限为2^16字节,类的静态初始化代码被表示为一个带有时髦名称的特殊方法。
更新
不幸的是,您的自助解决方案仍然会遇到不同的64K限制……如果太过分的话。拆分initialize()
方法可以绕过方法大小限制,但类常量池中的条目数量也有64K的限制。每个字符串字面值都需要一个常量池项。
发布于 2010-03-30 23:57:21
这不是一个简单的解决方案,但您可以尝试...修补Java编译器。
当您编写enum
时,Java编译器会生成一个扩展java.lang.Enum
的类(如果有特定于常量的方法,可能会有几个类)。该类有一些(隐藏的)静态字段,这些字段在字节码级别上是用特殊的<clinit>()
方法初始化的(第一次使用该类时,JVM会调用这个方法)。与任何其他方法一样,<clinit>()
方法的代码限制为65535字节。在<clinit>()
字节码中,每个常量贡献了大约20到22个字节(如果有特定于常量的构造函数,则会更多),因此达到了大约3000个枚举常量的限制。
现在,<clinit>()
方法有了一个有趣的名字,但它并没有什么特别之处;它可以调用其他方法。Java编译器可以将庞大的<clinit>()
分成几个隐藏子方法,然后<clinit>()
将一个接一个地调用这些子方法。Java编译器目前还没有做到这一点,但理论上它可以做到这一点。任何JRE都可以处理结果。
或者,综合您的enum类,从专用程序生成字节码,该程序本身可能用Java编写。本质上,这就像是为特定的目标编写自己的专用编译器,并使用自己的源码语法。BCEL库可能会有所帮助。
请注意,还有其他限制可能会跳到您身上。对于每个枚举常量,静态代码(<clinit>()
中的那个)使用两个“常量”,这两个“常量”是聚集在已编译类的“常量池”部分的内部值。这两个值是常量名称(以字符串形式)和结果静态字段引用。对65536个常量池条目有硬限制(索引为16位),因此不超过32000个枚举常量。打了补丁的Java编译器可以通过生成几个隐藏类来绕过这一限制,每个隐藏类都有自己的常量池。一个更严格的限制是静态字段的数量:每个枚举常量都成为"enum“类中的一个静态字段,并且一个类中的字段不能超过65535个(静态的或非静态的)。
https://stackoverflow.com/questions/2546470
复制相似问题