giudaballerino
giudaballerino

Reputation: 65

Array handling by Java Virtual Machine

I have a silly question for you all. Given the following Java Code

public void funct(String a) {
    byte[] bytearr;

    bytearr = new byte[a.getBytes().length];
    bytearr = a.getBytes();
}

Does the new call change anything? In particular, is the code processed in a different way from

public void funct(String a) {
    byte[] bytearr;

    bytearr = a.getBytes();
}

I am asking because, when executed, both present the same results and I can't get if

More in general, any suggestion to observe memory allocations/magic behaviour behind JVM would be very appreciated.

Thanks!

Upvotes: 1

Views: 295

Answers (3)

Andronicus
Andronicus

Reputation: 26056

They are different, let us take a look at the bytecode:

1. version:

   L0
    LINENUMBER 9 L0
    ALOAD 0
    ICONST_0
    AALOAD
    INVOKEVIRTUAL java/lang/String.getBytes ()[B
    ARRAYLENGTH
    NEWARRAY T_BYTE
    ASTORE 1
   L1
    LINENUMBER 10 L1
    ALOAD 0
    ICONST_0
    AALOAD
    INVOKEVIRTUAL java/lang/String.getBytes ()[B
    ASTORE 1
   L2
    LINENUMBER 11 L2
    RETURN
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
    LOCALVARIABLE bytearr [B L1 L3 1
    MAXSTACK = 2
    MAXLOCALS = 2

2. version:

   L0
    LINENUMBER 9 L0
    ALOAD 0
    ICONST_0
    AALOAD
    INVOKEVIRTUAL java/lang/String.getBytes ()[B
    ASTORE 1
   L1
    LINENUMBER 10 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    LOCALVARIABLE bytearr [B L1 L2 1
    MAXSTACK = 2
    MAXLOCALS = 2

The difference is in those instructions comming from the extra line:

LINENUMBER 9 L0
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ARRAYLENGTH
NEWARRAY T_BYTE
ASTORE 1

This introduces additional overhead (getBytes being called twice, more instructions). By invoking bytearr = a.getBytes() jvm already handles the size of the array being stored.

But since the first initialization is redundant, it will probably be optimized by the compiler at some point (after enough runs). Still, there is no need for additional instructions and less readable code.

Upvotes: 1

Florian Gutmann
Florian Gutmann

Reputation: 2756

The end result of both blocks of code is the same. The first version does some uneccessary extra work.

Version 1

Lets go through the first version step by step.

Step 1)

bytearr = new byte[a.getBytes().length];

bytearr assignment

A new empty array is created and bytearr points to it.

Step 2)

bytearr = a.getBytes();

overwriting the reference of bytearr

a.getBytes() creates a second array. bytearr is updated to point to that new array. The old (blue) array is now garbage because no reference to it is held anymore. The garbage collector of the JVM will free the memory occupied by it eventually.

Version 2

bytearr = a.getBytes();

direct assignment

bytearr is directly pointing to the array created by a.getBytes(). The end result is the same, but this version is more efficient.

More details about version 1

Actually version 1 creates even one more array during the call a.getBytes().length. The initial explaination skipped over it to keep it simple.

So actually there are 3 array instances involved in version 1:

  1. The one created during the call a.getBytes().length to measure the size (not shown in the diagrams for simplicty).
  2. The empty one created by new byte[...] with that size (the blue one).
  3. The one created during the second call to a.getBytes() (the green one).

Upvotes: 2

AtomicPixel
AtomicPixel

Reputation: 49

The only difference is that you're creating an extra, unnecessary object in the first snippet. If you want to prove it, you could point a second array reference at the first, print out their equivalence, then call the string.getBytes() on the first array and test their equivalence again:

        String aString = new String("something");

        byte[] byteArray1 = new byte[aString.length()];
        byte[] byteArray2 = byteArray1;
        System.out.println(byteArray1==byteArray2);

        byteArray1 = aString.getBytes();
        System.out.println(byteArray1==byteArray2);

It prints out true before the aString.getBytes() because the references are pointing at the same object. Then aString.getBytes() returns a new byte[] array, so byteArray1 no longer == byteArray2. Does this answer the question or were you looking for something else?

Upvotes: 2

Related Questions