Objeto vs byte [0] como trava
-
22-09-2019 - |
Pergunta
Eu comentei mais cedo essa questão ("Por que java.lang.Object não é abstrato?") Afirmando que eu tinha ouvido falar que usando um byte[0]
como uma trava era um pouco mais eficiente do que usar um java.lang.Object
. Tenho certeza de que li isso em algum lugar, mas não me lembro de onde: alguém sabe se isso é realmente verdade?
Eu suspeito que seja devido à instanciação de byte[0]
exigindo um pouco menos de código de byte do que Object
, embora tenha sido apontado que byte[0]
Requer armazenamento adicional para armazenar o campo de comprimento e, portanto, parece que isso pode negar qualquer benefício.
Solução
Fiquei curioso o suficiente para testá -lo. Código fonte:
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
Então, você está certo, pois o código de bytes é mais curto para matrizes, porque a criação de matrizes tem seu próprio código OPCOD. Mas o que isso significa? Nada realmente. É uma máquina virtual, portanto, não há absolutamente nenhuma garantia de que menos instruções de bytecode significam menos trabalho para a CPU física real. Poderíamos começar a perfilar, é claro, mas isso seria inútil. Se houver uma diferença, não importa de que maneira, isso nunca importará. A criação de objetos é incrivelmente rápida hoje em dia. Você provavelmente teria que começar a usar long
Para o seu índice de loop antes que você possa medir o tempo total.
Outras dicas
Usando java.lang.instrument.instrumentation para verificar os tamanhos:
O objeto usa 8 bytes, byte [0] precisa de 16 bytes. (Não tenho certeza se o tamanho está em bytes, não documentado).
Também tive tempo para criar um objeto e um byte [0] (2 vezes): o objeto é o vencedor.
(Todos os testes são executados em um laptop Dell, Intel 2GHz, Windos XP)
Usando o 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]
Usando o 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]
Eu vou ficar com new Object()
, não apenas por causa da legibilidade :-)
O código
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();
}
}
}
Cronômetro* usos ThreadMXBean
para obter os tempos.
* Timer é uma aula que eu fiz para tocar, é não Um dos timer Java.
De acordo comJava Language Spec, "Todos os tipos de classe e matriz herdam os métodos do objeto de classe", então não sei como o byte [0] conseguiu ser mais eficiente.
Isso parece ser verdade para o Primeira edição da especificação Além disso: "A superclasse de um tipo de matriz é considerada objeto".
Usar uma matriz é mais provável que confunda o leitor IMHO.
Criar menos objetos é mais eficiente do que criar mais; portanto, se ele criou objetos suficientes que importava, você está criando muitos.
O padrão de usar uma matriz vazia em Java como um objeto de bloqueio tem pouco a ver com o desempenho.
Matrizes vazias (até mesmo new Object[0]
) são preferíveis porque são serializáveis. Usando new Object()
Você está desistindo da serialização automática.
Eu me acostumei a fazer (nunca me importo com o desempenho):
private final Object lock = new Object[0];
Matrizes primitivas pegam menos bytecode para criar, então talvez new byte[0]
seria melhor".
Ver: Não há problema em fazer a trava transitória para uma classe serializável?
Sua pergunta menciona "eficiência", mas não diz que tipo de eficiência você procura. As respostas até agora preocupam o Tamanho dos objetos, mas os custos de tempo de execução de desreferência e uso do bloqueio intrínseco em qualquer representação devem ser os mesmos.
Você também pode comparar a sobrecarga de usar bloqueios intrínsecos para usar java.util.concurrent.locks.ReentrantLock
explicitamente ou que você se escreve no topo AbstractQueuedSynchronizer
. Se você pode tolerar uma referência adicional a um objeto alocado separadamente, requer mais detalhes sobre o seu problema para avaliar, mas já que você já está considerando byte
Matrizes, você deve estar pensando em usar uma trava intrínseca distinta de seu this
referência.