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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top