Я считал где-нибудь, что использование экземпляров класса как ниже не является хорошей идеей, поскольку они могли бы вызвать утечки памяти. Кто-то может сказать мне если, если это - допустимый оператор? Или они - какие-либо проблемы с помощью него этот путь?
Map<Class<?>,String> classToInstance = new HashMap();
classToInstance.put(String.class,"Test obj");
Да, надо быть осторожным! Например, если ваш код выполняется в веб-контейнере, и у вас есть привычка выполнять горячее развертывание веб-приложений, сохраненная ссылка на один объект класса может вызвать значительную утечку памяти permgen.
Эта статья подробно объясняет проблему. Но в двух словах проблема в том, что каждый класс содержит ссылку на свой загрузчик классов, а каждый загрузчик классов содержит ссылки на каждый загруженный им класс. Итак, если один класс доступен, все они доступны.
Также следует отметить, что если один из классов, которые вы используете в качестве ключа, перезагружается, то:
Из Java 8 - Permgen был удален. Как вы думаете, можно ли использовать экземпляр класса в качестве ключа HashMap в любых ситуациях?
Имейте в виду, что у вас все равно будет утечка памяти. Любой динамически загружаемый класс, используемый в вашем HashMap (ключ или значение) и (по крайней мере) другие динамически загружаемые классы, будет оставаться доступным. Это означает, что сборщик мусора не сможет выгрузить / удалить их.
То, что раньше было утечкой permgen, теперь стало обычной утечкой хранилища кучи и метапространства. (В метапространстве хранятся дескрипторы классов и объекты кода для классов.)
Нет, это не проблема. Пока вы все равно создавали экземпляр класса, вы больше не используете память, удерживая ссылку на сам класс.
Это зависит от того, где определена ссылка classToInstance=new HashMap();
.
Класс указывает обратно на свой загрузчик классов, и, как следствие, загрузчик классов не может быть собран в мусор, пока существует ссылка на класс. Но если ссылки образуют круг (или недостижимый кластер), это все равно работает - GC знает, как работать с круговыми ссылками.
parent class loader --> class loader <--> class // no GC is possible
parent class loader class loader <--> class // circular references are GC'ed
Таким образом, ссылка на класс может предотвратить GC загрузчика класса, только если ссылки исходят от объекта/класса в родительском загрузчике класса.
parent class loader class loader <--> class // no GC is possible
\-------------------/
Именно это означает предложение "любая ссылка извне приложения на объект в приложении, класс которого загружен загрузчиком класса приложения, вызовет утечку загрузчика класса" в статье, упомянутой в ответе Stephen C.
Но это не должно быть так, если карта является частью вашего приложения.
Как сказал Стивен С., утечка памяти действительно происходит из-за загрузчиков классов. Но проблема стоит острее, чем на первый взгляд. Подумайте об этом:
mapkey --> class --> classloader --> all other classes defined by this classloader.
Кроме того,
class --> any static members, including static Maps e.g. caches.
Несколько таких статических кешей могут начать накапливать серьезные объемы памяти, теряемой всякий раз, когда веб-приложение или какое-либо другое динамически (загруженное по классам) загружаемое приложение циклически повторяется.
Есть несколько подходов к решению этой проблемы. Если вас не интересуют разные «версии» одного и того же класса из разных загрузчиков классов, просто введите ключ на основе Class.getName ()
, который является java.lang.String
.
Другой вариант - использовать java.util.WeakHashMap
. Эта форма Map поддерживает только слабые ссылки на ключи. Слабые ссылки не задерживают сборщик мусора, поэтому они не вызывают накопления памяти. Однако значения , а не слабо указаны. Поэтому, если значения являются, например, экземплярами классов, используемых в качестве ключей, WeakHashMap
не работает.