1. La limite supérieure de l'allocation du tableau
La taille d'un tableau en Java est limitée car elle utilise le type int comme indice du tableau. Cela signifie que vous ne pouvez pas demander des tableaux qui dépassent la taille d'Integer.max_value (2 ^ 31-1). Cela ne signifie pas que la limite supérieure de votre application de mémoire est 2G. Vous pouvez demander une gamme de types plus grands. Par exemple:
La copie de code est la suivante:
final long [] ar = new long [Integer.max_value];
Cela allouera 16g -8 octets. n'est que des règles générales, l'allocation spécifique dépend de la situation réelle).
Malheureusement, en Java, en raison de la limitation de type des éléments du tableau, il sera plus difficile de faire fonctionner la mémoire. En ce qui concerne les tableaux de fonctionnement, ByteBuffer devrait être la classe la plus utile, qui fournit des méthodes pour lire et écrire différents types Java. Son inconvénient est que le type de tableau cible doit être l'octet [], ce qui signifie que le cache de mémoire maximum que vous allouez peut être 2G.
2. Utilisez tous les tableaux comme tableaux d'octets pour fonctionner
En supposant que la mémoire 2G est loin d'être suffisante pour nous maintenant, il est OK s'il est 16g. Nous avons attribué un long [], mais nous voulons le faire fonctionner comme un tableau d'octets. En Java, nous devons demander l'aide des programmeurs C - Sun.Misc.unsafe. Cette classe a deux ensembles de méthodes: GETN (objet, décalage), cette méthode consiste à obtenir une valeur du type spécifié à partir de la position où le décalage de l'objet est décalé et le renvoie. La méthode putn (objet, offset, valeur) consiste à écrire une valeur à la position du décalage de l'objet.
Malheureusement, ces méthodes ne peuvent obtenir ou définir que des valeurs d'un certain type. Si vous copiez des données à partir d'un tableau, vous avez également besoin d'une autre méthode de dangereuse, Copymemory (SrCObject, SrCoffset, DestObject, Destoffet, Count). Cela fonctionne de manière similaire à System.arrayCopy, mais il copie des octets plutôt que des éléments de tableau.
Pour accéder aux données d'un tableau via Sun.Misc.unsafe, vous avez besoin de deux choses:
1. Le décalage des données dans l'objet de tableau
2. Le décalage des éléments copiés dans les données du tableau
Comme les autres objets Java, Arrays a un en-tête d'objet, qui est stocké devant les données réelles. La longueur de cet en-tête peut être obtenue par la méthode dangere.ArrayBaseOffset (t []. Classe), où t est le type d'élément de tableau. La taille de l'élément de tableau peut être obtenue via la méthode dangere.ArrayIndexscale (t []. Classe). Cela signifie que si vous souhaitez accéder au nième élément de type T, votre décalage de décalage doit être ArrayOffset + N * Arrayscale.
Écrivons un exemple simple. Nous allouons un long tableau et mettons à jour quelques octets à l'intérieur. Nous mettons à jour le dernier élément à -1 (0xffffffffffffffffffffffffffffffffff), puis effacez tous les octets de cet élément un par un.
La copie de code est la suivante:
Final long [] ar = new long [1000];
index int final = ar.length - 1;
ar [index] = -1;
System.out.println ("avant Change =" + Long.tohexString (ar [index]));
pour (long i = 0; i <8; ++ i)
{
unsetafe.putByte (AR, LongarRayOffset + 8l * Index + i, (octet) 0);
System.out.println ("After Change: i =" + i + ", val =" + long.tohexstring (ar [index]));
}
Si vous souhaitez exécuter l'exemple ci-dessus, vous devez ajouter le bloc de code statique suivant à votre classe de test:
La copie de code est la suivante:
finale statique privée dangereuse dangereuse;
statique
{
essayer
{
Field Field = Unsetafe.class.getDeclaredField ("TheunSafe");
field.setAccessible (true);
disap = (danget) field.get (null);
}
Catch (exception e)
{
Jetez une nouvelle RuntimeException (E);
}
}
Final statique privé long longArrayoffset = danget.ArrayBaseOffset (Long []. Classe);
Le résultat de la sortie est:
La copie de code est la suivante:
Avant le changement = ffffffffffffffffffff
Après le changement: i = 0, val = ffffffffffffff00
Après le changement: i = 1, val = ffffffffffff0000
Après le changement: i = 2, val = ffffffffff000000
Après le changement: i = 3, val = ffffffff00000000
Après le changement: i = 4, val = ffffff00000000000
Après le changement: i = 5, Val = FFFF0000000000000
Après le changement: i = 6, Val = FF000000000000000
Après le changement: i = 7, val = 0
3. Attribution de la mémoire de Sun.Misc.unsafe
Comme mentionné ci-dessus, en Java pur, la taille de la mémoire que nous pouvons allouer est limitée. Cette restriction a été fixée dans la version initiale de Java, et à ce moment-là, les gens n'osaient pas partager plusieurs g de mémoire comme celle-ci. Mais maintenant, c'est l'ère des mégadonnées, et nous avons besoin de plus de mémoire. En Java, il y a deux façons d'obtenir plus de mémoire:
1. Allouer de nombreux petits morceaux de mémoire, puis les utiliser logiquement comme un grand morceau de mémoire continu.
2. Utilisez Sun.Misc.unsafe.AllCateMemory (Long) pour allouer la mémoire.
La première méthode est juste un peu plus intéressante du point de vue des algorithmes, alors jetons un œil à la deuxième méthode.
Sun.Misc.Unsafe fournit un ensemble de méthodes pour allouer, réaffecter et libérer la mémoire. Ils sont très similaires à la méthode Malloc / libre de C:
1.Long dangereux. ALLOCATEMEMMORY (Longue taille) - Allocier un élément de mémoire. Ce morceau de mémoire peut contenir des données indésirables (non effacées automatiquement). Si l'allocation échoue, une exception de java.lang.outofMemoryError sera lancée. Il renvoie une adresse mémoire non nulle (voir la description ci-dessous).
2.UNSAFE.ReallocateMemory (Adresse longue, taille longue) - Reparez-vous un morceau de mémoire et copiez les données de l'ancien tampon de mémoire (où l'adresse pointe vers) le bloc de mémoire nouvellement alloué. Si l'adresse est égale à 0, cette méthode a le même effet que l'allocatémémoire. Il renvoie l'adresse du nouveau tampon de mémoire.
3.UNSAFE.FREEMEMORY (ADRESSE LONGE) - Un tampon de mémoire généré par les deux méthodes précédentes. Si l'adresse est 0, ne faites rien.
La mémoire allouée par ces méthodes doit être utilisée dans un mode appelé une seule adresse de registre: Disape fournit un ensemble de méthodes qui acceptent un seul paramètre d'adresse (contrairement au mode de registre double, ils nécessitent un objet et un décalage de décalage). La mémoire allouée de cette manière peut être plus grande que ce que vous configurez dans les paramètres Java de -xmx.
Remarque: La mémoire allouée par dangereuse ne peut pas être collectée à la poubelle. Vous devez le traiter comme une ressource normale et le gérer vous-même.
Voici un exemple de l'utilisation de dangeta.AllocateMemory pour allouer la mémoire, et il vérifie également si l'ensemble du tampon de mémoire est lisible et inscrit:
La copie de code est la suivante:
Final int size = Integer.max_value / 2;
Addr long final = disApE.AllocateMemory (taille);
essayer
{
System.out.println ("Adresse dangereuse =" + addr);
pour (int i = 0; i <size; ++ i)
{
dangeta.putByte (addr + i, (octet) 123);
if (unsepe.getByte (addr + i)! = 123)
System.out.println ("Échec sur offset =" + i);
}
}
Enfin
{
unsetafe.freememory (addr);
}
Comme vous pouvez le voir, en utilisant Sun.Misc.unsafe, vous pouvez écrire un code d'accès à la mémoire très générale: Quel que soit le type de mémoire alloué en Java, vous pouvez lire et écrire tout type de données à volonté.