Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

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".

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.

The pattern of using an empty array in Java as a lock object has little to do with performance.

Empty arrays (even new Object[0]) are preferable because they are serializable. By using new Object() you're giving up automatic serialization.

I got used to doing (never caring about performance):

private final Object lock = new Object[0];

Primitive arrays take less bytecode to create, so maybe new byte[0] would be "better".

See: Is it okay to to make the lock transient for a Serializable class?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top