Как получить сам объект java, когда его собираются собрать

Как мы можем выполнить фрагмент кода, используя объект (необходимо его состояние), прежде чем он будет собран, если у нас нет контроля над его источником (мы не можем принудительно реализовать какой-либо интерфейс или, наконец, заблокировать)?

Типы ссылок Java позволяют нам получить доступ к объекту, если кто-то другой делает его полностью доступным + если мы используем очереди ссылок, мы также можем получать уведомления, когда объект собирается, если я не понимаю, что все, что вы можете делать со ссылочными типами, независимо от того, что вы используете в любой момент объект либо полностью достижим, либо его нет, и у вас есть null.

Все, что мне действительно нужно, это способ получить уведомление о том, что конкретный объект собирается быть собранным.

Может, не дуп, а это может иметь полезную информацию? Я думаю, вам нужно изменить саму виртуальную машину, чтобы делать то, что вы хотите.

Ken Y-N 15.03.2018 03:19

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

vach 15.03.2018 03:21

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

vach 15.03.2018 03:29

Это не работает. Когда ваш прокси становится финализируемым, это не является доказательством того, что обернутый объект стал недоступен.

Holger 15.03.2018 13:30
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Существует причина, по которой Reference API не позволяет получить собранный объект: разрешение снова сделать собранный объект доступным, как это происходит с методом finalize(), - это именно то, что не предназначено.

Стандартный подход заключается в создании подклассов ссылочных типов для хранения информации, связанной с референтом, например все необходимое для выполнения действия по очистке в рамках специализированного ссылочного объекта. Конечно, эта информация не должна содержать сильных ссылок на сам референт.

private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
static class IntegerPhantomReference extends PhantomReference<Integer> {
    final int value;
    public IntegerPhantomReference(Integer ref) {
        super(ref, QUEUE);
        value = ref.intValue();
    }
    public String toString() {
        return "Integer[value = "+value+"]";
    }
}
private static final Set<IntegerPhantomReference> REGISTERED = new HashSet<>();
public static void main(String[] args) throws InterruptedException {
    List<Integer> stronglyReferenced = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        Integer object = new Integer(i);
        stronglyReferenced.add(object);
        REGISTERED.add(new IntegerPhantomReference(object));
    }
    gcAndPoll("initial");
    stronglyReferenced.removeIf(i -> i%2 == 0);
    gcAndPoll("after removing even");
    stronglyReferenced.clear();
    gcAndPoll("after remove all");
    if (REGISTERED.isEmpty()) System.out.println("all objects collected");
}
private static void gcAndPoll(String msg) throws InterruptedException {
    System.out.println(msg);
    System.gc(); Thread.sleep(100);
    for(;;) {
        Reference<?> r = QUEUE.poll();
        if (r == null) break;
        System.out.println("collected "+r);
        REGISTERED.remove(r);
    }
}
initial
after removing even
collected Integer[value=4]
collected Integer[value=8]
collected Integer[value=6]
collected Integer[value=2]
collected Integer[value=0]
after remove all
collected Integer[value=1]
collected Integer[value=5]
collected Integer[value=3]
collected Integer[value=7]
collected Integer[value=9]
all objects collected

Для полноты картины есть хак, позволяющий воскресить собранный объект, который перестанет работать в Java 9.

В документации PhantomReference говорится:

Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued.

Непонятно, почему это было указано и get() метод PhantomReference был переопределен, чтобы всегда возвращать null, а именно, чтобы запретить извлечение выгоды из того факта, что эта ссылка не была очищена. Поскольку цель этого особого поведения неясна, оно было удалено из спецификации в Java 9, и эти ссылки автоматически удаляются, как и любые другие.

Но для предыдущих версий можно использовать Reflection с переопределением доступа для доступа к референту, чтобы делать именно то, для чего API не предназначался. Излишне говорить, что это только для информационных целей и настоятельно не рекомендуется (и, как уже говорилось, это перестает работать в Java 9).

private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
private static final Set<PhantomReference<Integer>> REGISTERED = new HashSet<>();
public static void main(String[] args)
        throws InterruptedException, IllegalAccessException {
    List<Integer> stronglyReferenced = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        Integer object = new Integer(i);
        stronglyReferenced.add(object);
        REGISTERED.add(new PhantomReference<>(object, QUEUE));
    }
    gcAndPoll("initial");
    stronglyReferenced.removeIf(i -> i%2 == 0);
    gcAndPoll("after removing even");
    stronglyReferenced.clear();
    gcAndPoll("after remove all");
    if (REGISTERED.isEmpty()) System.out.println("all objects collected");
}
static final Field REFERENT;
static {
    try {
        REFERENT = Reference.class.getDeclaredField("referent");
        REFERENT.setAccessible(true);
    } catch (NoSuchFieldException ex) {
        throw new ExceptionInInitializerError(ex);
    }
}
private static void gcAndPoll(String msg)
        throws InterruptedException, IllegalAccessException {
    System.out.println(msg);
    System.gc();
    Thread.sleep(100);
    for(;;) {
        Reference<?> r = QUEUE.poll();
        if (r == null) break;
        Object o = REFERENT.get(r);
        System.out.println("collected (and now resurrected)"+o);
        REGISTERED.remove(r);
    }
}

просто еще один пример дерьмового api, созданного разработчиками jdk, вместо того, чтобы дать вам четкий способ сказать: «Привет, jvm, когда этот объект будет очищен, позвольте мне поработать», они дают вам дерьмовый финал ... раздражает

vach 15.03.2018 19:11

В Java 9 есть API, чтобы сказать именно это, «Выполнить этот Runnable, когда этот объект мертв». Когда вы заменяете значение int в моем первом примере кода ссылкой на Runnable и добавляете поток, опрашивающий очередь и выполняющий их, вы получаете то же самое (и именно так это было реализовано).

Holger 16.03.2018 07:58

@Holger, какая красота кода! Я признаю, что потребовалась бы целая вечность, чтобы понять это в точных деталях, но у меня есть вопрос о Cleaner, если вы не возражаете. Я предполагаю, что когда в документации написано когда объект становится фантомно достижимым, это означает, что теперь он доступен для сборки мусора? Даже если вы теоретически держите сильную ссылку на эту фантомную ссылку? Я понимаю, что get всегда будет возвращать null ...

Eugene 16.03.2018 13:06

@Eugene, являющийся фантомная достижимость, подразумевает, что у кого-то есть сильная ссылка на PhantomReference, указывающий на объект (уборщик тоже будет). В противном случае объект - недоступен.

Holger 16.03.2018 14:39

@Eugene, поэтому код этого ответа сохраняет все экземпляры PhantomReference в наборе REGISTERED; они должны оставаться доступными, иначе они будут собраны, как обычные объекты, и ничего не произойдет. Они ставятся в очередь только тогда, когда достижимы (это относится ко всем типам ссылочных объектов).

Holger 16.03.2018 14:47

@Holger не может согласиться, используя этот подход, вам нужно захватить состояние и создать runnable и держать его в ссылке, что, если вы возражаете, продолжая изменять это состояние? возможно, вам нужно прочитать это в последний момент. Если бы эти люди знали, как создавать хорошие API, нам бы даже не пришлось знать, что такое PhantomReference ...

vach 17.03.2018 08:36

@vach 99,9% разработчиков никогда в жизни не нуждались в PhantomReference. Ваша идея получить доступ к объекту, который должен быть недоступен (что обычно подразумевает наличие недоступный), именно о том, о чем идет речь в «дрянной финализации». Противоречие само по себе. Вы можете обвинять «этих людей», говоря что-то вроде «Если бы эти люди знали, как создавать хорошие API…», но, насколько я могу судить, разработчики C# /. NET тоже потерпели неудачу в этом вопросе, и я не знаю любой лучший пример. Я также сомневаюсь, что именно вы могли бы предложить лучшую альтернативу.

Holger 19.03.2018 09:40

Другие вопросы по теме