Cześć wszystkim! Witajcie w drugim poście z naszej serii o Quarkus – dzisiaj porozmawiamy o kompilacji natywnej.

– to stos technologiczny Java, dostosowany do . I choć wciąż jest wiele do zrobienia, dobrze opracowaliśmy wiele aspektów, w tym optymalizację JVM oraz szereg frameworków. Jedną z cech Quarkusa, która wzbudziła duże zainteresowanie wśród programistów, jest kompleksowe, bezszwowe podejście do przekształcania kodu Java w pliki wykonywalne dla konkretnego systemu operacyjnego (tak zwana „kompilacja natywna”), w podobny sposób jak w C i C++, gdzie ta kompilacja zazwyczaj ma miejsce na końcu cyklu składającego się z budowy, testowania i wdrażania.
I choć kompilacja natywna, jak pokażemy poniżej, jest ważna, należy zauważyć, że Quarkus naprawdę dobrze działa również na zwykłej maszynie Java OpenJDK Hotspot dzięki udoskonaleniom wydajności, które wprowadziliśmy w całym stosie. Dlatego kompilację natywną warto traktować jako dodatkowy bonus, który można wykorzystać według potrzeby. W rzeczywistości Quarkus w dużej mierze opiera się na OpenJDK dla obrazów natywnych. A dobrze przyjęty przez programistów tryb dev mode zapewnia niemal natychmiastowe testowanie zmian dzięki rozwiniętym możliwościom dynamicznego wykonywania kodu, wdrożonym w Hotspot. Ponadto, podczas tworzenia obrazów natywnych, GraalVM wykorzystuje bibliotekę klas OpenJDK oraz możliwości HotSpot.
Więc po co w ogóle potrzebna jest kompilacja natywna, skoro wszystko jest tak dobrze zoptymalizowane? Na to pytanie postaramy się odpowiedzieć w dalszej części.
Zacznijmy od oczywistości: Red Hat ma ogromne doświadczenie w optymalizacji JVM, stosów i frameworków w trakcie rozwoju projektu , w tym:
- Pierwszy serwer aplikacji do pracy w chmurze na platformie .
- Pierwszy serwer aplikacji do pracy na komputerach .
- Pierwszy serwer aplikacji do pracy na .
- Cała seria projektów działających na urządzeniach .
Od wielu lat zajmujemy się problemami uruchamiania aplikacji Java w chmurze i na urządzeniach o ograniczonych zasobach (czytaj: IoT) i nauczyliśmy się wyciskać z JVM maksimum pod względem wydajności i optymalizacji pamięci. Jak wielu innych, od lat pracujemy z kompilacją natywną aplikacji Java za pomocą , , i nawet Doskonale zdajemy sobie sprawę z plusów i minusów takiego podejścia (na przykład dylematu wyboru między uniwersalnością „build once – run-anywhere” a tym, że skompilowane aplikacje mają mniejszy rozmiar i szybciej się uruchamiają).
Dlaczego tak ważne jest uwzględnienie tych plusów i minusów? Ponieważ w niektórych sytuacjach ich stosunek staje się decydujący:
- Na przykład w środowiskach serverless/zależnych od zdarzeń, gdzie w trybie (surowym lub miękkim) rzeczywistym, aby zdążyć z reakcją na zdarzenia. W odróżnieniu od długo żyjących usług, tutaj długość zimnego uruchomienia krytycznie zwiększa czas reakcji na zapytanie. Uruchomienie JVM nadal zajmuje znaczną ilość czasu, a chociaż w niektórych przypadkach można to skrócić wyłącznie metodami sprzętowymi, różnica między jedną sekundą a 5 milisekundami może być kwestią życia i śmierci. Tak, można tu eksperymentować z tworzeniem gorącej rezerwy maszyn Java (co zrobiliśmy na przykład podczas ), ale samo w sobie to nie gwarantuje wystarczającej liczby JVM do obsługi zapytań w miarę skalowania obciążenia. A z ekonomicznego punktu widzenia to z pewnością nie jest najrozsądniejsza opcja.
- Ponadto pojawia się jeszcze jeden często poruszany aspekt, jakim jest wielodostępność. Mimo że możliwości JVM znacznie zbliżyły się do systemów operacyjnych, wciąż nie są w stanie robić tego, do czego jesteśmy przyzwyczajeni w Linuxie – izolować procesy. Dlatego awaria jednego wątku może sparaliżować całą maszynę Java. Wiele osób próbuje obejść tę wadę, przydzielając aplikacjom każdego użytkownika osobną JVM, aby zminimalizować skutki awarii. To jest całkowicie logiczne, ale źle komponuje się ze skalowaniem.
- Dodatkowo, dla aplikacji chmurowych istotny jest taki wskaźnik, jak gęstość usług na hoście. Przejście na metodologię , mikroserwisy i Kubernetes zwiększają liczbę maszyn Java dla jednej aplikacji. Z jednej strony daje to elastyczność i niezawodność, ale z drugiej strony rośnie również wydatek pamięci bazowej na serwis, a część tych wydatków nie zawsze jest ściśle niezbędna. Statycznie skompilowane pliki wykonywalne zyskują tutaj dzięki różnym technikom optymalizacji, takim jak niskopoziomowe usuwanie martwego kodu, kiedy do finalnego obrazu zostają dołączone tylko te części frameworków (włączając w to sam JDK), które serwis rzeczywiście wykorzystuje. Dlatego natywna kompilacja Quarkus pomaga gęściej rozmieszczać instancje serwisów na hoście bez uszczerbku dla bezpieczeństwa.
Sprzyjające powyższym argumentom już wystarczająco tłumaczą zasadność natywnej kompilacji z perspektywy uczestników projektu Quarkus. Jednak jest jeszcze jeden, nie techniczny, ale również ważny powód: w ostatnich latach wielu programistów i firm programistycznych zrezygnowało z Javy na rzecz nowych języków programowania, uznając, że Java wraz z jej JVM, stosami i frameworkami stała się zbyt pamięciożerna, zbyt wolna itp.
Jednak przyzwyczajenie do używania tego samego narzędzia do rozwiązywania wszelkich problemów – . Czasami lepiej zrobić krok wstecz i poszukać czegoś innego. A jeśli Quarkus zmusza ludzi do zatrzymania się i zastanowienia, to jest to korzystne dla całego ekosystemu Javy. Quarkus urzeczywistnia nowatorskie podejście do tworzenia bardziej wydajnych aplikacji, czyniąc Javę bardziej odpowiednią dla nowych architektur aplikacji, takich jak serverless. Ponadto, dzięki swojej rozbudowie, Quarkus, jak mamy nadzieję, zyska całą ekosystem Java-rozszerzeń, znacznie zwiększając liczbę frameworków, które od razu będą wspierać natywną kompilację w ramach aplikacji.
Źródło: habr.com
