programowanieStarszy programista backend w Javie

Jak działa mechanizm ładowania klas (class loading) w Javie, jakie istnieją rodzaje loaderów klas i jakie mogą być problemy przy ich niewłaściwym użyciu?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Javie klasy są ładowane do pamięci przez specjalne obiekty — loader'y klas (class loaders). Każdy loader klas tworzy swoją własną przestrzeń widoczności klas.

Rodzaje loaderów klas:

  1. Bootstrap ClassLoader — ładuje podstawowe klasy JDK (rt.jar itd.).
  2. Extension ClassLoader — ładuje rozszerzenia z $JAVA_HOME/lib/ext.
  3. System (Application) ClassLoader — ładuje klasy z classpath aplikacji.
  4. Custom ClassLoader — niestandardowe loader'y dla dynamicznego ładowania.

Cechy szczególne:

  • Klasa jest uważana za unikalną w obrębie kombinacji (class loader, nazwa klasy).
  • Dwie identyczne klasy, załadowane przez różne loadery, są uważane za różne.
  • Loader'y klas tworzą hierarchię (rodzic-potomek).
  • Można ładować klasy z bajtów (defineClass) — aktualne dla wtyczek/JSP itd.

Pytanie z podstępem.

Czy można załadować kilka wersji tej samej klasy w procesie JVM, i jak to zrobić?

Odpowiedź: Tak, jest to możliwe, jeśli użyjesz różnych loaderów klas. Klasy z tym samym FQDN, załadowane przez różne loadery, są uważane za różne typy dla JVM.

Przykład:

ClassLoader loader1 = new URLClassLoader(new URL[]{...}); ClassLoader loader2 = new URLClassLoader(new URL[]{...}); Class clazz1 = loader1.loadClass("com.example.MyClass"); Class clazz2 = loader2.loadClass("com.example.MyClass"); System.out.println(clazz1 == clazz2); // false

Przykłady rzeczywistych błędów z powodu nieznajomości szczegółów tematu.


Historia

W dużym systemie serwerowym korzystano z wtyczek, z których każda była łączona przez osobny custom classloader. Wtyczki wymieniały obiekty przez wspólny interfejs, również załadowany przez swój classloader. Wystąpił ClassCastException przy rzutowaniu typów, ponieważ interfejs 'PluginApi' z classloader'a wtyczki i głównego systemu był uważany za różne typy.


Historia

Próba „gorącego” ponownego ładowania serwletu przez custom classloader doprowadziła do wycieków pamięci — stara klasa nie była usuwana z pamięci, ponieważ w zmiennych statycznych gdzieś pozostawał wskaźnik. W rezultacie PermGen szybko się przepełniał.


Historia

Produkt wspierający dynamiczne ładowanie modułów niejawnie używał systemowego loadera klas, a deweloperzy przypadkowo podmienili jego rodzica, tracąc dostęp do podstawowych klas (np. z JDK). Objawiło się to w awariach, gdy nowy moduł nie mógł załadować standardowych klas Javowych, takich jak java.sql.Driver.