J'ai fait quelques recherches. Il n'y a aucun moyen légal de créer un tableau non initialisé en Java. Même JNI NewXxxArray crée des tableaux initialisés. Il est donc impossible de connaître exactement le coût de la mise à zéro des tableaux. Néanmoins, j'ai fait quelques mesures :
1) Création de tableaux de 1000 octets avec des tailles de tableaux différentes
long t0 = System.currentTimeMillis();
for(int i = 0; i < 1000; i++) {
// byte[] a1 = new byte[1];
byte[] a1 = new byte[1000000];
}
System.out.println(System.currentTimeMillis() - t0);
Sur mon PC, il donne < 1ms pour l'octet [1] et ~500 ms pour l'octet [1000000]. Cela me semble impressionnant.
2) Nous n'avons pas de méthode rapide (native) dans le JDK pour remplir les tableaux, Arrays.fill est trop lent, alors voyons au moins combien il faut pour copier 1000 tableaux de taille 1,000,000 avec la méthode native System.arraycopy.
byte[] a1 = new byte[1000000];
byte[] a2 = new byte[1000000];
for(int i = 0; i < 1000; i++) {
System.arraycopy(a1, 0, a2, 0, 1000000);
}
Il est de 700 ms.
Cela me donne des raisons de croire que a) la création de tableaux longs est coûteuse b) elle semble être coûteuse à cause de l'initialisation inutile.
3) Prenons sun.misc.Unsafe http://www.javasourcecode.org/html/open-source/jdk/jdk-6u23/sun/misc/Unsafe.html . Il est protégé de l'usage extérieur, mais pas trop.
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe)f.get(null);
Voici le coût du test d'allocation de mémoire
for(int i = 0; i < 1000; i++) {
long m = u.allocateMemory(1000000);
}
Cela prend < 1 ms, si vous vous souvenez, pour new byte[1000000] cela a pris 500ms.
4) Unsafe n'a pas de méthodes directes pour travailler avec des tableaux. Il a besoin de connaître les champs de classe, mais la réflexion ne montre aucun champ dans un tableau. Il n'y a pas beaucoup d'informations sur les internes des tableaux, je suppose que c'est spécifique à la JVM / plateforme. Néanmoins, il s'agit, comme tout autre objet Java, d'un en-tête + des champs. Sur mon PC/JVM, cela ressemble à
header - 8 bytes
int length - 4 bytes
long bufferAddress - 8 bytes
Maintenant, en utilisant Unsafe, je vais créer byte[10], allouer un tampon mémoire de 10 octets et l'utiliser comme éléments de mon tableau :
byte[] a = new byte[10];
System.out.println(Arrays.toString(a));
long mem = unsafe.allocateMemory(10);
unsafe.putLong(a, 12, mem);
System.out.println(Arrays.toString(a));
il imprime
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[8, 15, -114, 24, 0, 0, 0, 0, 0, 0]
Vous pouvez voir que les données du tableau ne sont pas initialisées.
Maintenant je vais changer la longueur de notre tableau (bien qu'il pointe toujours vers une mémoire de 10 octets)
unsafe.putInt(a, 8, 1000000);
System.out.println(a.length);
il affiche 1000000. C'était juste pour prouver que l'idée fonctionne.
Maintenant, test de performance. Je vais créer un tableau d'octets vide a1, allouer un tampon de 1000000 octets, affecter ce tampon à a1 et définir a1.length = 10000000.
long t0 = System.currentTimeMillis();
for(int i = 0; i < 1000; i++) {
byte[] a1 = new byte[0];
long mem1 = unsafe.allocateMemory(1000000);
unsafe.putLong(a1, 12, mem);
unsafe.putInt(a1, 8, 1000000);
}
System.out.println(System.currentTimeMillis() - t0);
cela prend 10ms.
5) Il y a malloc et alloc en C++, malloc alloue juste un bloc de mémoire, calloc l'initialise aussi avec des zéros.
cpp
...
JNIEXPORT void JNICALL Java_Test_malloc(JNIEnv *env, jobject obj, jint n) {
malloc(n);
}
java
private native static void malloc(int n);
for (int i = 0; i < 500; i++) {
malloc(1000000);
}
résultats malloc - 78 ms ; calloc - 468 ms
Conclusions
- Il semble que la création de tableaux Java soit lente à cause de la mise à zéro inutile des éléments.
-
Nous ne pouvons pas le changer, mais Oracle le peut. Il n'est pas nécessaire de changer quoi que ce soit dans JLS, il suffit d'ajouter des méthodes natives à java.lang.reflect.Array comme
Public static native xxx[] newUninitialziedXxxArray(int size) ;
pour tous les types numériques primitifs (octet - double) et le type char. Il peut être utilisé partout dans le JDK, comme dans java.util.Arrays.
public static int[] copyOf(int[] original, int newLength) {
int[] copy = Array.newUninitializedIntArray(newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
...
ou java.lang.String
public String concat(String str) {
...
char[] buf = Array.newUninitializedCharArray(count + otherLen);
getChars(0, count, buf, 0);
...