1. Die Obergrenze der Array -Zuweisung
Die Größe eines Arrays in Java ist begrenzt, da der Int -Typ als Array -Index verwendet wird. Dies bedeutet, dass Sie keine Arrays beantragen können, die die Größe der Ganzzahl überschreiten.MAX_VALUE (2^31-1). Dies bedeutet nicht, dass die Obergrenze Ihrer Speicheranwendung 2G beträgt. Sie können eine Reihe größerer Typen beantragen. Zum Beispiel:
Die Codekopie lautet wie folgt:
Final Long [] ar = new Long [Integer.max_Value];
Dadurch werden 16G -8 -Bytes zugewiesen. ist nur allgemeine Regeln, die spezifische Zuordnung hängt von der tatsächlichen Situation ab).
Leider wird es in Java aufgrund der Typbeschränkung von Array -Elementen problematischer sein, den Speicher zu betreiben. Wenn es um den Betrieb von Arrays geht, sollte ByteBuffer die nützlichste Klasse sein, die Methoden zum Lesen und Schreiben verschiedener Java -Typen bietet. Der Nachteil ist, dass der Zielarray -Typ Byte [] sein muss, was bedeutet, dass der maximale Speichercache, den Sie zuweisen, 2G sein kann.
2. Verwenden Sie alle Arrays als Byte -Arrays zum Betrieb
Unter der Annahme, dass 2G -Speicher für uns jetzt alles andere als für uns ist, ist es in Ordnung, wenn es 16G ist. Wir haben eine lange [] zugewiesen, aber wir wollen es als Byte -Array bedienen. In Java müssen wir um Hilfe von C -Programmierern bitten - sun.misc.unsafe. Diese Klasse hat zwei Methodensätze: GETN (Objekt, Offset), diese Methode ist, einen Wert des angegebenen Typs aus der Position zu erhalten, in der der Objektversatz ausgefallen ist, und hier die Art der Rückgabe des Werts Putn -Methode (Objekt, Offset, Value) besteht darin, einen Wert in die Position des Offsetes des Objekts zu schreiben.
Leider können diese Methoden nur Werte eines bestimmten Typs erhalten oder festlegen. Wenn Sie Daten aus einem Array kopieren, benötigen Sie auch eine andere Methode von unsicherem, Copymemory (srcObject, srcoffset, Destobject, Destoffet, Count). Dies funktioniert ähnlich wie bei System.ArrayCopy, kopiert jedoch eher Bytes als Array -Elemente.
Um auf die Daten eines Arrays durch sun.misc.unsafe zuzugreifen, benötigen Sie zwei Dinge:
1. Der Versatz von Daten im Array -Objekt
2. Der Versatz der kopierten Elemente in den Array -Daten
Wie andere Java -Objekte verfügt Arrays über einen Objektkopf, der vor den tatsächlichen Daten gespeichert ist. Die Länge dieses Headers kann durch die Methode der Unsicherheit (T []) erhalten werden, wobei T die Art des Array -Elements ist. Die Größe des Array -Elements kann durch die Methode der Unsicherheit (T []) erhalten werden. Dies bedeutet, dass Ihr Offset -Offset ArrayOffset+N*ArraysCale sein sollte, wenn Sie auf das n -te Element vom Typ t zugreifen möchten.
Schreiben wir ein einfaches Beispiel. Wir weisen ein langes Array zu und aktualisieren ein paar Bytes darin. We update the last element to -1 (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF), and then clear all bytes of this element one by one.
Die Codekopie lautet wie folgt:
final long [] ar = new Long [1000];
endgültig int index = ar.Length - 1;
AR [INDEX] = -1; // fffffffffff
System.out.println ("vor change =" + long.tohexstring (ar [index]));
für (lang i = 0; i <8; ++ i)
{
unsicher. PutByte (AR, LongarrayOffset + 8L * INDEX + I, (BYTE) 0);
System.out.println ("After Change: i =" + i + ", val =" + long.toHexString (ar [index]));
}
Wenn Sie das obige Beispiel ausführen möchten, müssen Sie Ihrer Testklasse den folgenden statischen Codeblock hinzufügen:
Die Codekopie lautet wie folgt:
privates statisches Finale unsicher unsicher;
statisch
{
versuchen
{
Field field = unsicher.class.getDeclaredfield ("theunsafe");
field.setAccessible (true);
unsicher = (unsicher) field.get (null);
}
fangen (Ausnahme E)
{
neue runimeexception (e) werfen;
}
}
private statische endgültige LongarrayOffset = unsicher.ArrayBaseOffset (lang []. Klasse);
Das Ausgangsergebnis ist:
Die Codekopie lautet wie folgt:
Vor Change = fffffffffffffffffffff
Nach der Änderung: i = 0, val = fffffffffffffff00
Nach der Änderung: i = 1, val = fffffffffffff0000
Nach der Änderung: i = 2, val = fffffffffff000000
Nach der Änderung: i = 3, val = fffffffff00000000
Nach der Änderung: i = 4, val = ffffff00000000000
Nach der Änderung: i = 5, val = ffff0000000000000
Nach der Änderung: i = 6, val = ff0000000000000000
Nach der Änderung: i = 7, val = 0
3. Gedächtniszuweisung von sun.misc.unsafe
Wie oben erwähnt, ist in reinem Java die Speichergröße, die wir zuweisen können, begrenzt. Diese Einschränkung wurde in der ersten Version von Java festgelegt, und zu dieser Zeit wagten die Leute es nicht mehr, mehrere g Gedächtnis wie dieses zu teilen. Aber jetzt ist es die Ära von Big Data, und wir brauchen mehr Speicher. In Java gibt es zwei Möglichkeiten, mehr Erinnerung zu bekommen:
1. Zuwenden Sie viele kleine Speicherbrocken und verwenden Sie sie dann logisch als kontinuierlich großer Speicherblock.
2. Verwenden Sie sun.misc.unsafe.Allcatememory (lang), um Speicher zuzuweisen.
Die erste Methode ist aus der Perspektive von Algorithmen nur ein wenig interessanter. Schauen wir uns also die zweite Methode an.
sun.misc.unsafe bietet eine Reihe von Methoden zur Zuordnung, Neuzuordnung und Freigabe des Gedächtnisses. Sie sind der malloc/kostenlosen Methode von C sehr ähnlich:
1.Lang unsicher. Dieser Speicher kann Junk -Daten enthalten (nicht automatisch gelöscht). Wenn die Zuweisung fehlschlägt, wird eine Ausnahme von java.lang.outofMemoryError geworfen. Es gibt eine Speicheradresse ungleich Null zurück (siehe Beschreibung unten).
2.Unsafe.reallocatememory (Lange Adresse, lange Größe)-Reallocozen Sie ein Stück Speicher und kopieren Sie Daten aus dem alten Speicherpuffer (wobei die Adresse auf den neu zugewiesenen Speicherblock zeigt. Wenn die Adresse gleich 0 ist, hat diese Methode den gleichen Effekt wie Allocatememory. Es gibt die Adresse des neuen Speicherpuffers zurück.
3.Unsafe.Freememory (lange Adresse)-Freie ein Speicherpuffer, der von den beiden vorherigen Methoden erzeugt wird. Wenn die Adresse 0 ist, tun Sie nichts.
Der von diesen Methoden zugewiesene Speicher sollte in einem Modus verwendet werden, der als eine einzelne Registeradresse bezeichnet wird: Unafe bietet eine Reihe von Methoden, die nur einen Adressparameter akzeptieren (im Gegensatz zum Dual -Register -Modus benötigen sie ein Objekt und einen Offset -Offset). Der auf diese Weise zugewiesene Speicher kann größer sein als das, was Sie in den Java -Parametern von -xmx konfigurieren.
HINWEIS: Der von Unsicherheit zugewiesene Speicher kann nicht Müll erfasst werden. Sie müssen es als normale Ressource behandeln und selbst verwalten.
Hier ist ein Beispiel für die Verwendung von Unsicherheit.Allocatememory zur Zuordnung von Speicher und prüft auch, ob der gesamte Speicherpuffer lesbar und schriftlich ist:
Die Codekopie lautet wie folgt:
endgültige int size = Integer.max_Value / 2;
endgültig long addr = unsicher. allocatememory (Größe);
versuchen
{
System.out.println ("unsicher address =" + addr);
für (int i = 0; i <size; ++ i)
{
unsicherer PutByte (adDr + i, (byte) 123);
if (unsicher.getbyte (addr + i)! = 123)
System.out.println ("fehlgeschlagen bei offset =" + i);
}
}
Endlich
{
unsicher.Freememory (ADDR);
}
Wie Sie sehen können, können Sie mit sun.misc.unsafe einen sehr allgemeinen Speicherzugriffscode schreiben: Unabhängig davon, welche Art von Speicher in Java zugewiesen wird, können Sie nach Belieben jede Art von Daten lesen und schreiben.