对象vs字节[0]为锁

时间:2010-01-22 20:54:55

标签: java multithreading synchronization locking synchronized

我之前在this question上评论过(“为什么java.lang.Object不是抽象的?”),说我听说使用byte[0]作为锁定比使用java.lang.Object。我确定我已经在某个地方读过这篇文章,但我不记得在哪里:有谁知道这是否真的如此?

我怀疑这是由于byte[0]的实例化需要的字节代码略少于Object,尽管有人指出byte[0]需要额外的存储才能存储长度字段和所以听起来这可能会否定任何好处。

6 个答案:

答案 0 :(得分:14)

使用java.lang.instrument.Instrumentation检查大小:
对象使用8个字节,byte [0]需要16个字节。 (不确定大小是否以字节为单位,未记录)。

我也有时间创建一个Object和一个byte [0](2次):Object是胜利者。

(所有测试均在戴尔笔记本电脑,英特尔2GHz,Windos XP上运行)

使用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]

使用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]

我会留在new Object(),不仅仅是因为可读性:-)

守则

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();
    }
  }
}

计时器 * 使用ThreadMXBean来获取时间。

* Timer是我修改的类,它是的Java Timer之一。

答案 1 :(得分:13)

我很好奇地测试它。源代码:

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

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

字节码:

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

所以你说对的是数组的字节代码更短,因为数组创建有自己的JVM操作码。但是,这是什么意思?真的没什么。它是一个虚拟机,因此绝对无法保证较少的字节码指令意味着实际物理CPU的工作量减少。我们当然可以开始进行剖析,但那将毫无意义。如果存在差异,无论采用哪种方式,都不会有任何影响。如今,对象创建速度非常快。您甚至可以开始使用long作为循环索引,然后才能测量总时间。

答案 2 :(得分:5)

根据 Java Language Spec,“所有类和数组类型都继承了Object类的方法”,所以我不知道byte [0]如何能够提高效率。

对于first edition of the spec来说似乎也是如此:“数组类型的超类被认为是对象”。

答案 3 :(得分:3)

使用数组更容易混淆读者恕我直言。

创建更少的对象比创建更多的对象更有效,所以如果创建了足够重要的对象,那么创建的对象太多了。

答案 4 :(得分:2)

在Java中使用空数组作为锁定对象的模式与性能无关。

空数组(偶数new Object[0])更可取,因为它们是可序列化的。使用new Object()即表示您放弃了自动序列化。

我已经习惯了(从不关心性能):

private final Object lock = new Object[0];

原始数组创建的字节码较少,因此new byte[0]可能会“更好”。

请参阅:Is it okay to to make the lock transient for a Serializable class?

答案 5 :(得分:1)

你的问题提到了“效率”,但没有说明你所追求的效率。到目前为止,答案涉及对象的 size ,但在任一表示中解除引用和使用内部锁的运行时成本应该相同。

您还可以将使用内在锁定的开销与明确使用java.util.concurrent.locks.ReentrantLock或在AbstractQueuedSynchronizer之上自行编写的开销进行比较。是否可以容忍对单独分配的对象的额外引用需要更多有关要评估的问题的详细信息,但鉴于您已经在考虑byte数组,您必须考虑使用与{{{1}不同的内部锁。 1}}参考。