In diesem Artikel erfährst du wie durch "Compressed Oops" auf einem 64-Bit-System Referenzen auf Java-Objekte mit nur 32 statt 64 Bits dargestellt werden können und wie dadurch der Speicherbedarf einer Java-Anwendung signifikant reduziert wird.
Was ist ein 64-Bit-System?
Ein 64-Bit-System zeichnet sich dadurch aus, dass Pointer auf Adressen im Arbeitsspeicher 64 Bit groß sind. Dadurch lassen sich 264 Bytes = 16 Exabyte = 18.446.744.073.709.551.616 Bytes adressieren.
Heutzutage gibt es praktisch keine Programme mit diesem Speicherbedarf. In zehn Jahren werden wir über diese Aussage vielleicht schmunzeln ;-)
Hier die grafische Darstellung eines solchen 64-Bit-Pointers:
Nun könnte man überlegen, diese 64 Bit in der Mitte zu teilen und mit der gleichen Speichermenge nicht nur einen, sondern zwei Pointer zu speichern:
Mit 32 Bit lassen sich allerdings nur 232 Bytes = 4 GB adressieren. Das wiederum ist für viele Anwendungen zu wenig.
Die Java-Entwickler haben sich daher einen cleveren Trick („Compressed Oops“) einfallen lassen, um mit 32 Bit nicht nur 4 GB sondern 32 GB zu adressieren. Und das wiederum ist für die meisten Anwendungen unserer Zeit ausreichend.
Wie funktionieren Compressed Oops?
Durch Compressed Oops (OOP steht für „ordinary object pointer“) kann mit 32-Bit-Pointern ein Adressraum von 235 = 32 Gigabyte adressiert werden.
Wie ist das möglich? Um 32 Gigabyte zu adressieren, bräuchten wir doch eigentlich 35 Bits. 35 Bits können wir aber nicht in einem 32-Bit-int speichern, dafür bräuchten wir die nächstgrößere Datenstruktur, ein 64-Bit-long:
Wir hätten also 29 Bit verschwendet. Dann hätten wir auch bei 64-Bit-Pointern bleiben können.
Es wird Zeit einen Trick anzuwenden!
Zunächst einmal positionieren wir alle Java-Objekte an durch acht teilbare Speicheradressen. Acht ist 23, d. h. die letzten 3 Bits eines Pointers sind immer 0. Somit enthalten also nur die oberen 32 Bit relevante Informationen:
Da wir wissen, dass die letzten drei Bits immer 0 sind, brauchen wir diese nicht jedes Mal mit abzuspeichern. Die 35-Bit-Speicheradresse kann daher ohne Informationsverlust um 3 Bit nach rechts verschoben und somit in einem 32-Bit-Feld gespeichert werden:
Um später wieder auf die unkomprimierte Speicheradresse zugreifen zu können, müssen die 32 Bit lediglich wieder um 3 Bit nach links geschoben werden.
Compressed Oops sind standardmäßig aktiviert
Auf einem 64-Bit-System mit maximal 32 GB Heap sind Compressed OOPs standardmäßig aktiviert. Bei mehr als 32 GB können Compressed Oops nicht verwendet werden, da sie eben nur 32 GB adressieren können.
Compressed Oops können mit folgender VM-Option deaktiviert werden:
-XX:-UseCompressedOops
Warum sollte man das tun?
Das Komprimieren und Dekomprimieren der Pointer kostet Zeit. Nicht viel, da die dafür benötigte Shift-Operation in der Regel in einem einzigen CPU-Zyklus durchgeführt werden kann. Doch wer das letzte Quäntchen Performance aus seiner Anwendung herauskitzeln will und den dafür erhöhten Speicherbedarf in Kauf nehmen will (alle Pointer belegen 64 statt 32 Bits), der kann diese Möglichkeit in Betracht ziehen.
Fazit
Mit Compressed Oops lassen sich auf einem 64-Bit-System mit einer Heapgröße von maximal 32 GB Speicheradressen mit nur 32 statt 64 Bits kodieren. Das es auf jedes aktive Java-Objekt in einer Anwendung mindestens einen Pointer gibt, kann somit der Speicherbedarf einer Anwendung signifikant reduziert werden.
Willst du über alle neue Java-Features auf dem Laufenden sein? Dann klicke hier, um dich für den HappyCoders-Newsletter anzumelden.