views:

219

answers:

5

I commented earlier on this question ("Why java.lang.Object is not abstract?") stating that I'd heard that using a byte[0] as a lock was slightly more efficient than using an java.lang.Object. I'm sure I've read this somewhere but I can't recall where: Does anyone know if this is actually true?

I suspect it's due to the instantiation of byte[0] requiring slightly less byte code than Object, although it was pointed out that byte[0] requires additional storage in order to store the length field and so it sounds like this might negate any benefit.

+3  A: 

According to the Java Language Spec, "all class and array types inherit the methods of class Object", so I don't know how byte[0] could manage to be more efficient.

That seems to be true for the first edition of the spec as well: "The superclass of an array type is considered to be Object".

Nathan Hughes
+10  A: 

I got curious enough to test it. Sourcecode:

public class Test {
    public static Object returnObject() {
        return new Object();
    }

    public static byte[] returnArray(){
        return new byte[0];
    }
}

Bytecode:

public static java.lang.Object returnObject();
  Code:
   0:   new     #2; //class java/lang/Object
   3:   dup
   4:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   7:   areturn

public static byte[] returnArray();
  Code:
   0:   iconst_0
   1:   newarray byte
   3:   areturn

So you're right in that the byte code is shorter for arrays, because array creation has its own JVM opcode. But what does that mean? Nothing, really. It's a virtual machine, so there's absolutely no guarantee that fewer bytecode instructions mean less work for the actual physical CPU. We could start profiling of course, but that would be quite pointless. If there is a difference at all, no matter which way, it will never ever matter. Object creation is incredibly fast nowadays. You'd probably have to start using long for your loop index before you can even measure the total time.

Michael Borgwardt
+1  A: 

Using an array is more likely to confuse the reader IMHO.

Creating less objects is more efficient than creating more, so if it ever did create enough objects that it mattered, you are creating too many.

Peter Lawrey
+5  A: 

Using java.lang.instrument.Instrumentation to check the sizes:
Object uses 8 bytes, byte[0] needs 16 bytes. (not sure if the size is in bytes, not documented).

I also got the time to create an Object and a byte[0] (2 times): Object is the winner.

(all tests run on a DELL laptop, Intel 2GHz, Windos XP)

Using the client VM

java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Client VM (build 14.2-b01, mixed mode)

an implementation-specific approximation of the amount of storage
Object  = 8
byte[0] = 16

time to create 1000000000 instances
Object:  elapsed=11,140   cpu=9,766    user=9,703    [seconds]
byte[0]: elapsed=18,248   cpu=15,672   user=15,594   [seconds]

time to create 1000000000 instances
Object:  elapsed=11,135   cpu=9,828    user=9,750    [seconds]
byte[0]: elapsed=18,271   cpu=15,547   user=15,469   [seconds]

Using the server VM

java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Server VM (build 14.2-b01, mixed mode)

an implementation-specific approximation of the amount of storage
Object  = 8
byte[0] = 16

time to create 1000000000 instances
Object:  elapsed=8,441    cpu=7,156    user=7,125    [seconds]
byte[0]: elapsed=11,237   cpu=8,609    user=8,500    [seconds]

time to create 1000000000 instances
Object:  elapsed=8,501    cpu=7,234    user=7,156    [seconds]
byte[0]: elapsed=11,023   cpu=8,688    user=8,641    [seconds]

I will stay with new Object(), not only because of readability :-)

The Code

public class ObjectArrayCompare {

  private static Object o;

  public static void main(String[] args) {
    Instrumentation instr = InstrumentationAgent.getInstrumentation();
    if (instr == null) {
        System.err.println("No Instrumentation, use \"-javaagent:Instrumentation.jar\"");
        return;
    }
    System.out.println();
    System.out.println("an implementation-specific approximation of the amount of storage");
    System.out.println("Object  = " + instr.getObjectSize(new Object()));
    System.out.println("byte[0] = " + instr.getObjectSize(new byte[0]));
    System.out.println();

    final int MAX = (int) 1.0e9;
    Timer timer;
    Times times;

    for (int j = 0; j < 2; j++) {
      System.out.println("time to create " + MAX + " instances"); 
      timer = new Timer();
      for (int i = 0; i < MAX; i++) {
        o = new Object();
      }
      times = timer.times();
      System.out.println("Object:  " + times);

      timer = new Timer();
      for (int i = 0; i < MAX; i++) {
        o = new byte[0];
      }
      times = timer.times();
      System.out.println("byte[0]: " + times);

      System.out.println();
    }
  }
}

Timer* uses ThreadMXBean to get the times.

* Timer is a class I made for timming, it is not one of the Java Timer's.

Carlos Heuberger
+1: Interesting results.
Adamski
Sizes are in bytes; nothing else makes sense here. Are you using a 64-bit VM?
configurator
@configurator - I know that, but I (mostly) prefer to trust what is documented instead of what makes sense (e.g. January == 0) when programing... That's why I wrote bytes, but it's not documented, so I wrote the note (not being sure about it). Anyway it is not that important since it is only "an implementation-specific approximation of the amount of storage consumed" (as documented). No I'm not using a 64-bit VM! (??)
Carlos Heuberger
I was asking if you're using a 64-bit JVM because I was surprised arrays take 8 bytes more than objects.
configurator
For comparison, in C# the numbers are the same (surprisingly enough): 12 bytes for each an `object` or `byte[0]`.
configurator
In Java an Array is not just a pointer to memory, it's a normal Object, that is, it is an instance of a subclass of Object. Maybe the array instance also carries the length of the array... testing a bit more, up to `byte[4]` uses 16 bytes; `byte[5]` till `byte[12]` uses 24 bytes. Seams like the empty byte array uses 12 bytes!
Carlos Heuberger
+1  A: 

Your question mentions "efficiency", but doesn't say what kind of efficiency you're after. The answers thus far concern the size of the objects, but the run-time costs of dereferencing and using the intrinsic lock in either representation should be the same.

You can also compare the overhead of using intrinsic locks to using java.util.concurrent.locks.ReentrantLock explicitly or one you write yourself atop AbstractQueuedSynchronizer. Whether you can tolerate an additional reference to a separately-allocated object requires more detail on your problem to assess, but given that you're already considering byte arrays, you must be considering using an intrinsic lock distinct from your this reference.

seh
good point! Neither size nor creation time (code size) should be of concern, and I can't imagine any difference using Object or byte[0] for locking.
Carlos Heuberger