Does an Enum Class containing 2000+1 Enum Constants hit any limit? - java

The following code fails with a NullPointerException in main (map==null).
The issue occurs only if I define 2001 or more Enum constants, 2000 work fine.
Why isn't the static code block not executed?
Do we hit any silent limit of the compiler (no warnings, no errors) or JVM?
The compiled class file exceeds 172KB,
import java.util.HashMap;
public enum EnumTest {
E(1),E(2),...,E(2001);
private static HashMap<Integer, EnumTest> map = new HashMap<Integer, EnumTest>();
static {
for ( EnumTest f : EnumTest.values() ) {
map.put( (int) f.id, f );
}
}
short id;
private EnumTest(int id) {
this.id = (short) id;
};
public short getId() {
return id;
}
public static final EnumTest fromInt(int id) {
EnumTest e = map.get( id );
if ( e != null ) {
return e;
}
throw new IllegalArgumentException( "" + id );
}
public static void main(String[] args) {
System.out.println( "size:" + map.size() );
}
}
Runtime Environment:
java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode)
also happens with:
java version "1.6.0_32" Java(TM) SE Runtime Environment (build
1.6.0_32-b05) Java HotSpot(TM) Client VM (build 20.7-b02, mixed mode, sharing)

These kinds of problems come from the fact that some (often compiler-generated) initializer code exceeds 65536 bytes of byte code. A single method can not contain more than that many bytes of bytecode to be executed (due to restrictions in the class file format).
A common source of problems like this are large arrays like this:
byte someBytes = { 1, 2, 3, ..., someBigValue };
The problem here is that such fields are actually initialized with someBigValue assignment statements in a generated initializer (constructor or static initializer).
Enum values are actually initialized in a similar fashion.
Given the following enum class:
public enum Foo {
CONSTANT(1);
private Foo(int i) {
}
}
We look at the output of javap -v and see the following code block:
static {};
flags: ACC_STATIC
Code:
stack=5, locals=0, args_size=0
0: new #4 // class Foo
3: dup
4: ldc #7 // String CONSTANT
6: iconst_0
7: iconst_1
8: invokespecial #8 // Method "<init>":(Ljava/lang/String;II)V
11: putstatic #9 // Field CONSTANT:LFoo;
14: iconst_1
15: anewarray #4 // class Foo
18: dup
19: iconst_0
20: getstatic #9 // Field CONSTANT:LFoo;
23: aastore
24: putstatic #1 // Field $VALUES:[LFoo;
27: return
As you can see there are quite a lot of bytecode operations that handle instantiating CONSTANT with the correct values. If you have many such enum values, then the size of that static initializer block can easily exceed 64k bytes of code and thus make the class uncompilable.
A possible workaround is to reduce the size of the initializing code by reducing the number of arguments (for example by calculating the number passed in based on the index of the enum value instead of using an argument). That might just give you enough wiggle room to extend this a bit further.
Alternatively you could try splitting your enum into several enums connected by implementing a common interface. The enums could be grouped by area/intention/category/...:
public interface MessageType {
int getId();
}
public enum ConnectionMessage implements MessageType {
INIT_CONNECTION(1),
LOGIN(2),
LOGOUT(3),
CLOSE_CONNECTION(4);
// getId code, constructor, ...
}
public enum FrobnicationMessage implements MessageType {
FROBNICATE_FOO(5),
FROBNICATE_BAR(6),
DEFROB_FOO(7),
DEFROB_BAR(8),
...
// getId code, constructor, ...
}
I'm assuming that the enum values are actually referenced somewhere in your code and not just pure value holders, if they only hold values and individual values are not treated differently in your code, then replacing them with a single class instantiated once per data item stored in a central resource is probably the best approach.

I suspect what you should be seeing is an error when you compile
error: code too large
Perhaps your version of the compiler has a bug and doesn't show this.
When I create 2500 enums values it fails with this error but with 2400 enum values it runs correctly.
There is a limit of 64 KB for the byte code of any method and the enums are initialised in the one method for the static initializer block.
The problem is that many byte code instructions use the byte offset as a 16-bit value which places this limitation on the whole method (even if there is no such instruction at the end of a method)
The javac is not smart enough to break up the static initialiser block into multiple sub-methods, but then again having thousands of enums suggest you could do what is required another way.

Related

Default Instance Value Initialization in java

I am trying to check for default value of instance variables (i.e. 0 here) in the generated bytecode.
I can see <init>() getting called and if I print myvar instance variable inside constructor then I see getfield called for myvar but then where was this default set first?
Please answer on following:
When is default value being set in myvar? (after compilation or object creation time)
Who(compiler or jvm) is initializing(or let's say setting default value) in the instance variable?
public class FieldInit {
int myvar;
public static void main(String[] args) {
new FieldInit(); // and what would happen if I comment out this
}
}
I am trying to deassemble bytecode using javap but not able to see the <clinit>() method, I guess here this may be happening. Please let me know if it is possible to see <clinit>() method and if so, how?
In JVM, an object instantiation is split into two bytecode instructions:
new allocates a new uninitialized object;
invokespecial calls a constructor that initializes the object.
The JVM Specification for the new bytecode says:
Memory for a new instance of that class is allocated from the
garbage-collected heap, and the instance variables of the new object
are initialized to their default initial values
The JVM sets all instance fields to zeroes when executing new instruction. So, by the time a constructor is invoked, all fields are already set to their default values. You will not find this "zeroing" in the bytecode - it's done implicitly by the JVM during object allocation.
Your question is not answerable because it's more complicated than you think it is.
fields in java are encoded in a field datastructure and this datastructure includes room for the initial value. However, the only possible initial values in this data structure are numbers and strings.
Let's see it in action! (and note that L is just a java syntax thing to tell java: This number is a long, not an int. 5 is a constant, so is 5L).
class Test {
public static final long mark = 5L;
}
javac Test.java
javap -v c Test
.....
public static final long mark;
descriptor: J
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: long 5l
Hey look there it is, constant value 5L.
But what if it isn't constant?
Ah, that's a problem. You can't encode that here.
So, instead, it's syntax sugar time!
You can write a special method in any class that is run during a class's static initialization. You can also write a special method that is run as a new instance is made. That one is almost entirely the same as a constructor, with only exotic differences. It looks like this:
public class Test {
static {
System.out.println("What voodoo magic is this?");
}
public static void main(String[] args) {
System.out.println("In main");
}
}
Let's see it in action!
javac Test.java
java Test
What voodoo magic is this?
In main
javap -c -v Test
...
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #7 // Field >java/lang/System.out:Ljava/io/PrintStream;
3: ldc #21 // String Voodoo...
5: invokevirtual #15 // Method >java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
}
As you can see, that weird static{} thing was compiled to something that looks exactly like a method, bytecode and all, but the name of the method is bizarre, it's just static{}.
Here comes the clue
But what happens if we make that a little more complicated. Let's initialize this field to the current time, instead!
class Test {
public static final long mark = System.currentTimeMillis();
}
This is just syntax sugar. That explains how this works, because as I told you, at the class file level, you cannot initialize fields with non-constants. Thus, that compiles to the same thing as:
class Test {
public static final long mark;
static {
mark = System.currentTimeMillis();
}
}
and you can javap to confirm this.
One is called a 'compile time constant'. The other isn't. This shows up in various ways. For example, you can pass a CTC as annotation parameter. Try that: try using static final long MARK = 5L; (you will be able to), then static final long MARK = System.currentTimeMillis(); - that won't be allowed.
So, where is that initial value? If it's a constant value, javap -c -v will show it. If it isn't, it's stuck in the static block.

JAR replacing constants with values [duplicate]

In java, say I have the following
==fileA.java==
class A
{
public static final int SIZE = 100;
}
Then in another file I use this value
==fileB.java==
import A;
class b
{
Object[] temp = new Object[A.SIZE];
}
When this gets compiled does SIZE get replaced with the value 100, so that if I were to replace the FileA.jar but not FileB.jar, would the object array get the new value or would it have been hardcoded to 100 because that's the value when it was originally built?
Yes, the Java compiler does replace static constant values like SIZE in your example with their literal values.
So, if you would later change SIZE in class A but you don't recompile class b, you will still see the old value in class b. You can easily test this out:
file A.java
public class A {
public static final int VALUE = 200;
}
file B.java
public class B {
public static void main(String[] args) {
System.out.println(A.VALUE);
}
}
Compile A.java and B.java. Now run: java B
Change the value in A.java. Recompile A.java, but not B.java. Run again, and you'll see the old value being printed.
You can keep the constant from being compiled into B, by doing
class A
{
public static final int SIZE;
static
{
SIZE = 100;
}
}
Another route to proving that the behavior is to looking at the generated bytecode. When the constant is "small" (presumably < 128):
public B();
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 42
7: anewarray #3; //class java/lang/Object
10: putfield #12; //Field temp:[Ljava/lang/Object;
13: return
}
(I used 42 instead of 100 so it stands out more). In this case, it is clearly substituted in the byte code. But, say the constant is "bigger." Then you get byte code that looks like this:
public B();
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #12; //int 86753098
7: anewarray #3; //class java/lang/Object
10: putfield #13; //Field temp:[Ljava/lang/Object;
13: return
When it is bigger, the opcode "ldc" is used, which according to the JVM documentation "an unsigned byte that must be a valid index into the runtime constant pool of the current class".
In either case, the constant is embedded into B. I imagine, since that in opcodes you can only access the current classes runtime constant pool, that this the decision to write the constant into the class file is independent of implementation (but I don't know that for a fact).
Woo - you learn something new everyday!
Taken from the Java spec...
Note: If a primitive type or a string
is defined as a constant and the value
is known at compile time, the compiler
replaces the constant name everywhere
in the code with its value. This is
called a compile-time constant. If the
value of the constant in the outside
world changes (for example, if it is
legislated that pi actually should be
3.975), you will need to recompile any classes that use this constant to get
the current value.
The important concept here is that the static final field is initialised with a compile-time constant, as defined in the JLS. Use a non-constant initialiser (or non-static or non-final) and it wont be copied:
public static final int SIZE = null!=null?0: 100;
(null is not a *compile-time constant`.)
Actually I ran into this bizarreness a while ago.
This will compile "100" into class b directly. If you just recompile class A, this will not update the value in class B.
On top of that, the compiler may not notice to recompile class b (at the time I was compiling single directories and class B was in a separate directory and compiling a's directory did not trigger a compile of B)
As an optimization the compiler will inline that final variable.
So at compile time it will look like.
class b
{
Object[] temp = new Object[100];
}
One thing should note is: static final value is known at compile time
if the value is not known at compile time, compiler won't replaces the constant name everywhere in the code with its value.
public class TestA {
// public static final int value = 200;
public static final int value = getValue();
public static int getValue() {
return 100;
}
}
public class TestB {
public static void main(String[] args) {
System.out.println(TestA.value);
}
}
first compile TestA and TestB, run TestB
then change TestA.getValue() to return 200, compile TestA, run TestB, TestB will get the new value
enter image description here
There is an exception to this:-
If static final field is null at the time of compiling then it doesn't get replaced with null (which is actually its value)
A.java
class A{
public static final String constantString = null;
}
B.java
class B{
public static void main(String... aa){
System.out.println(A.constantString);
}
}
Compile both A.java and B.java and run java B
Output will be null
Now Update A.java with following code and compile only this class.
class A{
public static final String constantString = "Omg! picking updated value without re-compilation";
}
Now run java B
Output will be Omg! picking updated value without re-compilation
Java does optimise these sorts of values but only if they are in the same class. In this case the JVM looks in A.SIZE rather than optimizing it because of the usage case you are considering.

Changes around type inference check in Java 9

Code (Shortened the actual code to explain thew question).
import java.util.Map;
import java.util.HashMap;
public class TypeReferenceTest {
public static class Model {
public void setAbc(Abc<String> abc) { }
}
public static class Abc<T> {
public Abc(T val) { }
}
public static void main(String[] args) {
Map<String, Object> attrMap = new HashMap<>();
attrMap.put("key", 0);
Model m = new Model ();
m.setAbc(new Abc<>(getAttrOrDefault(attrMap, "key", "Default")));
System.out.println("Test completed.....");
}
public static <T extends Object> T getAttrOrDefault(Map<String, Object> attrMap, String attrName, T defaultValue) {
#SuppressWarnings("unchecked")
T attrValue = (T)attrMap.get(attrName);
return (attrValue == null) ? defaultValue : attrValue;
}
}
Test
host:~/temp/test> /usr/local/java/jdk1.8/bin/javac TypeReferenceTest.java
host:~/temp/test> file TypeReferenceTest.class
TypeReferenceTest.class: compiled Java class data, version 52.0 (Java 1.8)
host:~/temp/test> /usr/local/java/jdk9/bin/java TypeReferenceTest
Test completed.....
host:~/temp/test> /usr/local/java/jdk9/bin/javac TypeReferenceTest.java
host:~/temp/test> file TypeReferenceTest.class
TypeReferenceTest.class: compiled Java class data, version 53.0
host:~/temp/test> /usr/local/java/jdk9/bin/java TypeReferenceTest
Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.Integer cannot be cast to java.base/java.lang.String
at TypeReferenceTest.main(TypeReferenceTest.java:18)
host:~/temp/test>
Please note the exception when the same code was run on Java 9 compiled code. I understand the the reason why code caused ClassCastException, but it is OK if code is compiled with Java 8 (runtime being Java 9 in both the cases). To see the difference, I used javap and disassembled the code to see the diff.
Java 8 compiled disassembled code (only section of interest here)
39: invokestatic #11 // Method getAttrOrDefault:(Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
42: invokespecial
Java 9 compiled disassembled code (only section of interest here)
39: invokestatic #11 // Method getAttrOrDefault:(Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
42: checkcast #12 // class java/lang/String
45: invokespecial
Noting the difference in Java 9 compiled disassembled code, it is explicitly checking the type in relevant instruction. Of course, that should have been easily inferred from the code that return type should be string but there was no explicit check earlier.
Questions: Has there been some change around type inference in Java 9 and adding explicit check? If yes, where can I find the details (could not find in changelog)? Is it some compilation default option that has been changed in Java 9 that is adding this explicit type check in java 9?
Thanks,
Mozaffar
It looks like this was a bug in java-8, most probably this one. And it got fixed in java-9. The checkcast must be there to begin with since you only take Abc<String> as input, in my opinion.

Java compiler allows accessing uninitialized blank final field using 'this' keyword? Is this a bug? [duplicate]

This question already has answers here:
Use of uninitialized final field - with/without 'this.' qualifier
(4 answers)
Closed 8 years ago.
I wrote this piece of code and it seems compiler allows accessing uninitialized blank final field when accessed using 'this' keyword:
public class TestClass
{
public final int value1;
public int value2;
TestClass(int value) {
value2 = 2 + this.value1; // access final field using 'this' before initialization gives no compiler error
//value2 = 2 + value1; // uncomment it gives compile time error - variable value1 might not have been initialized
value1 = value;
}
public static void main(String args[]) {
TestClass tc = new TestClass(10);
System.out.println("\nTestClass Values : value1 =  " + tc.value1 + " , value2 =  " + tc.value2);
}
}
I tried compiling it on 1.5, 1.6, & 1.7 and got same result in all three of them.
To me it looks like compiler bug because compiler must throw error in this case but with 'this' keyword it doesn't and thus creates scope of coding error as it will go unnoticed by the programmer since no compile-time or run-time error will be thrown.
FEW POINTS WHY IT IS NOT A DUPLICATE
- all answers are explaining how it works and what JLS says, fine, but my real intent here is should that be allowed at the first place?
- my question here is more from programmer's point of view and not language semantics
This is not a bug. This a feature for Java Specification 1.6 and lower.
The final field can be accessed at any part of the code. There is no restriction about it.
The only restriction in case of final is that it has to be initialized before class instance is created.
When you use this, you express that it is element of the object that is being constructed.
Since the specification 1.7 because of change we may say that this is bug in some implementation of compilers.
But since 1.8 the code will produce same error for this.value1 or value1.
In Java ints have a default value of 0, so even if you did not initialize it, the compiler has still assigned 0 as value.
Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type.
Source: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
Note that this does not apply to local variables, only field variables have a default value.
I used the following code
public class HelloWorld {
public final int value1;
public int value2;
public HelloWorld(int value){
System.out.println(this.value1);
System.out.println(this.value2);
value1 = value;
}
public static void main(String args[]) {
}
}
and the bytecode it generates is
Compiled from "HelloWorld.java"
public class test.HelloWorld extends java.lang.Object{
public final int value1;
public int value2;
public test.HelloWorld(int);
Code:
0: aload_0
1: invokespecial #11; //Method java/lang/Object."<init>":()V
4: getstatic #14; //Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_0
8: getfield #20; //Field value1:I
11: invokevirtual #22; //Method java/io/PrintStream.println:(I)V
14: aload_0
15: iload_1
16: putfield #20; //Field value1:I
19: return
public static void main(java.lang.String[]);
Code:
0: return
}
If you notice JVM does a getstatic call in case of final and getfield in case of normal instance variable. Also now if you look at the specs for getstatic it says
On successful resolution of the field, the class or interface that declared the resolved field is initialized (§5.5) if that class or interface has not already been initialized.
So if you use this with a final variable then it is initialize it. Not it will initialize this.value1 and not value1 which is final. You still have to initialize it before using.

Use Scala constants in Java

I am currently evaluating Scala for future projects and came across something strange. I created the following constant for us in a JSP:
val FORMATED_TIME = "formatedTime";
And it did not work. After some experimenting I decided to decompile to get to the bottom of it:
private final java.lang.String FORMATED_TIME;
public java.lang.String FORMATED_TIME();
Code:
0: aload_0
1: getfield #25; //Field FORMATED_TIME:Ljava/lang/String;
4: areturn
Now that is interesting! Personally I have been wondering for quite a while why an inspector needs the prefix get and a mutator the prefix set in Java as they live in different name-spaces.
However it might still be awkward to explain that to the rest of the team. So is it possible to have a public constant without the inspector?
This is because of the Uniform Access Principle, i.e.: Methods and Fields Are Indistinguishable
See this answer
In Scala 2.8.0 this means if you have
a companion object, you lose your
static forwarders)
If you have this in Scala:
//Scala
object CommonControler {
val FORMATED_TIME = "formatedTime";
}
You may use it like this from Java
//Java
// Variables become methods
CommonControler$.MODULE$.FORMATED_TIME();
// A static forwarder is avaliable
CommonControler.FORMATED_TIME();
Also see the book Scala in Depth
Also note the #scala.reflect.BeanProperty for classes.
I had a further look into the decompiled code and noted something else. The variables are not actually static. So my next idea was to use an object instead:
object KommenControler
{
val FORMATED_TIME = "formatedTime";
} // KommenControler
But now things turn really ugly:
public final class ….KommenControler$ extends java.lang.Object implements scala.ScalaObject{
public static final ….KommenControler$ MODULE$;
private final java.lang.String FORMATED_TIME;
public static {};
Code:
0: new #9; //class …/KommenControler$
3: invokespecial #12; //Method "<init>":()V
6: return
public java.lang.String FORMATED_TIME();
Code:
0: aload_0
1: getfield #26; //Field FORMATED_TIME:Ljava/lang/String;
4: areturn
So I get an additional class ending on $ which has a singleton instance called MOUDLE$. And there is still the inspector. So the access to the variable inside a jsp becomes:
final String formatedTime = (String) request.getAttribute (….KommenControler$.MODULE$.FORMATED_TIME ());
This works as expected and I personally can live with it but how am I going to explain that to the team?
Of course if there is a simpler way I like to hear of it.

Categories