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.