JVM w krainie wiecznych łowów

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.

  1. Nie ma hierarchii klas – dziedziczą tylko i wyłącznie z Object
  2. 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 ==
  3. notify(), notifyall(), wait() nie są wspierane. Podobnie, nie można użyć takiego obiektu wraz z synchronize().
  4. 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ę.
  5. Wszystkie pola są zawsze i niezmiennie finalne.
  6. 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:

Pax et Bonum!

Oceń wpis

Autor: jgardo

Programista Java od 2013 roku. Interesuje się niskopoziomową Javą, ekosystemem Jvm i jego wydajnością. Co jednak nie przeszkadza w przywiązywaniu uwagi do czystości kodu w życiu codziennym ;) Pracuje w PayU.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *