16 marca wyszła 16 wersja Javy. ? W końcu zobaczymy rekordy jako oficjalną część języka! Zostało wprowadzone m.in. Vector API jako Incubator oraz prowadzone są pracę nad poprawą współpracy Javy z natywnym kodem. Nie przedłużając wstępu, zobaczmy jakie nowe zabawki otrzymamy w tej wersji!
Vector API (Incubator)
Normalnie procesor wykonuje jedną operację na jednym zestawie danych (SIMD – Single Instruction Single Data). Czyli przykładowo może dodać do siebie dwie liczby. Wektory pozwalają na wykonanie operacji na wielu liczbach naraz (SIMD -Single Instruction Multipe Data). Wirtualna maszyna Javy już wcześniej posiadała optymalizację, która automatycznie potrafiła zmieniać kod, aby wykorzystać możliwości procesora operującego na wektorach. Jednak możliwości te są ograniczone i delikatne. Nowe API odda w ręce programistów większą kontrolę nad procesem, dzięki temu będziemy mogli wprowadzić takie optymalizacje sami, w większej ilości miejsc.
Dokumentacja podaje przykład pętli, którą możemy zoptymalizować. Pętla wygląda następująco:
void scalarComputation(float[] a, float[] b, float[] c) { | |
for (int i = 0; i < a.length; i++) { | |
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f; | |
} | |
} |
Oraz wersja z wykorzystaniem API wektorowego.
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256; | |
void vectorComputation(float[] a, float[] b, float[] c) { | |
for (int i = 0; i < a.length; i += SPECIES.length()) { | |
var m = SPECIES.indexInRange(i, a.length); | |
// FloatVector va, vb, vc; | |
var va = FloatVector.fromArray(SPECIES, a, i, m); | |
var vb = FloatVector.fromArray(SPECIES, b, i, m); | |
var vc = va.mul(va). | |
add(vb.mul(vb)). | |
neg(); | |
vc.intoArray(c, i, m); | |
} | |
} |
Więcej przykładów możecie znaleźć na stronie https://openjdk.java.net/jeps/338
Rekordy
W dużym uproszczeniu jest to nowy typ klasy do przechowywania niemutowalnego stanu. Dzięki temu w bardzo zwięzły sposób możemy zadeklarować klasę z polami, do której automatycznie zostaną dodane gettery, konstruktor, metody equals, hashCode i toString. Pierwszy raz pojawiły się w Javie 14 jako preview, następnie w Javie 15 zostały oznaczone jako second preview. W aktualnej wersji wchodzą już oficjalnie do języka. Szerzej pisałem o nich w oddzielnym poście: Java 14 – Record classes. Więcej możecie przeczytać też na oficjalnej stronie: https://openjdk.java.net/jeps/395.
Pattern Matching for instanceof
Podobnie jak rekordy, pattern matching for instanceof pierwszy raz ujrzeliśmy w Javie 14. Dzięki temu możemy zadeklarować zmienną od razu przy sprawdzeniu, czy jest odpowiedniego typu. W poniższym przykładzie używamy od razu zmiennej w instrukcji if, aby sprawdzić, czy ma odpowiednią długość.
if (obj instanceof String s && s.length() > 5) { | |
return s.contains("Yes!"); | |
} |
Więcej możecie przeczytać w poprzednim wpisie: Java 14 – Co nowego w kolejnym wydaniu? oraz na oficjalnej stronie: https://openjdk.java.net/jeps/394.
Elastic Metaspace
Nieużywana pamięć na metadane klasy będzie zwracana do systemu. W przeszłości zdarzało się, że metaspace w JVM zajmował przesadnie dużo pamięci. Nawet gdy była ona już niepotrzebna, nadal nie była zwracana do systemu. W nowej wersji kod zarządzający tą częścią pamięci został uproszczony oraz zmienił się wykorzystywany algorytm, dzięki czemu możliwe jest zwolnienie pamięci. Więcej możecie znaleźć na stronie: https://openjdk.java.net/jeps/387.
Unix-Domain Socket Channels
Dodanie wsparcia dla Unix-domain socket. Są one używane do komunikacji pomiędzy procesami (IPC – Inter-process communication). Są bardzo podobne do socketów TCP/IP, z tą różnicą, że są adresowane za pomocą ścieżek w systemie plików zamiast adresów IP. Jeśli chodzi o odczyt i zapis, to będą one się zachowywać tak samo, akceptacja przychodzących połączeń, multipleksacja i inne operacje będą działały tak samo. Dzięki temu będzie można w bezpieczniejszy i wydajniejszy sposób komunikować się pomiędzy procesami niż przez połączenie loopback TCP/IP. Więcej możecie przeczytać na stronie https://openjdk.java.net/jeps/380.
Sealed Classes (Second Preview)
Klasy zapieczętowne pojawiły się już w Javie 15, w tym wydaniu są oznaczone jako drugi preview. Dzięki temu możemy ustalić jakie klasy będą dziedziczyły po naszej klasie lub implementowały interfejs. W przyszłości również będzie możliwy lepszy pattern matching (np. będziemy mogli tworzyć wyczerpujące switche na podstawie typu klasy).
Przykład klas zapieczętowanych poniżej. Warto zauważyć, że klasa Square dziedzicząca po zapieczętowanej klasie Shape jest już non-sealed, oznacza to, że po niej mogą dziedziczyć inne klasy bez ograniczeń, mimo że klasa nadrzędna Shape była oznaczona jako sealed.
package com.example.geometry; | |
public abstract sealed class Shape | |
permits Circle, Rectangle, Square { ... } | |
public final class Circle extends Shape { ... } | |
public sealed class Rectangle extends Shape | |
permits TransparentRectangle, FilledRectangle { ... } | |
public final class TransparentRectangle extends Rectangle { ... } | |
public final class FilledRectangle extends Rectangle { ... } | |
public non-sealed class Square extends Shape { ... } |
Więcej możecie przeczytać na stronie https://openjdk.java.net/jeps/397.
Porty dla Alpine Linux i Windows/AArch64
Przygotowanie portu JDK dla dystrybucji Alpine Linux, który używa musl jako biblioteki C dla architektury x64 i AArch64. Alpine Linux jest często wykorzystywany w deploymentach w chmurze i kontenerach dzięki temu, że obraz jest bardzo mały. Przykładowo dockerowy obraz zajmuje mniej niż 6MB. Więcej tutaj: https://openjdk.java.net/jeps/386.
Drugim portem jest port dla Windowsa działającego na architekturze AArch64 (ARM64). Architektura ta nabiera coraz większej popularności, dlatego zdecydowano się dodać dla niej port. Więcej możecie znaleźć tutaj: https://openjdk.java.net/jeps/388.
Foreign Linker API (Incubator)
Nowe API ma pozwalać na łatwiejszy, czysto Javovy dostęp do natywnego kodu. Razem z Foreign Memory Access API mają zastąpić mechanizm JNI. Ma być też co najmniej tak szybkie jak JNI. Więcej znajdziecie tutaj: https://openjdk.java.net/jeps/389.
Foreign-Memory Access API (Third Incubator)
Foreign Memory Access API ma zapewnić szybki i bezpieczny dostęp do pamięci natywnej. Aktualnie możemy się do niej dostać poprzez Unsafe, co jak sama nazwa wskazuje, jest niebezpieczne. Drugą możliwością jest wykorzystanie DirectByteBuffer, co jest bezpieczne, jednak to API nie jest zaprojektowane jako API do ogólnej pracy z pamięcią. Jeśli czytamy lub zapisujemy często małe porcje danych to sprawdzanie, czy nie wychodzimy poza zaalokowaną pamięć (bound checks) może zajmować większość czasu procesora. Nowe API będzie to mocno optymalizować, na przykład jeśli robimy odczyty w pętli, takie sprawdzenia zostaną wyniesione poza pętlę i wykonane tylko raz. Więcej przeczytacie tutaj: https://openjdk.java.net/jeps/393.
Inne zmiany
Do nowego wydania weszło też dużo zmian, które nie będą dla nas widoczne w codziennej pracy:
- Enable C++14 Language Features – w kodzie źródłowym C++ do JDK będzie można używać funkcjonalności z C++ 14 (https://openjdk.java.net/jeps/347).
- Migrate from Mercurial to Git – kod źródłowy OpenJDK będzie trzymany teraz w Gicie zamiast Mercuriala (https://openjdk.java.net/jeps/357).
- Migrate to GitHub – repozytoria z kodem źródłowym do OpenJDK i wszystkich projektów będą zmigrowane do GitHuba dla wersji 11 i późniejszych (https://openjdk.java.net/jeps/369),
- Strongly Encapsulate JDK Internals by Default – w JDK 9 jako default była rozluźniona enkapsulacja, w 16 domyślnie będzie silna. Można to oczywiście zmienić, jednak ma to zachęcić programistów do używania standardowego API zamiast wewnętrznych mechanizmów JDK. Na razie krytyczne API takie jak Unsafe pozostanie dostępne.
- ZGC: Concurrent Thread-Stack Processing – kolejne ulepszenie ZGC – przeniesienie procesowania stosu wątku z safepointu do fazy równoległej. Przy dużej ilości wątków wyszukiwanie korzeni może robić się problematyczne i zabierać dużo czasu. Przeniesienie tej operacji do fazy równoległej powinno zmniejszyć czas pauz GC. Więcej tutaj: https://openjdk.java.net/jeps/376.
- Warnings for Value-Based Classes – używanie konstruktorów klas opakowujących typy prymitywne będzie generowało ostrzeżenia przy kompilacji. Również próba synchronizacji opartej na tych klasach będzie generowało ostrzeżenie. Klasy te w przyszłości mają stać się value-based classes. Czyli obiekt będzie definiowany przez jego wartość. Czym są value-based classes możesz przeczytac tutaj: https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/doc-files/ValueBased.html, a o samych zmianach w Javie tutaj: https://openjdk.java.net/jeps/390.
- Packaging Tool – narzędzie do przygotowywania natywnych paczek aplikacji (msi/exe dla Windowsa, pkg/dmg dla MacOS i deb/rpm dla Linuxa). Jest to rozwinięcie javapackager tool znanego z JavaFX. Pierwszy raz pojawiło się w Javie 14 jako Incubation, w tej wersji wchodzi już jako pełna funkcjonalność. Więcej tutaj: https://openjdk.java.net/jeps/392.
Podsumowanie
To już wszystko, co znajdziemy w nowej Javie. Nowa wersja pojawiająca się co pół roku bardzo przyśpieszyła wprowadzanie zmian w języku. Czekam niecierpliwie na ogłoszenie kolejnych ulepszeń w przyszłości.