首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >枚举超出静态初始值设定项的65535字节限制...最好的办法是什么?

枚举超出静态初始值设定项的65535字节限制...最好的办法是什么?
EN

Stack Overflow用户
提问于 2010-03-30 23:28:50
回答 5查看 9K关注 0票数 20

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

下面是我的错误:静态初始化器的代码超过了65535字节的限制

很清楚这是从哪里来的-我的枚举只是有很多元素。但我需要这些元素--没有办法减少这些元素的数量。

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

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

我目前唯一的想法是定义一些称为描述符的接口,并编写几个实现它的枚举。这样,我希望能够像使用单个Enum一样使用Hibernate Enum映射。但我甚至不确定这是否会奏效。我失去了独一无二的安全感。

有什么办法处理这种情况吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-03-31 18:21:49

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

代码语言:javascript
复制
@Enumerated(STRING)
private DescriptorEnum descriptor;

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

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

代码语言:javascript
复制
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不允许将初始化分成几个块-我的类就是这么做的。

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

代码语言:javascript
复制
@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一样使用该类。有点棘手..。但它似乎起作用了。感谢您的所有投入,最终使我找到了那个解决方案。

票数 0
EN

Stack Overflow用户

发布于 2010-03-30 23:41:20

很简单。请不要使用enum完成此操作。你不能,它不会起作用的。

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

这种方法有一个额外的优点,那就是你可以修改“描述符”映射,而不需要修改程序的源代码。

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

更新

不幸的是,您的自助解决方案仍然会遇到不同的64K限制……如果太过分的话。拆分initialize()方法可以绕过方法大小限制,但类常量池中的条目数量也有64K的限制。每个字符串字面值都需要一个常量池项。

票数 8
EN

Stack Overflow用户

发布于 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个(静态的或非静态的)。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2546470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档