Pour garder les choses en perspective, envisager d'exécuter ce code avec -Xmx64m
:
static long sum;
public static void main(String[] args) {
System.out.println("Warming up...");
for (int i = 0; i < 100_000; i++) test(1);
System.out.println("Main call");
test(5_500_000);
System.out.println("Sum: " + sum);
}
static void test(int size) {
// for (int i = 0; i < 1; i++)
{
long[] a2 = new long[size];
sum += a2.length;
}
long[] a1 = new long[size];
sum += a1.length;
}
Selon si vous ne l'échauffement ou l'ignorer, elle va se faire sauter ou ne pas sauter. C'est parce que le JITted code correctement null
s le var, alors que le code n'est pas interprété. Les deux comportements sont acceptables en vertu de la Java Langage de Spécification, ce qui signifie que vous êtes à la merci de la JVM avec cette.
Testé avec Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
sur OS X.
Le Bytecode de l'analyse
Regarder le bytecode avec l' for
boucle (code simple, sans l' sum
variable):
static void test(int);
Code:
0: iconst_0
1: istore_1
2: goto 12
5: iload_0
6: newarray long
8: astore_2
9: iinc 1, 1
12: iload_1
13: iconst_1
14: if_icmplt 5
17: iload_0
18: newarray long
20: astore_1
21: return
et sans:
static void test(int);
Code:
0: iload_0
1: newarray long
3: astore_1
4: iload_0
5: newarray long
7: astore_1
8: return
Pas explicite null
ing dans les deux cas, mais notez que dans le pas-par exemple le même emplacement de mémoire est effectivement réutilisé, en contraste avec la par exemple. Ce serait, si tout, conduire à l'attente face au comportement observé.
Un twist...
Basé sur ce que nous avons appris à partir du bytecode, l'essayer:
public static void main(String[] args) {
{
long[] a1 = new long[5_000_000];
}
long[] a2 = new long[0];
long[] a3 = new long[5_000_000];
}
Pas de OOME jeté. Commentaire de la déclaration de l' a2
, et il est de retour. Nous allouer plus, mais occupent moins? Regardez le pseudo-code:
public static void main(java.lang.String[]);
Code:
0: ldc #16 // int 5000000
2: istore_1
3: ldc #16 // int 5000000
5: newarray long
7: astore_2
8: iconst_0
9: newarray long
11: astore_2
12: ldc #16 // int 5000000
14: newarray long
16: astore_3
17: return
L'emplacement 2, utilisé pour a1
, est réutilisé pour l' a2
. Le même est vrai pour des OP de code, mais maintenant nous écraser l'emplacement avec une référence à une inoffensive zéro-longueur de la matrice, et d'utiliser un autre emplacement pour stocker la référence à notre grand tableau.
Pour résumer...
Le champ d'application de l'accessibilité d'un objet affecté à une variable locale n'est pas spécifié par le Langage Java Spécification et la JVM spec seulement dit que le "cadre" avec des variables locales est détruit dans son ensemble sur la méthode de l'achèvement. Par conséquent, tous les comportements que nous avons observés sont par le livre. L' invisible, de l'état d'un objet (mentionné dans le document lié par keppil) est juste une façon de décrire ce qui se passe dans certaines implémentations et dans certaines circonstances, mais n'est en aucune façon tout type de mode canonique.