我之前评论过这个问题(“为什么 java.lang.Object 不是抽象的?”),说我听说使用 abyte[0]
作为锁比使用java.lang.Object
. 我确定我在某处读过这篇文章,但我不记得在哪里:有人知道这是否真的是真的吗?
我怀疑这是由于实例化byte[0]
需要的字节码比 略少Object
,尽管有人指出byte[0]
需要额外的存储空间才能存储长度字段,因此听起来这可能会抵消任何好处。
我之前评论过这个问题(“为什么 java.lang.Object 不是抽象的?”),说我听说使用 abyte[0]
作为锁比使用java.lang.Object
. 我确定我在某处读过这篇文章,但我不记得在哪里:有人知道这是否真的是真的吗?
我怀疑这是由于实例化byte[0]
需要的字节码比 略少Object
,尽管有人指出byte[0]
需要额外的存储空间才能存储长度字段,因此听起来这可能会抵消任何好处。
使用 java.lang.instrument.Instrumentation 检查大小:
对象使用 8 个字节,byte[0] 需要 16 个字节。(不确定大小是否以字节为单位,未记录)。
我也有时间创建一个对象和一个字节[0](2 次):对象是赢家。
(所有测试都在 DELL 笔记本电脑、Intel 2GHz、Windos XP 上运行)
client
虚拟机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
虚拟机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();
}
}
}
Timer *用于ThreadMXBean
获取时间。
* Timer 是我为计时而制作的一个类,它不是Java Timer 的一个。
我很好奇来测试它。源代码:
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
在您甚至可以测量总时间之前,您可能必须开始使用您的循环索引。
根据 Java Language Spec,“所有类和数组类型都继承类 Object 的方法”,所以我不知道 byte[0] 可以如何提高效率。
对于规范的第一版似乎也是如此:“数组类型的超类被认为是对象”。
恕我直言,使用数组更有可能使读者感到困惑。
创建更少的对象比创建更多的对象更有效,所以如果它确实创建了足够重要的对象,那么你创建的对象太多了。
在 Java 中使用空数组作为锁对象的模式与性能关系不大。
空数组(甚至new Object[0]
)更可取,因为它们是可序列化的。通过使用new Object()
,您放弃了自动序列化。
我习惯了(从不关心性能):
private final Object lock = new Object[0];
原始数组需要更少的字节码来创建,所以可能new byte[0]
会“更好”。
你的问题提到了“效率”,但没有说你追求什么样的效率。迄今为止的答案与对象的大小有关,但是在任一表示中取消引用和使用内部锁的运行时成本应该是相同的。
您还可以比较使用内部锁与java.util.concurrent.locks.ReentrantLock
显式使用或您自己编写的锁的开销AbstractQueuedSynchronizer
。您是否可以容忍对单独分配的对象的额外引用需要更多关于您的问题的详细信息来评估,但鉴于您已经在考虑byte
数组,您必须考虑使用与您的this
引用不同的内在锁。