Гарантирован ли загрузчик классов Java не загружать неиспользуемые классы?

Есть ли гарантия, что загрузчик классов Java (по умолчанию, системный) не пытается загружать классы, на которые нет ссылок в выполняемом коде? Несколько примеров того, что я имею в виду:

  • Я использую framework.jar который я знаю, чтобы содержать ссылки на другой library.jarклассы в нем, но я использую только ту часть фреймворка, которая не содержит этих ссылок. Это безопасно оставить library.jar из?
  • Статические блоки запускаются при первой загрузке класса. Если ни один запущенный код не содержит ссылок на определенный класс, уверен ли он, что его статический блок не запущен?

Быстрое тестирование, кажется, работает, как предполагалось выше, и в любом случае не имеет смысла загружать неиспользуемые классы, но есть ли на это гарантия?

Дополнение: Кажется, что мои "статические блоки запускаются, когда класс загружается впервые", приведенное выше утверждение несколько неверно. Определенно можно загружать классы (одно), не запуская их (другое). Так что я заинтересован в обоих случаях; гарантии о том, что классы не загружаются и не запускаются.

7 ответов

Решение

Нет такой гарантии 1 по загрузке классов.

Однако вам гарантировано, что статические блоки не будут запущены преждевременно. События, которые инициируют инициализацию класса, определены в JLS 12.4.1.

Класс T или интерфейсный тип T будут инициализированы непосредственно перед первым появлением любого из следующего:

  • T является классом, и экземпляр T создан.
  • T является классом, и вызывается статический метод, объявленный T.
  • Статическое поле, объявленное T, присваивается.
  • Используется статическое поле, объявленное T, и поле не является константной переменной (§4.12.4).
  • T является классом верхнего уровня, и выполняется оператор assert (§14.10), лексически вложенный в T.

1 - Замечено, что реализации Java текущего поколения не загружают классы без необходимости, но это не гарантия. Единственные гарантии - это то, что написано в официальных спецификациях.

Спецификация Java утверждает

Процесс загрузки реализуется классом ClassLoader и его подклассами. Разные подклассы ClassLoader могут реализовывать разные политики загрузки. В частности, загрузчик классов может кэшировать двоичные представления классов и интерфейсов, предварительно выбирать их на основе ожидаемого использования или загружать группу связанных классов вместе.

Таким образом, загрузчик классов может предварительно выбирать файлы классов.

Класс T или интерфейсный тип T будут инициализированы непосредственно перед первым появлением любого из следующего:

  • T является классом, и экземпляр T создан.
  • T является классом, и вызывается статический метод, объявленный T.
  • Статическое поле, объявленное T, присваивается.
  • Используется статическое поле, объявленное T, и ссылка на поле не является константой времени компиляции (§15.28). Ссылки на константы времени компиляции должны быть преобразованы во время компиляции в копию значения константы времени компиляции, поэтому использование такого поля никогда не вызывает инициализацию.

Статические блоки будут выполняться только при первом использовании класса.

То, что тянет в классах, это ссылки на них из Java-байтового кода (который снова может тянуть в других классах). Если классы, которые вы запускаете, не имеют никакой ссылки на класс X, он не будет загружен.

Тем не менее, обратите внимание, что существуют более новые способы регистрации, например, услуг через META-INF. Эти классы тоже должны быть загружены.

Вы всегда можете запустить с "-verbose", чтобы увидеть, как классы загружаются - порядок ясно показывает, что они загружаются при необходимости.

Я не думаю, что есть такая гарантия. Во-первых, я видел сканеры кода, которые выполняют такие вещи, как обработка аннотаций из целых иерархий /JAR-пакетов во время запуска приложения; они бы сразу нарушили это предположение.

Почему это важно? Вы обычно после сильно контролируемой загрузки системы, так что все, что имеет значение, будет где-то, где вы все равно хотите форсировать это...

Если вы не используете рефлексию, то вы можете статически проверить, какие классы используются, используя инструмент удаления мертвого кода, такой как ProGuard. Он проанализирует ваш код и определит все используемые классы. На основании этого он удаляет неиспользуемый код, включая неиспользуемый код в библиотеках.

Если ваш код или библиотеки используют отражение для загрузки классов, вам нужно будет выполнить полный тест покрытия вашего приложения и записать в журнал все загруженные классы, которые вы поручили ProGuard сохранить.

Там нет таких гарантий, как упоминали другие плакаты. Но ваш вопрос и ваше беспокойство не следуют друг от друга. Для того, чтобы вы оставили библиотеку. Вам не нужны такие гарантии.

Существует ряд платформ, которые обнаруживают наличие или отсутствие других платформ во время выполнения. Пример: Commons-logging открывает кучу других фреймворков. Веб-поток Spring обнаруживает структуру сценариев (например, OGNL) во время выполнения. Эти платформы, очевидно, скомпилированы с использованием всех зависимых структур, но они не должны существовать во время выполнения.

Следовательно, вполне допустимо оставлять файл library.jar во время выполнения.

Да.

Подумайте о следующем. Если вы добавите следующий код к этому library.jar учебный класс:

 public ShutDown {static { System.exit(-1); }}

Этот код не будет автоматически загружаться системой по умолчанию, поскольку ни один из существующих кодов не имеет ссылки или не знает о ShutDown У класса также не было возможности загрузить его, и загрузчик классов Java не просто пытается загрузить случайные классы из фляги.

Порядок загрузки классов описан в предыдущих ответах. Если вы внимательно изучите их, ни один из них не будет содержать "Если в jar есть класс, он будет загружен" какого-то рода.

Другие вопросы по тегам