Как решить проблему строгих ссылок на значения в WeakHashMap?
У меня есть два объекта классов User и UserMetadata.
Объект типа User это объект класса сторонней библиотеки. Я не могу менять этот класс.
Объект типа UserMetadata это объект моего класса. Этот объект хранит дополнительные данные, связанные с объектом User. Если бы я мог редактировать класс User, я бы эти данные вынес туда. Но поскольку я не могу этого сделать, я вынес их свой класс.
В объекте UserMetadata имеется ссылка на User, а так же дополнительные данные:
class UserMetadata {
User user;
int age;
int money;
// и остальные доп. данные
}
Для того, чтобы я мог получить UserMetadata по User, мне пришлось связать их в HashMap<User, UserMetadata>.
Но ведь такой HashMap надо чистить. Но к сожалению я не могу знать, когда это делать из-за особенностей моего приложения. Но я знаю, что как только User станет не нужен, тогда и UserMetadata станет не нужен.
По этому я заменил HashMap на WeakHashMap. Но потом обнаружил утечку памяти, и ее причина была - сам WeakHashMap. Оказывается, что WeakHashMap не работает, если значение ссылается на ключ. А у меня UserMetadata имеет ссылку на User. О такой ситуации говорится в документации к WeakHashMap:
Implementation note: The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded. Note that a value object may refer indirectly to its key via the WeakHashMap itself; that is, a value object may strongly refer to some other key object whose associated value object, in turn, strongly refers to the key of the first value object. If the values in the map do not rely on the map holding strong references to them, one way to deal with this is to wrap values themselves within WeakReferences before inserting, as in: m.put(key, new WeakReference(value)), and then unwrapping upon each get.
В этой же документации предлагается решение - обернуть значение в WeakReference. Но это ведь не решение, меняется логика. Это допускает ситуацию, что UserMetadata будет очищен до очистки User. Мне это не подходит. Я хочу, чтобы UserMetadata жил до тех пор, пока живет User. И очищался UserMetadata вместе с User или после него. Будто бы UserMetadata это часть User, его дополнительный field.
Как же решить эту проблему?
Ответы (2 шт):
Я советую убрать ссылку на User из класса UserMetadata.
Везде где сейчас UserMetadata передается в качестве параметра, передавать User. И получать ссылку на UserMetadata по необходимости.
Таким образом у вас ни когда не возникнет потребности получить User из UserMetadata.
Все трудности от того, что нельзя просто взять и сделать так, чтоб объект стал как бы полем другого объекта.
Сделайте свой класс, чтоб полностью контролировать все аспекты его жизненного цикла и его содержимого и используйте его:
class MyUserWithMetadata {
private User user;
private UserMetadata metadata;
}
Для удобства миграции можно делегировать вызовы методов к User и т.п., но нужно смотреть по месту, где-то вероятно будет удобней получить User, чтоб использовать в существующем коде.