对象与 byte[0] 作为锁

发布于 2024-08-19 01:47:51 字数 387 浏览 8 评论 0原文

我之前评论过这个问题(“为什么java.lang.Object不是抽象的?”)指出我听说使用 byte[0] 作为锁比使用 java.lang.Object 稍微更有效。我确信我在某处读过这篇文章,但我不记得在哪里:有谁知道这是否属实?

我怀疑这是由于 byte[0] 的实例化需要比 Object 稍少的字节代码,尽管有人指出 byte[0] > 需要额外的存储空间才能存储长度字段,因此听起来这可能会抵消任何好处。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

时光清浅 2024-08-26 01:47:51

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

我还花时间创建了一个 Object 和一个 byte[0] (2 次):Object 是赢家。

(所有测试均在 DELL 笔记本电脑、Intel 2GHz、Windos XP 上运行)

使用客户端 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]

使用服务器 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(),不仅仅是因为可读性:-)

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* 使用 ThreadMXBean 来获取时间。

* Timer 是我为计时而创建的类,它不是 Java 计时器之一。

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.

方觉久 2024-08-26 01:47:51

我很好奇,想测试一下。源代码:

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 作为循环索引。

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.

氛圍 2024-08-26 01:47:51

根据
Java 语言规范, "所有类和数组类型继承了 Object 类的方法”,所以我不知道 byte[0] 如何能够变得更高效。

这对于 第一版来说似乎是正确的规范以及:“数组类型的超类被认为是对象”。

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

稚然 2024-08-26 01:47:51

恕我直言,使用数组更有可能让读者感到困惑。

创建较少的对象比创建更多的对象更有效,因此,如果它确实创建了足够多的重要对象,那么您就创建了太多对象。

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.

扬花落满肩 2024-08-26 01:47:51

Java中使用空数组作为锁对象的模式与性能关系不大。

空数组(甚至new Object[0])更可取,因为它们是可序列化的。通过使用new Object(),您将放弃自动序列化。

我习惯了这样做(从不关心性能):

private final Object lock = new Object[0];

原始数组需要更少的字节码来创建,所以也许 new byte[0] 会“更好”。

请参阅:可以为可序列化类设置瞬态锁吗?

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?

狂之美人 2024-08-26 01:47:51

你的问题提到了“效率”,但没有说你追求什么样的效率。到目前为止,答案涉及对象的大小,但是在任一表示中取消引用和使用内在锁的运行时成本应该是相同的。

您还可以比较使用内在锁与使用 java.util.concurrent.locks.ReentrantLock 显式地或您自己编写的 AbstractQueuedSynchronizer。您是否可以容忍对单独分配的对象的额外引用需要更详细的问题来评估,但考虑到您已经在考虑字节数组,您必须考虑使用与您的this参考。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文