Objet vs octet [0] comme verrou
-
22-09-2019 - |
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.
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
.