Enum exeeding limit of 65535 bytes of static initializer ... what is the best thing to do?

I started a fairly large Enum from the so-called descriptors that I wanted to use as a list of links in my model. But now I am faced with a compiler / virtual machine restriction for the first time, and therefore I am looking for the best solution for this.

Here is my mistake: the code for the static initializer exceeds the limit of 65535 bytes

It's clear where this comes from - my Enum just has a lot of elements. But I need these elements - there is no way to reduce this set.

I originally planned to use one Enum, because I want to make sure that all elements in Enum are unique. It is used in the context of saving Hibernate , where the reference to Enum is stored as a String value in the database. Therefore, it must be unique!

The contents of my Enum can be divided into several groups of elements that belong to each other. But splitting Enum will remove the unique security that I get at compile time. Or can this be achieved with several listings in some way?

My only idea is to define some interface called the Descriptor and encode several Enums that implement it. That way, I hope I can use the Hibernate Enum mapping, as if it were the only Enum. But I'm not even sure that this will work. And I am losing unique security.

Any ideas how to handle this case?

+18
java compiler-construction enums limit
Mar 30 '10 at 15:28
source share
5 answers

My initial idea was to display Enum using @Enumerated annotion. It would look like this:

@Enumerated(STRING) private DescriptorEnum descriptor; 

There will be a column in the database called DESCRIPTOR of type varchar, and Hibernate (in my case) will map the string to an enumeration.

But I have this limit of 65 thousand (see the question), which in my case is small. But I found a solution. Take a look at the following example:

 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; } } 

This descriptor class mimics the use of a typical enumeration. But now I can split the initialize () method into several that work with a 65k limit, which also exists for methods. Enum does not allow to split the initialization into several pieces - my class does.

Now I need to use a slightly different mapping:

 @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; } 

That way, I can use a class like Enum in most cases. It's a little complicated ... but it seems to work. Thanks for all that led me to this decision.

0
Mar 31 '10 at 10:21
source share

Simple Do not use enum for this. You can not. This will not work.

Most likely, your source code does not explicitly reference many of the enumeration values. Rather, you use enumeration as a convenient way of matching between unique instances of objects and string names. Therefore, simply replace the enumeration type with a type that explicitly controls the mapping, initializing it by reading from a file or database. If you do this correctly, you will get the computational properties and the security type of the enumeration. The only thing you lose is syntactic sugar ... and statics.

This approach will have the added benefit that you can change the display of descriptors without changing the source code of your program.




By the way, the restriction you are working on is imposed by the file format of the JVM class. The method or constructor has an upper limit of 2 ร— 16 bytes, and the static class initialization code is presented as a special method with a funky name.

UPDATE

Unfortunately, your self-answer solution will still run into another 64K limit ... if you go too far. The separation of the initialize() method extends to the method size limit, but there is also a 64 Kbyte limit on the number of entries in the pool of class constants. For each string literal, a constant pool entry is required.

+8
Mar 30 '10 at 15:41
source share

This is not an easy solution, but you can try ... fix the Java compiler.

When you write enum , the Java compiler generates a class that extends java.lang.Enum (possibly several classes if there are methods specific to the constant). The class has several (hidden) static fields that are initialized at the bytecode level with the special <clinit>() method (which the JVM calls when the class is first used). Like any other method, the code for the <clinit>() method is limited to 65535 bytes. Each constant contributes about 20-22 bytes to the <clinit>() bytecode (more if there are constructors specific to the constant), so you click the limit on about 3000 enumeration constants.

The <clinit>() method now has a funny name, but thatโ€™s nothing special; it may call other methods. The Java compiler could break the mammoth <clinit>() into several hidden sub-methods, which then <clinit>() call one after another. The Java compiler does not currently do this, but theoretically it can. The result will be processed by any JRE.

Alternatively, synthesize your enum class synthetically by generating bytecode from a dedicated program, possibly written in Java itself. Essentially, it is like writing your own specialized compiler for a specific purpose and using your own source syntax. BCEL library can help.

Please note that there are other restrictions that may apply to you. For each enumeration constant, the static code (one in <clinit>() ) uses two "constants", which are internal values โ€‹โ€‹aggregated in the "constant pool" part of the compiled class. Two values โ€‹โ€‹are the name of the constant (as a string) and the resulting static field reference. There is a hard limit for 65,536 permanent pool entries (16-bit indexes), so no more than 32,000 enumeration constants. An encrypted Java compiler could get around this limit by creating several hidden classes, each of which has its own persistent pool. A more severe restriction is the number of static fields: each enumeration constant becomes a static field in the "enum" class, and there can be no more than 65535 fields (static or not) in the class.

+5
Mar 30 '10 at 15:57
source share

You can try the typume enum pattern described by Joshua Bloch in the first issue of his book Effective Java.

The idea is to create a class with a private constructor. Inside this class, you define static instances of any number you want - as in the Java enumeration.

I do not know if there is a limit on the number of static elements in Java.

+3
Mar 30 '10 at 15:36
source share

You can try inserting static inner classes into a top-level class

0
Mar 30 '10 at 16:11
source share



All Articles