ProgrammazioneSenior Java Backend разработчик

Как работает механизм загрузки классов (class loading) в Java, какие существуют типы загрузчиков классов и какие возможны проблемы при их неправильном использовании?

Supera i colloqui con l'assistente IA Hintsage

Ответ.

В Java классы загружаются в память специальными объектами — класс-лоадерами (class loaders). Каждый класс-лоадер формирует своё собственное пространство видимости классов.

Виды класс-лоадеров:

  1. Bootstrap ClassLoader — загружает базовые классы JDK (rt.jar и т.п.).
  2. Extension ClassLoader — загружает расширения из $JAVA_HOME/lib/ext.
  3. System (Application) ClassLoader — загружает классы из classpath приложения.
  4. Custom ClassLoader — пользовательские загрузчики для динамической загрузки.

Особенности:

  • Класс считается уникальным в рамках сочетания (class loader, имя класса).
  • Два одинаковых класса, загруженных разными загрузчиками, считаются разными.
  • Класс-лоадеры образуют иерархию (родитель-потомок).
  • Можно загружать классы из байтов (defineClass) — актуально для плагинов/JSP и пр.

Вопрос с подвохом.

Можно ли загрузить несколько версий одного и того же класса в процесс JVM, и как это сделать?

Ответ: Да, возможно, если использовать разные class loaders. Классы с одинаковым FQDN, загруженные разными загрузчиками, считаются разными типами для JVM.

Пример:

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

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В крупной серверной системе использовали плагины, каждый из которых подключался через отдельный custom classloader. Плагины обменивались объектами через общий интерфейс, также загруженный своим classloader'ом. Возникало ClassCastException при приведении типов, так как interface 'PluginApi' из classloader плагина и основной системы считался разными типами.


История

Попытка "горячей" перезагрузки сервлета через custom classloader привела к утечкам памяти — старый класс из памяти не выгружался, поскольку в статических переменных где-то оставалась ссылка. В результате PermGen быстро переполнялся.


История

Продукт с поддержкой динамической загрузки модулей неявно использовал системный class loader, а разработчики случайно подменили его родителя, потеряв доступ к базовым классам (например, из JDK). Это проявилось в падениях, когда новый модуль не мог загрузить стандартные Java-классы, например, java.sql.Driver.