From what I understand, Java has boxed (heap) and unboxed (stack) variables, and if I assign a boxed type to an unboxed type or vice-versa there is a(n) (un)boxing cost involved.
Is unboxing cheaper than allocating a new boxed object? And do boxed objects support COW if used readonly?
class MyClass {
public String myString;
public Long myLong;
public MyClass(String s, Long l) {
myString = s;
myLong = l;
}
}
MyClass cls = new MyClass("A string", 5L);
Long l2 = cls.myLong; // does l2 and cls.myLong share memory or is new memory allocated here?
if (l2 < 10) {
l2 += 5; // now l2 must have its own memory
}
Advertisement
Answer
Wrapping types are not on the heap because they box a primitive type but because they are objects. Primitive types can live on the stack, as local variables, or on the heap, as member variables.
What happens in your example is:
Long l2 = cls.myLong;assigns the reference of the object behindclass.myLongtol2, or in other words:l2now references the same object asclass.myLong.l2 += 5;unboxes the object referenced byl2, adds 5 to the value, wraps the result in a new instance ofLongand assigns the reference of that new instance tol2, or in other words:l2now references a new object, which wraps the result.
Imagine this example code:
public class Test {
public static void main(String[] args) {
Long long1 = Long.valueOf(1);
Long long2 = long1 + 1;
}
}
Compile it (javac Test.java) and see the bytecode (javap -c Test):
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: lconst_1
1: invokestatic #2 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
4: astore_1
5: aload_1
6: invokevirtual #3 // Method java/lang/Long.longValue:()J
9: lconst_1
10: ladd
11: invokestatic #2 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
14: astore_2
15: return
}
You can see that the addition to long1 expands to a call of longValue, the “unboxing”, and valueOf, the “boxing”. Like in your example, a new instance is created.
Is unboxing cheaper than allocating a new boxed object?
As we have seen, unboxing is more or less equivalent to calling a method. Creating a new instance includes more steps, e.g. additionally allocating memory.
And do boxed objects support COW if used readonly?
These classes are immutable so copy-on-write is what they always do.