Powody przejścia do środowiska Java 11 i nowszych

Pytanie nie brzmi , jeśli należy przejść do środowiska Java 11 lub nowszej wersji, ale kiedy. W ciągu najbliższych kilku lat środowisko Java 8 nie będzie już obsługiwane, a użytkownicy będą musieli przejść do środowiska Java 11 lub nowszego. Firma Microsoft uważa, że przejście do środowiska Java 11 przynosi wiele korzyści i zachęca zespoły zrobienia tego najszybciej, jak to możliwe.

Dodano nowe funkcje i wprowadzono ulepszenia względem środowiska Java 8. Dodano składniki i wprowadzono zauważalne modyfikacje interfejsu API orz usprawnienia, które zwiększają skracają czas uruchamiania, zwiększają wydajność i poprawiają wykorzystanie pamięci.

Przejście do środowiska Java 11

Przejście do środowiska Java 11 można wykonać stopniowo. Aby kod działał on w środowisku Java 11, nie jest wymagane stosowanie modułów Java. Środowisko Java 11 może służyć do uruchamiania kodu opracowanego i utworzonego za pomocą zestawu JDK 8. Jednak istnieją potencjalne problemy, dotyczące głównie przestarzałego interfejsu API, modułów ładujących klasy i odbicia.

Zespół inżynierów języka Java w firmie Microsoft przygotował przewodnik przejścia ze środowiska Java 8 do środowiska Java 11. Platforma Java, Standard Edition Oracle JDK 9 Migration Guide (Przewodnik migracji zestawu Oracle JDK 9) i The State of the Module System: Compatibility and Migration (Zgodność i migracja) to inne przydatne przewodniki.

Zmiany wysokiego poziomu w środowisku Java 11 względem środowiska Java 8

Ta sekcja nie wylicza wszystkich zmian wprowadzonych w języku Java w wersji 9 [1], 10 [2] i 11 [3]. Wyróżniono zmiany, które mają wpływ na wydajność, diagnostykę i produktywność.

Moduły [4]

Moduły rozwiązują problemy dotyczące konfiguracji i hermetyzacji, którymi trudno zarządzać w aplikacjach o dużej skali uruchomionych na ścieżce klasy. Moduł to samoopisująca kolekcja klas i interfejsów języka Java oraz powiązanych zasobów.

Moduły umożliwiają dostosowywanie konfiguracji środowiska uruchomieniowego, które zawierają tylko składniki wymagane przez aplikację. To dostosowanie powoduje mniejsze zużycie pamięci i umożliwia statyczne łączenie aplikacji przy użyciu narzędzia jlink w niestandardowe środowisko uruchomieniowe na potrzeby wdrożenia. Mniejsze zużycie pamięci może być szczególnie przydatne w architekturze mikrousług.

Wewnętrznie wirtualna maszyna Java (JVM) jest w stanie korzystać z modułów w taki sposób, aby zwiększyć efektywność ładowania klas. W efekcie pozwala to uzyskać środowisko uruchomieniowe, które jest mniejsze, lżejsze i umożliwia szybsze uruchamianie. Techniki optymalizacji używane przez JVM w celu zwiększenia wydajności aplikacji mogą być bardziej efektywne, ponieważ moduły kodują składniki, których wymaga klasa.

Dla programistów moduły mogą wymuszać silną hermetyzację, wymagając jawnej deklaracji, które pakiety eksportuje moduł i jakich składników wymaga, oraz ograniczając dostęp odbijający. Ten poziom hermetyzacji sprawia, że aplikacja jest bezpieczniejsza i łatwiejsza w obsłudze.

Aplikacja może nadal używać ścieżki klas, a przejście na moduły nie jest wymagane do uruchamiania w środowisku Java 11.

Profilowanie i diagnostyka

Java Flight Recorder [5]

Narzędzie Java Flight Recorder (JFR) zbiera dane diagnostyczne i profilowania z uruchomionej aplikacji Java. JFR ma niewielki wpływ na działającą aplikację Java. Zebrane dane można następnie analizować przy użyciu narzędzia Java Mission Control (JMC) i innych narzędzi. Narzędzia JFR i JMC były funkcjami komercyjnymi w środowisku Java 8, jednak oba te narzędzia stanowią oprogramowanie open source w środowisku Java 11.

Java Mission Control [6]

Java Mission Control (JMC) udostępnia graficzne wyświetlanie danych zebranych przez narzędzie Java Flight Recorder (JFR) i jest open source w środowisku Java 11. Oprócz ogólnych informacji o uruchomionej aplikacji, JMC umożliwia użytkownikowi przechodzenie do szczegółów danych. JFR i JMC mogą służyć do diagnozowania problemów dotyczących środowiska uruchomieniowego, takich jak przecieki pamięci, obciążenie GC, metody gorące, wąskie gardła wątków i blokowanie operacji we/wy.

Ujednolicone rejestrowanie [7]

Środowisko Java 11 ma wspólny system rejestrowania dla wszystkich składników JVM. Ten ujednolicony system rejestrowania umożliwia użytkownikowi definiowanie składników do rejestrowania oraz jego poziomu. To szczegółowe rejestrowanie jest przydatne do wykonywania analiz głównych przyczyn w przypadku awarii JVM oraz do diagnozowania problemów z wydajnością w środowisku produkcyjnym.

Profilowanie stert o niskim narzucie [8]

Dodano nowy interfejs API do interfejsu JVMTI (Java Virtual Machine Tool Interface) służący do próbkowania alokacji sterty Java. Próbkowanie powoduje niewielkie obciążenie i może być stale włączone. Alokacja sterty może być monitorowana przy użyciu narzędzia Java Flight Recorder (JFR), metoda próbkowania w JFR działa tylko w przypadku alokacji. Implementacja JFR może również pomijać alokacje. Natomiast próbkowanie sterty w środowisku Java 11 umożliwia uzyskiwanie informacji o obiektach aktywnych i nieaktywnych.

Dostawcy rozwiązań monitorowania wydajności aplikacji (APM) zaczynają korzystać z tej nowej funkcji, a grupa inżynierów Java bada jej potencjalne możliwości jej wykorzystania za pomocą narzędzi do monitorowania wydajności platformy Azure.

StackWalker [9]

Pobieranie migawki stosu dla bieżącego wątku jest często używane podczas rejestrowania. Problem polega na tym, jaki zakres śladu stosu ma być rejestrowany, oraz czy w ogóle należy rejestrować ślad stosu. Na przykład jedna osoba może chcieć zobaczyć ślad stosu tylko dla pewnego wyjątku z metody. Klasa StackWalker (dodana w języku Java 9) zawiera migawkę stosu i udostępnia metody, które umożliwiają programistom precyzyjne sterowanie sposobem korzystania ze śladu stosu.

Odzyskiwanie pamięci [10]

Następujące moduły odśmieceń pamięci są dostępne w środowisku Java 11: Serial, Parallel, Garbage-First i Epsilon. Domyślny moduł zbierający elementy bezużyteczne w środowisku Java 11 to Garbage First Garbage Collector (G1GC).

Pozostałe moduły zbierające wymieniono w celu zapewnienia kompletnych informacji. Z Garbage Collector (ZGC) do współbieżny moduł zbierający o niskim opóźnieniu, który próbuje utrzymać czas wstrzymania na poziomie poniżej 10 ms. Moduł ZGC jest dostępny jako funkcja eksperymentalna w środowisku Java 11. Moduł zbierający Shenandoah jest modułem zbierającym o krótkim czasie wstrzymania, który skraca czas wstrzymania odzyskiwania pamięci przez wykonywanie odzyskiwania pamięci w większym zakresie współbieżnie z uruchomionym programem Java. Shenandoah to eksperymentalna funkcja w środowisku Java 12, ale istnieją wersje dostosowane do środowiska Java 11. Moduł zbierający Concurrent Mark and Sweep (CMS) jest dostępny, ale został uznany za przestarzały od wersji 9 środowiska Java.

JVM ustawia wartości domyślne odzyskiwania pamięci dla typowego przypadku użycia. Często te wartości domyślne i inne ustawienia odzyskiwania pamięci muszą zostać dostosowane w celu uzyskania optymalnej przepływności lub opóźnienia, zgodnie z wymaganiami aplikacji. Odpowiednie dostosowywanie odzyskiwania pamięci wymaga dogłębnej znajomości tego tematu, którą cechuje się grupa inżynierów Java firmy Microsoft.

G1GC

Domyślny moduł zbierający elementy bezużyteczne w środowisku Java 11 to moduł zbierający G1 (G1GC). Celem G1GC jest uzyskanie równowagi między opóźnieniami a przepływnością. Moduł zbierający G1 próbuje uzyskać wysoką przepływność dzięki dużemu prawdopodobieństwu realizacji celów dotyczących czasu wstrzymania. G1GC została zaprojektowana tak, aby uniknąć pełnych kolekcji, ale gdy współbieżne kolekcje nie mogą odzyskać pamięci wystarczająco szybko, wystąpi powrót pełny GC. Pełne odzyskiwanie pamięci wykorzystuje taką samą liczbę równoległych wątków roboczych, jak w przypadku zbierania elementów młodych i mieszanych.

Parallel GC

Równoległy moduł zbierający jest domyślnym modułem zbierającym w środowisku Java 8. Równoległy moduł zbierający jest modułem zbierającym przepływności, który korzysta z wielu wątków w celu przyspieszenia odzyskiwania pamięci.

Epsilon [11]

Moduł zbierający Epsilon obsługuje alokacje, ale nie odzyskuje pamięci. Po wyczerpaniu sterty wirtualna maszyna Java (JVM) zostanie zamknięta. Moduł Epsilon jest przydatny w przypadku krótkoterminowych usług i aplikacji, które są wolne od elementów bezużytecznych.

Ulepszenia kontenerów platformy Docker [12]

W środowiskach starszych niż Java 10 ograniczenia dotyczące pamięci i procesora CPU ustawione w kontenerze nie były rozpoznawane przez JVM. Na przykład w środowisku Java 8 JVM domyśnie ustawia maksymalny rozmiar sterty na ¼ pamięci fizycznej podstawowego hosta. Począwszy od środowiska Java 10, JVM używa ograniczeń ustawionych przez grupy kontroli kontenerów (cgroup) do ustawiania limitów pamięci i procesora (patrz uwaga poniżej). Na przykład domyślny maksymalny rozmiar sterty to ¼ limitu pamięci kontenera (np. 500 MB dla -m2G).

Dodano również opcje JVM, aby umożliwić użytkownikom kontenera platformy Docker precyzyjne sterowanie ilością pamięci systemowej, która będzie używana dla sterty języka Java.

Ta obsługa jest domyślnie włączona i jest dostępna tylko na platformach opartych na systemie Linux.

Uwaga

Większość zmian związanych z obsługą cgroup została wdrożona w środowisku Java 8 od wersji jdk8u191. Dalsze ulepszenia niekoniecznie zostaną dostosowane do wersji 8.

Pliki jar z wieloma wersjami [13]

W środowisku Java 11 można utworzyć plik JAR, który zawiera wiele wersji plików klas przeznaczonych dla określonych wydań środowiska Java. Pliki jar z wieloma wersjami umożliwiają deweloperom bibliotek obsługę wielu wersji środowiska Java bez konieczności dostarczania wielu wersji plików jar. W przypadku użytkowników tych bibliotek pliki jar zawierające różne wersje rozwiązują problem z koniecznością dopasowania określonych plików jar do określonych elementów docelowych środowiska uruchomieniowego.

Różne ulepszenia wydajności

Następujące zmiany w JVM mają bezpośredni wpływ na wydajność.

  • JEP 197: Segmented Code Cache [14] — dzieli pamięć podręczną kodu na odrębne segmenty. Ta segmentacja zapewnia lepszą kontrolę nad pamięcią JVM, skraca czas skanowania skompilowanych metod, znacznie zmniejsza fragmentację pamięci podręcznej kodu i poprawia wydajność.

  • JEP 254: Ciągi kompaktowe [15] — zmienia wewnętrzną reprezentację ciągu z dwóch bajtów na znak do jednego lub dwóch bajtów na znak, w zależności od kodowania znaków. Ponieważ większość ciągów zawiera znaki ISO-8859-1/Latin-1, ta zmiana skutecznie zmniejsza o połowę ilość miejsca wymaganego do przechowywania ciągu.

  • JEP 310: Udostępnianie aplikacji Class-Data [16] — Class-Data Udostępnianie zmniejsza czas uruchamiania, umożliwiając zarchiwizowanym klasom mapowanie pamięci w czasie wykonywania. Udostępnianie danych klas aplikacji rozszerza udostępnianie danych klas, umożliwiając umieszczanie klas aplikacji w archiwum CDS. Gdy wiele JVM współdzieli ten sam plik archiwum, pamięć jest zapisywana i łączny czas odpowiedzi systemu jest krótszy.

  • JEP 312: Thread-Local Uzgadnianie [17] — umożliwia wykonanie wywołania zwrotnego na wątkach bez wykonywania globalnego punktu bezpieczeństwa maszyny wirtualnej, co pomaga maszynie wirtualnej osiągnąć mniejsze opóźnienie dzięki zmniejszeniu liczby globalnych punktów bezpieczeństwa.

  • Leniwa alokacja wątków kompilatora [18] — w trybie kompilacji warstwowej maszyna wirtualna uruchamia dużą liczbę wątków kompilatora. Jest to tryb domyślny w systemach z wieloma procesorami. Te wątki są tworzone bez względu na dostępną pamięć lub liczbę żądań kompilacji. Wątki zużywają pamięć nawet wtedy, gdy są bezczynne (niemal przez cały czas), co prowadzi do niewydajnego korzystania z zasobów. Aby rozwiązać ten problem, implementacja została zmieniona tak, aby uruchamiała tylko jeden wątek kompilatora dla każdego typu podczas uruchamiania. Uruchamianie dodatkowych wątków i zamykanie nieużywanych wątków jest obsługiwane dynamicznie.

Następujące zmiany w bibliotekach podstawowych mają wpływ na wydajność nowego lub zmodyfikowanego kodu.

  • JEP 193: Uchwyty zmiennych [19] — definiuje standardową metodę wywoływania odpowiedników różnych operacji java.util.concurrent.atomic i sun.misc.Niebezpiecznych operacji na polach obiektów i elementach tablicy, standardowym zestawie operacji ogrodzenia dla precyzyjnej kontroli kolejności pamięci oraz standardowej operacji ogrodzenia osiągalności w celu zapewnienia, że obiekt, do którego odwołuje się odwołanie, pozostaje silnie osiągalny.

  • JEP 269: Metody fabryki wygody dla kolekcji [20] — definiuje interfejsy API biblioteki, aby ułatwić tworzenie wystąpień kolekcji i map z małą liczbą elementów. Statyczne metody tworzące w interfejsach kolekcji, które tworzą kompaktowe, niemodyfikowalne wystąpienia kolekcji. Te wystąpienia są z natury bardziej wydajne. Interfejsy API tworzą kolekcje, które są kompaktowo reprezentowane i nie mają klasy otoki.

  • JEP 285: Spin-Wait Wskazówki [21] — udostępnia interfejs API, który umożliwia java wskazówki dotyczące systemu czasu wykonywania, który znajduje się w pętli spin. Na niektórych platformach sprzętowych korzystne jest wskazanie programowe, że wątek jest w stanie zajętości.

  • JEP 321: Klient HTTP (standard) [22] — udostępnia nowy interfejs API klienta HTTP, który implementuje protokół HTTP/2 i protokół WebSocket i może zastąpić starszy interfejs API HttpURLConnection.

Odwołania

[1] Oracle Corporation, "Java Development Kit 9 Release Notes", (Online). Dostępne: https://www.oracle.com/technetwork/java/javase/9u-relnotes-3704429.html. (Uzyskano dostęp 13 listopada 2019 r.).

[2] Oracle Corporation, "Java Development Kit 10 Release Notes", (Online). Dostępne: https://www.oracle.com/technetwork/java/javase/10u-relnotes-4108739.html. (Uzyskano dostęp 13 listopada 2019 r.).

[3] Oracle Corporation, "Java Development Kit 11 Release Notes", (Online). Dostępne: https://www.oracle.com/technetwork/java/javase/11u-relnotes-5093844.html. (Uzyskano dostęp 13 listopada 2019 r.).

[4] Oracle Corporation, "Project Jigsaw", wrzesień 22, 2017. (Online). Dostępne: http://openjdk.java.net/projects/jigsaw/. (Uzyskano dostęp 13 listopada 2019 r.).

[5] Oracle Corporation, "JEP 328: Flight Recorder", wrzesień 9, 2018. (online). Dostępne: http://openjdk.java.net/jeps/328. (Uzyskano dostęp 13 listopada 2019 r.).

[6] Oracle Corporation, "Mission Control", kwiecień 25, 2019. (online). Dostępne: https://wiki.openjdk.java.net/display/jmc/Main. (Uzyskano dostęp 13 listopada 2019 r.).

[7] Oracle Corporation, "JEP 158: Unified JVM Logging", luty 14, 2019. (online). Dostępne: http://openjdk.java.net/jeps/158. (Uzyskano dostęp 13 listopada 2019 r.).

[8] Oracle Corporation, "JEP 331: Low-Overhead Profilowanie stert", wrzesień 5, 2018. (online). Dostępne: http://openjdk.java.net/jeps/331. (Uzyskano dostęp 13 listopada 2019 r.).

[9] Oracle Corporation, "JEP 259: Stack-Walking API", lipiec 18, 2017. (online). Dostępne: http://openjdk.java.net/jeps/259. (Uzyskano dostęp 13 listopada 2019 r.).

[10] Oracle Corporation, "JEP 248: Make G1 the Default Garbage Collector", wrzesień 12, 2017. (online). Dostępne: http://openjdk.java.net/jeps/248. (Uzyskano dostęp 13 listopada 2019 r.).

[11] Oracle Corporation, "JEP 318: Epsilon: A No-Op Garbage Collector", wrzesień 24, 2018. (online). Dostępne: http://openjdk.java.net/jeps/318. (Uzyskano dostęp 13 listopada 2019 r.).

[12] Oracle Corporation, "JDK-8146115: Ulepszanie wykrywania kontenerów platformy Docker i użycia konfiguracji zasobów", wrzesień 16, 2019. (online). Dostępne: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8146115. (Uzyskano dostęp 13 listopada 2019 r.).

[13] Oracle Corporation, "JEP 238: Multi-Release JAR Files", czerwiec 22, 2017. (online). Dostępne: http://openjdk.java.net/jeps/238. (Uzyskano dostęp 13 listopada 2019 r.).

[14] Oracle Corporation, "JEP 197: Segmented Code Cache", kwiecień 28, 2017. (online). Dostępne: http://openjdk.java.net/jeps/197. (Uzyskano dostęp 13 listopada 2019 r.).

[15] Oracle Corporation, "JEP 254: Compact Strings", maj 18, 2019. (online). Dostępne: http://openjdk.java.net/jeps/254. (Uzyskano dostęp 13 listopada 2019 r.).

[16] Oracle Corporation, "JEP 310: Application Class-Data Sharing", 17 sierpnia 2018 r. (online). Dostępne: https://openjdk.java.net/jeps/310. (Uzyskano dostęp 13 listopada 2019 r.).

[17] Oracle Corporation, "JEP 312: Thread-Local Uzgadnianie", 21 sierpnia 2019 r. (online). Dostępne: https://openjdk.java.net/jeps/312. (Uzyskano dostęp 13 listopada 2019 r.).

[18] Oracle Corporation, "JDK-8198756: Leniwa alokacja wątków kompilatora", październik 29, 2018. (online). Dostępne: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8198756. (Uzyskano dostęp 13 listopada 2019 r.).

[19] Oracle Corporation, "JEP 193: Zmienne uchwyty", 17 sierpnia 2017. (online). Dostępne: https://openjdk.java.net/jeps/193. (Uzyskano dostęp 13 listopada 2019 r.).

[20] Oracle Corporation, "JEP 269: Convenience Factory Methods for Collections", czerwiec 26, 2017. (online). Dostępne: https://openjdk.java.net/jeps/269. (Uzyskano dostęp 13 listopada 2019 r.).

[21] Oracle Corporation, "JEP 285: Spin-Wait Hints", sierpień 20, 2017. (online). Dostępne: https://openjdk.java.net/jeps/285. (Uzyskano dostęp 13 listopada 2019 r.).

[22] Oracle Corporation, "JEP 321: HTTP Client (Standard)" 27 września 2018 r. (online). Dostępne: https://openjdk.java.net/jeps/321. (Uzyskano dostęp 13 listopada 2019 r.).