Question

Je dit plus tôt sur cette question ( "Pourquoi java. lang.Object n'est pas abstraite? ») indiquant que je l'avais entendu dire que l'aide d'un byte[0] comme verrou était légèrement plus efficace que l'utilisation d'un java.lang.Object. Je suis sûr que je l'ai lu quelque part, mais je ne me souviens pas où:? Est-ce que quelqu'un sait si cela est effectivement vrai

Je pense qu'il est dû à l'instanciation de byte[0] nécessitant un peu moins bytecode que Object, bien qu'il ait été signalé que byte[0] nécessite un stockage supplémentaire pour stocker le champ de longueur et il semble que cela pourrait annuler tout avantage.

Était-ce utile?

La solution

Je suis assez curieux de le tester. Code source:

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

Vous avez raison en ce que le code binaire est plus court pour les tableaux, parce que la création de tableau a sa propre machine virtuelle Java opcode. Mais qu'est ce que ça veut dire? Rien, vraiment. Il est une machine virtuelle, donc il n'y a absolument aucune garantie que moins d'instructions de bytecode moins de travail pour la CPU physique. Nous pourrions commencer le profilage bien sûr, mais ce serait tout à fait inutile. S'il y a une différence du tout, peu importe de quelle manière, il ne sera jamais jamais d'importance. Création d'objets est incroyablement rapide de nos jours. Vous auriez probablement commencer à utiliser long pour votre index de boucle avant même de mesurer le temps total.

Autres conseils

Utilisation java.lang.instrument.Instrumentation pour vérifier les tailles:
Objet utilise 8 octets, octet [0] a besoin de 16 octets. (Pas sûr si la taille est en octets, et non documenté).

J'ai aussi eu le temps de créer un objet et un octet [0] (2 fois): Object est le gagnant.

(tous les tests exécutés sur un ordinateur portable DELL, Intel 2GHz, Windos XP)

Utilisation de la machine virtuelle 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]

Utilisation de la machine virtuelle 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]

Je resterai avec new Object(), non seulement en raison de la lisibilité :-)

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

Minuteur * utilise ThreadMXBean pour obtenir les temps.

* Timer classe I fait pour timming, il est pas l'un des de Java minuterie.

D'après la langage Java Spec , « toutes les classes et le réseau types héritent les méthodes de la classe Object », donc je ne sais pas comment l'octet [0] parvenais à être plus efficace.

Cela semble être vrai pour le premier édition de la spécification aussi bien. « la superclasse d'un type de tableau est considéré comme objet »

L'utilisation d'un tableau est plus susceptible de confondre le lecteur à mon humble avis.

Création d'objets moins est plus efficace que de créer plus, si elle a jamais fait de créer suffisamment d'objets qu'il importait, vous créez un trop grand nombre.

Le motif de l'utilisation d'un tableau vide en Java comme un objet de verrouillage a peu à voir avec la performance.

tableaux vides (même de new Object[0]) sont préférables parce qu'ils sont sérialisable. En utilisant new Object() vous vous cédez sérialisation automatique.

Je suis habitué à faire (soin jamais à la performance):

private final Object lock = new Object[0];

tableaux primitifs prennent moins bytecode pour créer, si new byte[0] serait peut-être « mieux ».

Voir: Est-il correct de faire le transitoire de verrouillage pour une classe Serializable

Votre question mentionne « l'efficacité », mais ne dit pas ce genre que vous êtes après l'efficacité. Les réponses concernent jusqu'ici la Taille des objets, mais les coûts d'exécution de déréférencement et en utilisant le verrou intrinsèque soit la représentation doit être identique.

Vous pouvez également comparer les frais généraux de l'utilisation de verrous intrinsèques à l'aide de java.util.concurrent.locks.ReentrantLock explicitement ou que vous vous écrivez au-dessus AbstractQueuedSynchronizer . Que vous pouvez tolérer une référence supplémentaire à un objet séparément alloué nécessite plus de détails sur votre problème à évaluer, mais étant donné que vous envisagez déjà des tableaux de byte, vous devez être envisagez d'utiliser un verrou intrinsèque distincte de votre référence de this.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top