Подтвердить что ты не робот

Использует ли экземпляр класса как ключ карты наилучшую практику?

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

Map<Class<?>,String> classToInstance = new HashMap();

classToInstance.put(String.class,"Test obj");
4b9b3361

Ответ 1

Да, вы должны быть осторожны! Например, если ваш код выполняется в веб-контейнере, и у вас есть привычка выполнять горячее развертывание веб-приложений, сохранение ссылки на один объект класса может привести к значительной утечке памяти permgen.

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

Следует также отметить, что если один из классов, которые вы используете в качестве ключа, перезагружается, то:

  1. Старая и новая версии класса не будут равны.
  2. Поиск нового класса первоначально даст "промах".
  3. После добавления нового класса на карту у вас появятся две разные записи на карте для разных версий класса.
  4. Это применимо, даже если между двумя версиями класса нет разницы в коде. Они будут отличаться просто потому, что были загружены разными загрузчиками классов.

С Java 8 - Permgen был удален. Как вы думаете, нормально ли использовать экземпляр класса в качестве ключа HashMap в любых ситуациях?

Знайте, что у вас все еще будет утечка памяти. Любой динамически загруженный класс, используемый в вашем HashMap (ключ или значение) и (как минимум) других динамически загружаемых классов, будет оставаться доступным. Это означает, что GC не сможет выгрузить/удалить их.

То, что раньше было утечкой permgen, теперь стало обычной утечкой памяти в куче и метапространстве. (В Metaspace хранятся дескрипторы классов и объекты кода для классов.)

Ответ 2

Нет, это не проблема. Пока вы все равно создавали экземпляр класса, вы не используете больше памяти, удерживая ссылку на сам класс.

Ответ 3

Как упоминал Стивен C, утечка памяти действительно вызвана загрузчиками классов. Но проблема острее, чем на первый взгляд. Рассмотрим это:

mapkey --> class --> classloader --> all other classes defined by this classloader.

Кроме того,

class --> any static members, including static Maps e.g. caches.

Несколько таких статических кэшей могут начинать сбрасывать до серьезного объема памяти, теряемого всякий раз, когда циклически загружается приложение webapp или какое-либо другое динамически загружаемое приложение (classloaded).

Существует несколько подходов к решению этой проблемы. Если вам не нужны разные "версии" одного и того же класса из разных загрузчиков классов, просто введите ключ на основе Class.getName(), который является java.lang.String.

Другой вариант - использовать java.util.WeakHashMap. Эта форма Карты поддерживает только слабые ссылки на ключи. Слабые ссылки не поддерживают GC, поэтому они не вызовут накопления памяти. Однако значения не слабо ссылаются. Поэтому, если значения представляют собой, например, экземпляры классов, используемых в качестве ключей, WeakHashMap не работает.

Ответ 4

Это зависит от того, где определена эталонная 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'ed только в том случае, если ссылки относятся к объекту/классу в загрузчике родительского класса.

parent class loader     class loader <--> class  // no GC is possible
                    \-------------------/

То, что предложение "любая ссылка извне приложения на объект, в приложении которого класс загружается загрузчиком класса приложения, вызовет утечку загрузчика класса" в статья, упомянутая в ответе Стивена К.

Но это не должно быть, если карта является частью вашего приложения.