Зачем в Java несколько загрузчиков классов и почему не один
Что проверяют
Понимаете ли вы, как JVM находит и различает классы и зачем серверам приложений и плагинам свои загрузчики. Следом почти всегда спрашивают про модель делегирования и про ClassCastException вида «Foo cannot be cast to Foo».
Как отвечать
Начните с иерархии, актуальной с Java 9:
- bootstrap: встроен в JVM, грузит базовые модули вроде
java.base. Для его классовgetClassLoader()возвращаетnull; - platform: остальные модули платформы. Он заменил extension loader, каталога
extбольше нет; - application (system): classpath и module path приложения.
Работают они по модели делегирования: загрузчик сначала спрашивает родителя и грузит класс сам, только если тот не нашёл. Поэтому подменить java.lang.String своим классом нельзя.
Главный аргумент: класс в JVM определяется парой из полного имени и загрузчика. Отсюда причины держать несколько загрузчиков:
- изоляция: Tomcat даёт каждому веб-приложению свой загрузчик, и два приложения живут с разными версиями одной библиотеки. Для классов приложения он сначала ищет у себя, отступая от делегирования;
- выгрузка: класс выгружается только вместе со своим загрузчиком, когда тот собран GC. На этом держатся редеплой без перезапуска JVM и рестарт в Spring Boot DevTools, где классы проекта грузит отдельный загрузчик;
- нестандартные источники: вложенные jar внутри fat jar Spring Boot, сгенерированный байткод;
- плагины: IDE, Jenkins, OSGi.
Упомяните и контекстный загрузчик потока (Thread.getContextClassLoader()): через него ServiceLoader находит реализации из кода приложения.
Где спотыкаются
Называют иерархию bootstrap, extension, system и приводят SecurityManager как главный довод. Extension loader убран в Java 9, а SecurityManager объявлен устаревшим в Java 17 и отключён в JDK 24.
Не могут объяснить ClassCastException между одноимёнными классами: один и тот же .class, загруженный двумя загрузчиками, для JVM это два разных типа.
Забывают про утечки при редеплое. Если на загрузчик приложения ссылается ThreadLocal или драйвер в DriverManager, он не выгружается, и Metaspace растёт с каждым деплоем.