Карта / JVM под капотом / Загрузчики классов (ClassLoader)

Загрузчики классов (ClassLoader)

ClassLoader загружает .class-файлы в JVM в момент первого использования класса — лениво, не все сразу при старте. Загрузчиков несколько, они выстроены в иерархию:

Bootstrap ClassLoader java.lang.String, java.util.* … Platform ClassLoader модули платформы (java.sql, …) Application ClassLoader JDK (C++) JDK твой код

Принцип родительского делегирования (Parent Delegation): запрос на загрузку класса сначала идёт вверх к Bootstrap, и только если тот не нашёл — обрабатывает дочерний. Это защищает системные классы: никто не может подменить java.lang.String своим классом из classpath.

// Посмотреть загрузчик класса
System.out.println(String.class.getClassLoader());   // null — Bootstrap (native)
System.out.println(MyClass.class.getClassLoader());  // AppClassLoader
Копнуть глубже

Когда класс загружается. Загрузка происходит при первом из событий:

  • создание экземпляра (new MyClass())
  • обращение к статическому полю или методу
  • явная загрузка: Class.forName("com.app.MyClass")

После загрузки идут линковка (верификация байт-кода + разрешение ссылок) и инициализация (выполнение static {}). Статический блок выполняется ровно один раз при первой загрузке.

Custom ClassLoader — для особых случаев: загрузка из сети/БД, hot-reload (Spring DevTools, JRebel), изоляция зависимостей. Tomcat использует свой ClassLoader чтобы изолировать разные веб-приложения друг от друга:

public class MyLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = loadBytesFromNetwork(name);
        return defineClass(name, bytes, 0, bytes.length);
    }
}

Проблема «разных ClassLoader’ов». Класс com.App загруженный ClassLoader’ом A и тот же класс загруженный ClassLoader’ом B — разные типы для JVM. Причина загадочных ClassCastException в OSGi, Tomcat, и при сериализации между classloader’ами.

🎤 Закрыл тему, если можешь объяснить:
• зачем нужна иерархия ClassLoader'ов и принцип родительского делегирования;
• когда именно загружается класс и что выполняет static {} блок (если дошёл до 2-го слоя).