© JustDOne/Shutterstock.com
Der neue ValueType in Java

Warum Werttypen wichtig sind


Bisher kennt Java zwei verschiedene Datentypen: primitive Datentypen und Objekte. In den kommenden Releases wird es einen neuen Typ geben, den ValueType. Für die tägliche Programmierung sollte es keinen Unterschied zwischen Objekten und Werttypen geben, unter der Haube sind sie jedoch ziemlich unterschiedlich. ValueTypes sind wie Objekte, aber funktionieren wie primitive Datentypen. Das heißt, sie sind effektiv.

Andere Sprachen nutzen ebenfalls diese Art der Datenverarbeitung, obwohl sie sie normalerweise anders bezeichnen. In diesem Artikel beschreibe ich die Problematik von Objekten, die es notwendig macht, ValueTypes (oder Werttypen) in Java aufzunehmen. Anschließend erkläre ich, was ValueTypes sind, und gehe schließlich auf ihre Problematik ein. Ja, auch Werttypen unterliegen einer Problematik, und der Grund dafür ist ein sehr elementarer: Das Leben ist kein Ponyhof, und man kann nicht alles haben. Es gibt bei der Verwendung von ValueTypes gegenüber Objekten Vorteile, aber ebenso existieren Anwendungen beziehungsweise Programmierkonstrukte, bei denen Objekte besser passen. Im letzten Teil des Artikels erkläre ich außerdem, warum es Einschränkungen bei Werttypen gibt, wie z. B. keine Vererbung, Mangel an generischen Elementen und Unveränderlichkeit.

Objekte in Java

Objekte in Java sind kleine Speicherteile, die in der Regel in einem Segment des Speichers namens Heap gespeichert werden. Der Speicher wird beim Erstellen des Objekts zugewiesen und freigegeben, wenn das Objekt nicht mehr verwendet und „weggeräumt“ wird (Garbage Collection). Während der Lebensdauer des Objekts kann der Speicher, der das Objekt repräsentiert, im Verlauf des Garbage-Collection-Prozesses von einem Ort zum anderen verschoben werden. Auf diese Weise verwaltet Java den Speicher und stellt sicher, dass unabhängig davon, wie Objekte erstellt und zerstört werden, der Speicher nicht segmentiert wird. Andere Sprachen mit Garbage Collector, die den „Objektmüll“ zwar wegräumen, aber nicht komprimieren, laufen Gefahr, dass lange laufende Prozesse den Speicher segmentieren. Aber: Komprimieren ist ein CPU-intensiver Prozess, deshalb konkurriert Java GC kaum mit der Geschwindigkeit der Garbage Collection (GC) der Programmiersprache Go. Vor allem aber macht die Tatsache, dass sich die Objekte im Speicher bewegen, Zeiger nutzlos. Es wird zwar irgendwohin verwiesen, die Daten können sich jedoch nach einer Komprimierungsphase von GC bereits irgendwo anders befinden. Das ist der Grund, warum Java keine Zeiger verwendet – Java verwendet Referenzen.

Man könnte sich fragen, worin der Unterschied zwischen Zeiger und Referenz besteht. Kurz gesagt: Bei Referenzen handelt es sich um verwaltete Zeiger. Wenn GC ein Objekt im Speicher verschiebt, müssen alle Referenzen aktualisiert werden, damit sie jederzeit auf das richtige Objekt verweisen. Die Integration von nativem Code ist ebenfalls etwas umständlich. Java GC kann nicht wissen, wohin im durch den nativen Code verwalteten Speicher der Zeiger kopiert wurde. Außerdem gibt es in Java keine Zeigerarithmetik. Es existieren zwar Arrays, aber ansonsten kann man die schöne Zeigerarithmetik vergessen, an die man sich bei der Programmierung in C oder C++ vielleicht bereits gewöhnt hat. Der Vorteil ist, dass auch durch fehlerhafte Zeigerwertberechnungen verursachte Speicherfehler ausgeschlossen werden können.

Dennoch befinden sich die Objekte im Speicher, und wenn der Prozessor damit rechnen muss, muss das Objekt vom Hauptspeicher zum Prozessor gelangen. In den guten alten Zeiten, in denen die CPUs mit 4 MHz liefen, war das kein Problem. Die Geschwindigkeit des Speicherzugriffs war vergleichbar mit der Geschwindigkeit des Prozessors. Heute laufen Prozessoren mit 4 GHz, und der Speicherzugriff ist kaum schneller als früher. Die Technologie ist nicht schlecht, es handelt sich hierbei einfach um Physik. Man muss nur die Zeit berechnen, die benötigt wird, um von der CPU zum Speicher und mit Lichtgeschwindigkeit wieder zurück zu gelangen, das ist alles. Es gibt nur einen Weg, die Geschwindigkeit zu erhöhen, nämlich den Speicher näher an die Verarbeitungseinheit zu bringen. Und genau das ist es, was moderne CPUs auszeichnet: Sie verfügen über Speichercaches auf der CPU selbst. Leider ist nicht nur die CPU-Geschwindigkeit, sondern auch der Speicherbedarf gestiegen. Früher hatten wir 640 kB auf einem Computer, was für alles reichen musste. Heute hat mein Mac 16 GB. Und hier kommt wieder die Physik ins Spiel: Man kann nicht 16 GB oder mehr auf die CPU legen, da dort kein Platz ist und es auch keine effektive Methode gibt, das System entsprechend zu kühlen. Und wir wollen die CPU schließlich zum Rechnen nutzen, nicht zum Kochen.

Wenn die CPU Speicherplatz braucht, wird im Cache gespeichert. Wenn ein Programm etwas von einem Speicherort abruft, ist es wahrscheinlich, dass es bald etwas vom anderen Speicherort abruft und so weiter. Deshalb liest die CPU ganze Speicherseiten in den Cache ein. Im Cache können sich viele Seiten aus verschiedenen Speicherbereichen befinden. Wenn wir auf ein Element in einem Array zugreifen wollen, kann das lange dauern (aus CPU-Sicht bedeutet „lange dauern“ ein paar Dutzend Nanosekunden), wenn wir jedoch ein zweites Element aus dem Array brauchen, befindet es sich bereits im Cache. Das ist sehr effektiv, es sei denn, es handelt sich um ein Array von Objekten. In...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang