No, tego-tygodniowe wydanie zdecydowanie zdominował Project Loom. Ci, którzy nie czekają na tego najbardziej w ostatnich latach oczekiwanego javowego „ficzura” znajdą dla siebie jednak nieco praktyczniejsze rzeczy, choćby pod postacią Cleanerów oraz Micronauta 3.5. Zapraszam do lektury
1. Społeczność testuje Project Loom
Tak jak się spodziewaliśmy, dostaliśmy istny wysyp tekstów dotyczących Projektu Loom. Wiele z nich jest naprawdę interesujących, dlatego zaczniemy od małej selekcji.
Zacznijmy od publikacji naszego starego znajomego, Gunnara Morlinga, który już kiedyś rozpracowywał dla nas tajniki formatu pliku Java Flight Recordera. Tym razem do publikacji zainspirowała go dyskusja między Ronem Presslerem (twórcą Looma), a Timem Foxem z Vert.x, z której dowiadujemy się o pewnych nieoczywistych cechach implementacji Looma. Otóż scheduler Looma nie ma w zasadzie pojęcia o tym, czy metoda jest blokująca czy nie – modyfikacji został za to poddany model blokowania klasycznego JDK, który został “nauczony” uwalniania wątku systemu operacyjnego gdy prosi go o to Wirtualny (loomowski) Wątek. Pojawiły się jednak wątpliwości na temat Uczciwość (Fairness) takiego rozwiązania i ryzyko “zagłodzenia” konkretnych wątków, które pojawić się może w wypadku zadań intensywnie wymagających nie IO, a CPU. Nie istnieje bowiem mechanizm, który pozwoliłby na na wywłaszczenie takiego długotrwającego zadania opartego na CPU.
Gunnar w swojej publikacji prezentuje kilka wykresów, które bardzo klarownie tłumaczą, jak ten problem objawia się w praktyce. W uproszczeniu – jeśli wystartujemy więcej długożyjących wątków niż jest dostępne realnych egzekutorów, w wypadku klasycznych Threadów wszystkie kończą się mniej więcej w tym samym czasie. W Loomie ten rozkład będzie niejednorodny – część wątków zakończy się szybciej niż u poprzednika, ale na wiele z nich będziemy musieli poczekać dłużej. Ron Pressler przekonuje, że w praktyce nie będzie stanowiło to większego problemu, a już istnieje też gotowy kod, który ma to rozwiązać – jeszcze nie został po prostu zmergowany. Ogólnie problem jest rozwiązywalny – np. programiści Go jakoś sobie z tym poradzili poprzez umożliwienie “wywłaszczania” gorutyny dłuższej niż 10ms. No i od tego mamy właśnie wersję Preview żeby tego typu rzeczy wyłapywać.
Oprócz ograniczeń (czy raczej braki ograniczeń?) związanych z procesorem, powinny się też pojawiać te z użyciem pamięci. W końcu każdy wątek jest zasobem, który wymaga (nawet jeśli minimalnych) resourców systemowych. Eksperymenty Heinza M. Kabutza z periodyku JavaSpecialist pokazują jednak, że limity mogą być o wiele dalej niż na zdrowy rozum powinny się znajdować. W jego małym eksperymencie, “fizyczny” limit (biorąc pod uwagę użycie pamięci) ilości stworzonych wirtualnych wątków powinien wynosić około 8 milionów – jego snippet (którego znajdziecie pod linkiem Gazillion Virtual Threads) był w stanie wykręcić 36 miliardów! Jak to możliwe? Otóż okazuje się, że parkowane (zatrzymane) wątki nie zajmują w zasadzie pamięci i można ich tworzyć ile fabryka dała. Wadą jest to, że mogą być w zasadzie w dowolnym momencie ściągnięte przez GC.
Kontynuując temat Looma, poza tworzeniem kosmicznej ilości zasobów społeczność już zaczyna wynajdować nowe sposoby na użycie tej technologii, niektóre bardzo kreatywne. Fanatycy systemów rozproszonych z pewnością kojarzą Jepsena – narzędzie do weryfikacje zapewnień twórców baz i systemów co do ich spójności (takiej w rozumieniu “consistency” w CAP Theorem – bo to wcale nie jest takie oczywiste). Z artykułu Jamesa Bakera (który pracuje jako Engineering Group Lead w Palantir Technologies) dowiecie się nie tylko jak działa Jepsen (a także rykoszetem FoundationDB – naprawdę fascynująca sprawa), ale również dlaczego Loom może być bardzo dobrym narzędziem dla każdego, kto potrzebuje zasymulować dziwne przypadki brzegowe jeśli chodzi o kolejność zapisów i odczytów. Okazuje się, że kontrola przepływu, którą dostajemy wraz z nową zabawką na JVMie jest na tyle deterministyczna, że można ją używać również w tym kontekście. Using Java’s Project Loom to build more reliable distributed systems to kawał mocno krwistego mięcha dla każdego, kto wie czym jest Paxos czy zegary logiczne.
Na zakończenie, skoro już jesteśmy w temacie tekstów na temat Looma, to pozwolę sobie przypomnieć o klasycznym (no bo sierpień 2020 to już chyba klasyk) artykule On the performance of user-mode threads and coroutines autorstwa Rona Presslera. Dla osób niezaznajomionych z tematem jest on bowiem (całkiem przystępną, mimo sporej ilości wzorów) podstawą do dyskusji na temat wydajności Looma.
A, i Structured Concurrency jednak pojawi się w inkubacji w JDK 19. To wydanie zapowiada się jeszcze ciekawiej niż ostatnio.
Źródła
- Loom and Thread Fairness – Gunnar Morling
- Problem ucztujących filozofów – Wikipedia, wolna encyklopedia
- [JavaSpecialists 301] – Gazillion Virtual Threads
- Using Java’s Project Loom to build more reliable distributed systems · James Baker
- On the performance of user-mode threads and coroutines – Inside.java
Zainstaluj teraz i czytaj tylko dobre teksty!
2. Alternatywa dla Finalizerów – JDK 9 i Cleaners
No, ale Loom to akurat przyszłość JVM. Wiecie co jest jego przeszłością? Finalizery. I Oracle starał się to nam usilnie uzmysłowić w ostatnim tygodniu, publikując dwa teksty dotyczące ich alternatywy, czyli Cleanerów.
Czym są finalizery? Pewnie większość z naszych czytelników zdaje sobie sprawę, ale dla wyrównania poziomu – mówimy tutaj o specjalnych blokach kodu w ramach obiektu, które odpalają się wraz z czyszczeniem przez Garbage Collector obiektów. Pozwalały min. na czyszczenie otwartych powiązań do plików czy połączeń do bazy – ogólnie rzeczy znajdujących się poza obszarem JVM. Brzmi dobrze w teorii, ale finalizery przez lata wypracowały sobie (zasłużenie) funkcjonalności, której ciężko zaufać, gdyż w wielu przypadkach po prostu… nie działały. To, a także poziom skomplikowania związany z ich implementacją (co pewnie w jakiś sposób się łączy) sprawiły, że twórcy JDK postanowili ostatecznie się ich pozbyć, więc w JDK 18 doczekały się deprekacji w ramach JEP 421: Deprecate Finalization for Removal. Nie ma nad czym płakać, gdyż przez lata JVM doczekał się wielu alternatywnych, bardziej wyspecjalizowanych rozwiązań, jak try-with-resources czy właśnie Cleanery.
Cleanery pojawiły się w JDK 9 i stanowią drugie podejście do czyszczenia stanu po wyczyszczeniu obiektu przez Garbage Collector. Rozwiązują jednak poważny problem, który towarzyszą finalizerom. Ta zdeprecatowana funkcjonalność definiowała bowiem zachowanie, które należy uruchomić po czyszczeniu jako… część czyszczonego obiektu. Prowadziło to do urokliwych wyścigów, czy funkcja czyszcząca uruchomi się, zanim obiekt zostanie ostatecznie usunięty przez GC.
Cleanery rozwiązują ten problem w bardzo sprytny sposób – zamiast być częścią obiektu, tworzą (w formie lambdy) dodatkowy obiekt, rejestrowany równolegle do czyszczonego przez nie obiektu. Dzięki temu nie dochodzi do kuriozalnych race condition.
class Foo {
private static final Cleaner cleaner = Cleaner.create();
private final char[] data;
Foo(char[] chars) {
final char[] array = chars.clone();
cleaner.register(this,
() -> Arrays.fill(array, (char)0));
this.data = array;
}
}
Jeżeli chcecie dowiedzieć się więcej, oficjalny blog inside.java republikował u siebie dwie świetne publikacje, będące bardzo dobrym opracowaniem tematu: Replacing finalizers with cleaners oraz Testing clean cleaner cleanup opublikowane oryginalnie na blogu Musing on Java Core Libraries. W nich znajdziecie więcej szczegółów, a także przykładów do czego można użyć cleanerów, jeśli jeszcze nie macie pomysłów na eksplorację tej funkcjonalności.
Źródła
Zainstaluj teraz i czytaj tylko dobre teksty!
3. Release Radar: Micronaut 3.5
Nowy tydzień, nowy Radar. Dość skromny, ponieważ tylko z jednym wydaniem, i też nie jakoś mocno wybijającym się, ale dla porządku stwierdziłem, że warto nim zakończyć tego tygodniowy przegląd.
Otóż w zeszłym tygodniu swoją premierą miał nowy Micronaut 3.5. Poza podbiciem zależności i kompatybilności (min. z nowym GraalVM) przynosi on w zasadzie trzy nowe duże dodatki dla programistów. Posiadaczy dużych projektów w Micronaucie (co samo w sobie wydaje mi się być ciekawym konceptem, choć i te mniejsze powinny skorzystać) ucieszy z pewnością wprowadzenie inkrementalnej kompilacji dla buildów Gradle, co powinno skrócić czas budowania i dać lepszy developer experience. Mniej osób zainteresuje pewnie wsparcie dla Turbo – w Micronaut Views. Jak piszą sami twórcy:
Turbo to zbiór technik umożliwiających tworzenie szybkich, stopniowo ulepszonych aplikacji internetowych bez użycia dużej ilości JavaScriptu. Cała logika jest przechowywana na serwerze, a przeglądarka zajmuje się tylko końcowym kodem HTML.
Wychodząc poza banał, Turbo ma kilka interesujących asów w rękawie, jak choćby “pchanie” HTMLa do przeglądarki za pomocą WebSocketów. Jeśli mierzi Was tworzenie SPA, Turbo może być interesującą alternatywą. Za całością stoją ludzie tworzący wcześniej Ruby on Rails, a ten framework przez lata uchodził za szczyt programistycznej produktywności.
Ostatnim z dużych dodatków jest nowy moduł, Micronaut MicroStream. MicroStream to swoista “serializacja na sterydach”, silnik persystencji do przechowywania obiektów Javowych w wielu różnych formatach i storage, takich jak choćby S3 czy MongoDB. Oryginalnie część innego frameworka, Helidona, doczekała się teraz integracji z Micronautem
Mały bonusik: Spring.io opubklikował też w zeszłym tygodniu tekst Preparing for Spring Boot 3.0. W nim znajdziecie kroki, które warto wykonać przed szykowaną na jesień dużą premierą dużej wersji. Poza oczywistościami pokroju upgrade do Javy 17 i sprawdzenia czy projekt działa, jest tam trochę mniej oczywistych sugestii. Dlatego już dzisiaj sugeruje spojrzeć, może czas już powoli szykować taski na backlogu?