Learn · concepts

Co to jest JNI? Java Native Interface dla modderów

JNI (Java Native Interface) to wbudowany most w Javie, umożliwiający wywoływanie kodu C/C++ z JVM i z powrotem. Jest to standardowy sposób, w jaki mod Minecraft ładuje bibliotekę natywną.

TRtrolPublished 5 min read

Czym jest JNI w prostych słowach?

JNI (Java Native Interface) to część Javy, która łączy dwie sfery: zarządzany bytecode na JVM i skompilowany kod maszynowy napisany w C lub C++. Metoda otrzymuje słowo kluczowe native i nie posiada ciała, a w czasie działania JVM łączy ją z pasującą funkcją w załadowanej bibliotece natywnej. Następnie obie strony wymieniają argumenty i wartości zwracane.

Granica nie jest jednostronna. Funkcja natywna otrzymuje wskaźnik JNIEnv, a ten uchwyt jest bramą powrotną do Javy: udostępnia wywołania do odczytu i zapisu pól Javy, wywoływania metod Javy, konstruowania obiektów i zgłaszania wyjątków. Tak więc ten sam most, który pozwala Jawie wywoływać, pozwala również C wywoływać.

Dlaczego mod lub klient Minecraft musiałby sięgać po kod natywny?

Większość kodu Javy nie potrzebuje JNI. Mod sięga po niego, gdy sam JVM nie jest w stanie wykonać zadania, zwykle w przypadku grafiki, audio, integracji z systemem operacyjnym lub rutyny, która musi być szybka. Biblioteka natywna wykonuje ciężką pracę, a JNI jest standardowym sposobem na jej ładowanie i wywoływanie.

Ponownie wykorzystaj sprawdzoną bibliotekę C

Istniejący, dojrzały kod natywny już rozwiązuje ten problem. Przerabianie go na Javę byłoby marnowaniem wysiłku, dlatego mod łączy się z nim bezpośrednio.

Dotrzyj do systemu operacyjnego

Niektóre funkcje platformy nie posiadają czysto-jawowej API. Wejście natywne staje się jedyną drogą do nich.

Szybka droga

Szczególnie krótka lub niskopoziomowa rutyna może działać szybciej jako skompilowany kod natywny niż jako bytecode na JVM.

Sterowanie sprzętem i sterownikami

Renderowanie, dźwięk i dostęp do urządzeń często znajdują się za bibliotekami natywnymi, które JNI udostępnia stronie Java.

Jak działa wywołanie JNI od początku do końca?

Przebieg jest identyczny na każdej platformie: deklaruje się metodę w Javie, kompiluje pasującą funkcję C do biblioteki współdzielonej, ładuje się ją w czasie działania, pozwala się JVM połączyć je po nazwie, a następnie wywołuje się funkcję. Nazwy funkcji podążają za ustalonym wzorcem, aby JVM mogła je rozwiązać, a wygenerowany plik nagłówkowy określa dokładną sygnaturę do zaimplementowania.

KrokCo się dzieje
DeklaracjaMetoda Java jest pisana ze słowem kluczowym native i pozostawiona pusta po stronie Java.
KompilacjaFunkcja C lub C++ jest kompilowana do biblioteki współdzielonej: .dll na Windows, .so na Linux, .dylib na macOS.
ŁadowanieJava wywołuje System.loadLibrary w celu załadowania tej biblioteki podczas działania programu.
ŁączenieJVM dopasowuje każdą metodę native do jej funkcji C za pomocą nazwy zniekształconej sygnatury.
WywołanieJava wywołuje metodę; sterowanie przechodzi do kodu natywnego i zwraca wynik.

Co kosztuje Cię JNI?

JNI jest potężne, ale ma ostre krawędzie. Ponieważ natywny kod działa poza bezpiecznikiem JVM, błąd w nim powoduje awarię całego procesu zamiast zgłoszenia wychwytalnego wyjątku. Pamięć przydzielona po stronie natywnej jest niewidoczna dla garbage collectora, więc wycieki się nagromadzają, chyba że zostaną zwolnione ręcznie. Każde przekroczenie granic wiąże się z narzutem, co sprawia, że wywoływanie przez granicę wewnątrz ciasnej pętli jest kosztowne. A kompilacje natywne są zablokowane pod konkretną platformę: plik .dll dla systemu Windows nie załaduje się na Linuksie bez oddzielnej kompilacji.

Co to kupuje

  • Bezpośredni dostęp do istniejących bibliotek C i C++
  • Dostęp do funkcji systemu operacyjnego i sprzętu, które JVM nie może osiągnąć
  • Wykonywanie z prędkością natywną dla najbardziej obciążonych fragmentów kodu

Co to kosztuje

  • Natywny błąd może zaważyć na całej JVM
  • Ręczne zarządzanie pamięcią bez zabezpieczenia w postaci garbage collectora
  • Budowanie dla każdej platformy w celu utrzymania i dystrybucji
  • Narzut związany z przekraczaniem granic w gorących pętlach

JNI w porównaniu z API Funkcji Obcych i Pamięci

JNI to oryginalny mechanizm, ale nie jest już jedynym. Nowsze wersje Javy zawierają API Funkcji Obcych i Pamięci (FFM), zaprojektowane do wywoływania kodu natywnego z mniejszą ilością kodu pomocniczego i silniejszymi gwarancjami bezpieczeństwa. JNI wciąż dominuje w istniejących środowiskach programistycznych, ponieważ jest powszechne i dobrze rozumiane.

JNIAPI FFM
WiekOryginalne, obecne od wczesnych wersji JavyNowoczesne, dodane w ostatnich wydaniach
Kod pomocniczyGenerowane nagłówki, funkcje C z przekształconymi nazwamiObsługi metody zdefiniowane w Javie, brak kodu C pośredniczącego
Bezpieczeństwo pamięciRęczne; błędy mogą uszkodzić JVMOgraniczone segmenty z silniejszymi kontrolami
ZastosowaniePowszechne w istniejących bibliotekachRosnące, nowsze kody bazowe

FAQ

Pobierz Terminus

Stworzone przez ludzi, którzy naprawdę czytają wnętrzności JVM.