Rzecz jasna chodzi o projekt w ramach OpenJDK – Valhalla. Takich projektów jest sporo i dotyczą przeróżnych tematów – od portu jdk na Alpine Linuxa przez dodatki do składni Javy, na niskopoziomowych aspektach pamięci i procesora kończąc.
Głównym celem tego projektu jest wprowadzenie tzw. value objects
, które później zaczęto nazywać inline class
(a ostatnio primitive object
).
Ale o so chosi?
Java jest stara… Ma 25 lat. Niektóre koncepcje początkowo słuszne straciły na aktualności z biegiem czasu.
Głównym założeniem obiektów jest to, że każdy obiekt ma swoje miejsce na Heapie. Jeśli chcemy posiadać odwołanie z jednego obiektu do drugiego, to możemy tylko zapisać referencję do tego obiektu. Dzięki temu obiekty nie są nadmiernie duże.
Takie założenie ma też swoją wadę – jest mało przyjazne dla zarządzania pamięcią. Najprościej będzie to wytłumaczyć na przykładzie.
Załóżmy, że mamy taki zmienną lokalną Optional<Integer>
. Aby dowiedzieć się, jaka wartość jest w tej zmiennej, musimy wykonać 3 odczyty:
1.Wartość wskaźnika na Optional
(ze stosu wątku),
2.Wartość wskaźnika na Integer
(ze sterty),
3.Wartość Integer
(ze sterty). Wszystkie te dane mogą być teoretycznie w zupełnie różnych miejscach.
Logicznym usprawnieniem jest sprawienie, aby wszystkie dane były w jednym miejscu. Możliwym podejściem jest zawieranie w sobie całych obiektów (ang. embedding – osadzanie), zamiast trzymania odwołań do obiektów. W przypadku wspomnianego Optional<Integer>
można by w ten sposób ograniczyć odwołania do RAMu do 1 odczytu.
Założenia
Klasy Inline mają kilka własności, które różnią ich od standardowych klas.
- Nie ma hierarchii klas – dziedziczą tylko i wyłącznie z Object
- Nie mają własnej tożsamości –
equals()
,hashCode()
,toString()
bazują tylko i wyłącznie od wartości zawartych w klasie inline.
Co więcej – jeśli takie obiekty mają takie same wartości pól, to można je porównywać z użyciem operatora==
notify()
,notifyall()
,wait()
nie są wspierane. Podobnie, nie można użyć takiego obiektu wraz zsynchronize()
.- Zmienne lokalne o typie zwykłych obiektów są przechowywane zawsze na stercie (Heapie), gdy na stosie wątków są tylko odniesienia do nich. W przypadku lokalnych obiektów inline są one przechowywane na stosie wątków.
Podobnie, jeśli jakiś zwykły obiekt zawiera pole o typie klasy inline, to może on być embedowany, gdy normalnie przechowujemy wskaźnik do tego obiektu na stertę. - Wszystkie pola są zawsze i niezmiennie finalne.
- Podobnie jak dla typów prymitywnych, pola tego typu nie mogą być nullem – zawsze istnieje wartość domyślna dla takiego pola.
Niektóre właściwości klas inline zostają. Przykładowo, taka klasa będzie miała metody oraz będzie mogła implementować interfejs.
A kiedy to wejdzie?
Nie wspominałbym o tym projekcie, gdyby JEP 390 wchodzący w Javie 16. Chodzi w nim o oswajanie się, że niektóre klasy będą kandydatami do stania się value-based
. W tych przypadkach w Javie 16 będą wyświetlane na konsolę ostrzeżenia w przypadku niedozwolonych w przyszłości akcji takich jak synchronizacja na takim obiekcie, używanie konstruktorów zamiast metod fabrycznych (zamiast new Integer()
używamy Integer.valueOf()
).
W JEP 390 znalazłem niepokojący fragment:The design and implementation of primitive classes is sufficiently mature that we can confidently anticipate migrating certain classes of the Java Platform to become primitive classes in a future release.
Niepokój dotyczy sformułowania „future release”, co może sugerować, że chodzi o następny release. Jakkolwiek filologii angielskiej nie skończyłem, więc dopuszczam myśl o własnej niedoskonałości 😉
Prototyp klas inline jest dostępny już od Javy 14. Można ściągnąć i się pobawić.
A na co to komu?
Potencjalnie klas do usprawnienia jest wiele. Począwszy od wrapperów typów prymitywnych (Integer
, Long
, Boolean
itd) poprzez Optional
, LocalDateTime
przez niezmienne klasy List.of()
, Set.of()
aż na klasach generowanych dla implementacji wyrażeń lambda kończąc.
Jeśli chodzi o wrappery, to jest to dobry sposób na pozbycie się autoboxingu, który dość skutecznie ogranicza wydajność. Pozwoli też na usunięcie bugów związanych z porówywaniem Integer
ów z użyciem ==
dla wartości > 128 😉
Przetwarzanie tablicach (prawdopodobnie również ArrayList
oraz List.of()
) z użyciem obiektów prymitywnych również powinno być zaletą wprowadzenia projektu w życie.
Ostatecznie mniej alokacji na Heapie może spowodować rzadsze uruchamianie GC. Jakkolwiek w zamian mogą się pojawić problemy z pamięcią na stosach wątków…
Słowo końcowe
Jeden z ciekawszych projektów w OpenJDK miejmy nadzieję wchodzi w decydującą fazę. Ciekawi mnie, jak wypadną benchmarki, jak już projekt wejdzie produkcyjnie.
Garść linków dla ciekawych:
- Dobry opis projektu na serwisie InfoQ
- Notatki Braina Goetza o Valhalli – w ramach zachęty powiem, że w projekt wprowadza 2 nowe kody bytecodu. Więcej w notatkach 😉
- Newsy o Valhalli
Pax et Bonum!