Спецификация языка Java подробно описывает механизм загрузки, выгрузки и перезагрузки классов.
Загрузка относится к процессу поиска двоичной формы типа class
или interface
с конкретным именем, возможно, путем его вычисления на лету, но более обычно путем извлечения двоичного представления, предварительно вычисленного из исходного кода компилятором, и создания из этой двоичной формы объекта Class
для представления класса или интерфейса.
Точная семантика загрузки дана в главе 5 Спецификации виртуальной машины Java (всякий раз, когда мы ссылаемся на спецификацию виртуальной машины Java в этой книге, мы имеем в виду второе издание с поправками, внесенными в JSR 924). Здесь мы представляем обзор процесса с точки зрения языка программирования Java.
Бинарный формат класса или интерфейса обычно представляет собой формат файла класса, описанный в приведенной выше спецификации виртуальной машины Java, но возможны и другие форматы при условии, что они соответствуют требованиям, указанным в §13.1. Метод defineClass
из class ClassLoader
может использоваться для создания Class
объектов из двоичных представлений в формате файла класса.
При определенных обстоятельствах возможно выгрузить классы и интерфейсы, что может вызвать непредсказуемую перезагрузку.
Реализация языка программирования Java может выгружать классы. Класс или интерфейс могут быть выгружены, если и только если их определяющий загрузчик класса может быть восстановлен сборщиком мусора, как обсуждалось в §12.6. Классы и интерфейсы, загруженные загрузчиком начальной загрузки, не могут быть выгружены.
Разгрузка классов - это оптимизация, которая помогает сократить использование памяти. Очевидно, что семантика программы не должна зависеть от того, решит ли и каким образом система реализовать оптимизацию, такую как выгрузка классов. В противном случае это может поставить под угрозу переносимость программ. Следовательно, независимо от того, был ли класс или интерфейс выгружен или нет, он должен быть прозрачным для программы.
Однако, если класс или интерфейс C был выгружен, а его определяющий загрузчик потенциально доступен, то C может быть перезагружен. Никогда нельзя было гарантировать, что этого не произойдет.
На самом деле, это пошло для решения ваших конкретных проблем:
Перезагрузка может быть не прозрачной, если, например, класс имеет:
- Статические переменные (состояние которых будет потеряно).
- Статические инициализаторы (которые могут иметь побочные эффекты).
- Собственные методы (которые могут сохранять статическое состояние).
Кроме того, значение хеш-функции объекта Class зависит от его идентичности. Поэтому, как правило, невозможно полностью перезагрузить класс или интерфейс.
Поскольку мы никогда не можем гарантировать, что выгрузка класса или интерфейса, чей загрузчик потенциально доступен, не вызовет перезагрузку, а перезагрузка никогда не бывает прозрачной, но выгрузка должна быть прозрачной, из этого следует, что нельзя выгружать класс или интерфейс, пока его загрузчик потенциально достижимо. Аналогичная аргументация может использоваться для вывода, что классы и интерфейсы, загруженные загрузчиком начальной загрузки, никогда не могут быть выгружены.
Необходимо также спорить, почему безопасно выгружать класс C, если его определяющий загрузчик классов может быть восстановлен. Если определяющий загрузчик может быть восстановлен, то никогда не может быть никаких живых ссылок на него (это включает ссылки, которые не являются живыми, но могут быть воскрешены финализаторами). Это, в свою очередь, может быть правдой только в том случае, если никогда не может быть никаких живых ссылок ни на один из классов, определенных этим загрузчиком, включая C, либо из их экземпляров, либо из кода.
Выгрузка классов - это оптимизация, которая важна только для приложений, которые загружают большое количество классов и через некоторое время перестают использовать большинство этих классов. Ярким примером такого приложения является веб-браузер, но есть и другие. Характерной чертой таких приложений является то, что они управляют классами посредством явного использования загрузчиков классов. В результате изложенная выше политика хорошо работает для них.
Строго говоря, не обязательно, чтобы проблема выгрузки класса обсуждалась в этой спецификации, поскольку выгрузка класса - это просто оптимизация. Тем не менее, проблема очень тонкая, и поэтому она упоминается здесь для пояснения.