Как значение в записи в WeakHashMap получает сборщик мусора, когда фактический объект собирается сборщиком мусора? - PullRequest
0 голосов
/ 04 июня 2018

Прежде всего, я хотел бы уточнить мое понимание WeakReference, поскольку следующий вопрос зависит от того же.

static void test() {
    Person p = new Person();
    WeakReference<Person> person = new WeakReference<>(p);
    p = null;
    System.gc();
    System.out.println(person.get());
    System.out.println(person);
}

static class Person {
    String name;
}

static class PersonMetadata {
    String someData;

    public PersonMetadata(String met) {
        someData = met;
    }
}

Вывод приведенного выше кода:

null java.lang.ref.WeakReference@7852e922

Это означает, что, хотя существует фактический объект person, мусор собирается после запуска GC, тамв памяти есть объект класса WeakReference<Person>, который на данный момент ни на что не указывает.

Теперь, учитывая приведенное выше понимание истины, я не понимаю, как работает WeakHashMap<K,V>.В приведенном ниже коде

public static void main(String[] args) {
    Person p = new Person();
    p.name = "John";
    WeakHashMap<Person, PersonMetadata> map = new WeakHashMap<>();
    PersonMetadata meta = new PersonMetadata("Geek");
    map.put(p, meta);
    p = null;
    System.gc();
    if (map.values().contains(meta)) {
        System.out.println("Value present");
    } else {
        System.out.println("Value gone");
    }
}

static class Person {
    String name;
}

static class PersonMetadata {
    String someData;

    public PersonMetadata(String met) {
        someData = met;
    }
}

ВЫХОД: Value gone

Теперь вопрос такой, как сказано, что ключ в WeakHashMap<K,V> является слабой ссылкой, что означает, что в приведенном выше коде, когда p становится null, фактический объект может быть подвергнут сборке мусора, поскольку нет более сильной ссылки на объект, но какделает значение and, являющееся объектом класса PersonMetadata, собирающим мусор , поскольку первый код доказывает, что объект класса WeakReference не является сборщиком мусора, даже если фактический объект собран.

1 Ответ

0 голосов
/ 05 июня 2018

Вы неправильно понимаете ситуацию.Когда 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 обычно выполняется неоптимизированным.Формально локальные переменные не требуются для предотвращения сбора мусора, если референт не используется иным образом, что актуально на практике, когда методы оптимизированы.

...