Object vs byte[0] as lock
Asked Answered
R

6

6

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.

Rimmer answered 22/1, 2010 at 20:54 Comment(5)
You (and everyone else asking "which is faster" questions) are all barking up the wrong tree. No performance code-red in the history of the universe has ever been resolved by changing locks from Objects to zero-length byte arrays or vice versa. Wondering about the difference is a pure, 100% waste of your time!Millen
Kevin: Thanks - I'm well aware of that and am merely curious about the inner workings of the JVM. I agree with you in that it's generally a waste time making micro-optimisations.Rimmer
You forget that byte[0] is an Object (and doesn't need to be boxed) and so would have the overhead of an object plus the overhead of the array.Lout
@configurator: I disagree: An array doesn't have the overhead of an object plus an array - If you view Michael's answer you'll see that there's a dedicated opcode for array creation but the "invokespecial" opcode (i.e. constructor) is not called.Rimmer
Sorry, I meant the memory overhead. I will note that Object's constructor is empty though so it would probably be optimized away by the JIT.Lout
L
13

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.

Lovellalovelock answered 22/1, 2010 at 21:13 Comment(0)
A
15

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.

Ansel answered 23/1, 2010 at 21:31 Comment(6)
Sizes are in bytes; nothing else makes sense here. Are you using a 64-bit VM?Lout
@Lout - 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! (??)Ansel
I was asking if you're using a 64-bit JVM because I was surprised arrays take 8 bytes more than objects.Lout
For comparison, in C# the numbers are the same (surprisingly enough): 12 bytes for each an object or byte[0].Lout
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!Ansel
An empty byte array will take 16 bytes on 32 bit JVM. 8 (for object header) + 4 (for length, int) + 4 (padding).Hengelo
L
13

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.

Lovellalovelock answered 22/1, 2010 at 21:13 Comment(0)
C
5

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

Cambrel answered 22/1, 2010 at 21:9 Comment(0)
L
3

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.

Larrisa answered 23/1, 2010 at 19:13 Comment(1)
readability should always trump performance until it becomes a real, measureable problem.Dither
P
3

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?

Propellant answered 15/11, 2013 at 20:23 Comment(0)
C
1

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.

Cranston answered 23/1, 2010 at 23:46 Comment(1)
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.Ansel

© 2022 - 2024 — McMap. All rights reserved.