Как вы, похоже, знаете, и приведенный выше ответ также проясняет, что сопутствующие объекты транслируются в классы, и класс, который их объявляет, содержит статическую ссылку c на объект сопутствующего класса, что-то вроде следующего:
public static final MyClass.Companion Companion = new MyClass.Companion(null);
Теперь вопрос
Сохраняются ли сопутствующие объекты в памяти на протяжении жизненного цикла приложения
, поскольку декларирующий класс содержит static
ссылку на сопутствующий класс, вопрос сводится к времени жизни static
полей в jvm class
, и ответ лежит в JVM spe c, но spe c является битом dry в объяснении, так Я добавляю некоторые фрагменты из книги Внутри Java Виртуальная машина .
Как в вашем примере, скажем, у нас есть class
только с одним объектом-компаньоном.
Первый вопрос: когда будет создан объект класса-компаньона? или когда static
поля инициализируются?
соответствующий текст из книги . (для контекста книга говорит о процедуре загрузки класса)
Инициализация
Последний шаг, необходимый для подготовки класса или интерфейса для его первого активного использования, - это инициализация, процесс установки переменные класса к их надлежащим начальным значениям. Как здесь используется, «правильное» начальное значение - это желаемое начальное значение программиста для переменной класса. Правильное начальное значение контрастирует с начальным значением по умолчанию, заданным для переменных класса во время подготовки. Как описано выше, виртуальная машина назначает значения по умолчанию только на основе типа каждой переменной. Правильные начальные значения, напротив, основаны на каком-то генеральном плане, известном только программисту. В коде Java правильное начальное значение указывается через инициализатор переменной класса или инициализатор stati c.
Итак, мы знаем, что после загрузки и инициализации MyClass
объект-компаньон класс будет создан.
но что заставит JVM загрузить MyClass
?
Спецификация Java Virtual Machine обеспечивает гибкость реализации во времени загрузки класса и интерфейса и связывания, но строго определяет время инициализации. Все реализации должны инициализировать каждый класс и интерфейс при первом активном использовании. Активное использование класса:
Вызов конструктора для нового экземпляра класса
Создание массива, который имеет класс как свой тип элемента
Вызов метода, объявленного классом (не унаследованного от суперкласса)
4 использование или присвоение поля, объявленного классом (не унаследованного от суперкласса или суперинтерфейса), за исключением полей, которые являются как stati c, так и final, и инициализируются константным выражением времени компиляции
Таким образом, согласно 4-му пункту, когда вы делаете MyClass.foo()
из kotlin или MyClass.Companion.foo()
, в этот момент MyClass
будет загружено и готово. (Вероятно, намного раньше)
Обратите внимание, что на данный момент нет объекта MyClass
, то есть мы не использовали выражение MyClass()
.
Означает ли это static
поля будут оставаться в памяти до тех пор, пока приложение работает?
Их можно собирать мусором, если объявленный тип выгружается, в нашем случае, если JVM или ART (на android) выгружает MyClass
, тогда есть вероятность, что объект-компаньон будет собирать мусор.
JVM Spe c может сказать следующее о разгрузке классов
Реализация языка программирования Java может выгружать классы.
Класс или интерфейс могут быть выгружены тогда и только тогда, когда сборщик мусора может восстановить их определяющий загрузчик классов, как обсуждалось в §12.6.
Классы и интерфейсы, загруженные загрузчиком bootstrap, не могут быть выгружены.
В практическом классе выгрузка почти (я сказал почти) никогда не происходит, так что да, объекты-компаньоны останутся в памяти для жизненного цикла приложения .