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.myLong
tol2
, or in other words:l2
now 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 ofLong
and assigns the reference of that new instance tol2
, or in other words:l2
now 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.