Вы неправильно понимаете ситуацию.Когда map.values().contains(meta)
или short map.containsValue(meta)
возвращает false
, это не означает, что meta
было собрано мусором.Фактически, вы держите ссылку на объект в meta
и даже передаете эту ссылку в метод contains
, который может вызвать equals
для него.Итак, как этот объект мог быть собран мусором?
Ответ только говорит вам, что нет никакой связи между одним из ключей карты с этим объектом, и, поскольку единственный ключ был собран мусором, это правильный ответ.В качестве альтернативы вы могли бы просто попросить map.isEmpty()
проверить наличие ассоциации.
Это то, что WeakHashMap
предоставляет:
Хэшоснованная на таблицах реализация интерфейса Map
со слабыми ключами .Запись в WeakHashMap
будет автоматически удалена, если ее ключ больше не используется.Точнее говоря, наличие сопоставления для данного ключа не помешает тому, чтобы ключ был отброшен сборщиком мусора, то есть сделан финализируемым, финализированным и затем восстановленным.Когда ключ отброшен, его запись эффективно удаляется с карты, поэтому этот класс ведет себя несколько иначе, чем другие реализации Map
.
Удаление записи не происходит мгновенно.Он основан на добавлении WeakReference
в ReferenceQueue
, который затем запрашивается внутри, когда вы делаете следующий запрос, например containsValue
или даже size()
.Например, если я изменю вашу программу на:
Person p = new Person();
WeakHashMap<Person, PersonMetadata> map = new WeakHashMap<>();
PersonMetadata meta = new PersonMetadata("Geek");
map.put(p, meta);
WeakReference<?> ref = new WeakReference<>(p);
p = null;
while(ref.get() != null) System.gc();
System.out.println(map.containsValue(meta)? "Value present": "Value gone");
Иногда она выводит «Value present», несмотря на то, что ключ Person
экземпляр, как доказано, был собран в этот момент мусором.Как уже было сказано выше, речь идет о внутренней очистке карты, а не об экземпляре PersonMetadata
, на который мы все равно имеем строгую ссылку в meta
.
Предоставление PersonMetadata
права на сборку мусора являетсясовершенно другая вещь.Как уже было сказано, WeakReference
выполняет внутреннюю очистку всякий раз, когда мы вызываем метод it.Если мы этого не сделаем, очистки не произойдет, и, следовательно, мы все равно будем ссылаться, даже если ключ был собран мусором.Рассмотрим:
Person p = new Person();
WeakHashMap<Person, PersonMetadata> map = new WeakHashMap<>();
PersonMetadata meta = new PersonMetadata("Geek");
map.put(p, meta);
WeakReference<?> personRef = new WeakReference<>(p);
WeakReference<?> metaRef = new WeakReference<>(meta);
p = null;
meta = null;
while(personRef.get() != null) System.gc();
System.out.println("Person collected");
for(int i = 0; metaRef.get() != null && i < 10; i++) {
System.out.println("PersonMetadata not collected");
System.gc();
Thread.sleep(1000);
}
System.out.println("calling a query method on map");
System.out.println("map.size() == "+map.size());
System.gc();
System.out.println("PersonMetadata "+(metaRef.get()==null? "collected": "not collected"));
, который будет печатать
Person collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
PersonMetadata not collected
calling a query method on map
map.size() == 0
PersonMetadata collected
, демонстрируя, как WeakHashMap
содержит строгую ссылку на значение уже собранного ключа, пока мы в конечном итоге не вызовем для него метод,чтобы дать ему возможность выполнить внутреннюю очистку.
Значение в конечном итоге собирается, когда ни WeakHashMap
, ни наш метод не содержат ссылку на него.Когда мы удаляем оператор meta = null;
, карта все равно будет пустой в конце (после внутренней очистки), но значение не будет собрано.
Важно помнить, что эти примеры кодапредназначены для демонстрации и касаются специфического поведения реализации, в частности, метод main
обычно выполняется неоптимизированным.Формально локальные переменные не требуются для предотвращения сбора мусора, если референт не используется иным образом, что актуально на практике, когда методы оптимизированы.