Java / слабые ссылки: слабая ссылка.get() не равна нулю, пока в ней нет более сильной ссылки

#java #android #weak-references

#java #Android #слабые ссылки

Вопрос:

Вот мой очень простой код :

 class UpdateAlertActivity extends Activity {
    O o ;
    WeakReference<O> _o ;
    ArrayList<O> arr = new ArrayList<>();

    static class O {
        private String l ;

        public O(String l) {
            this.l = l ;
        }
    }

    void test()
    {
        arr.clear();
        Runtime.getRuntime().gc();
        Log.i(LOG_TAG, "breakpoint");
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        o = new O("hello");
        arr.add(o);

        _o = new WeakReference<>(o);


        test(); 
    }
}
  

Когда я очищаю массив в test() методе, я исключал _o.get() , чтобы быть null в моей точке останова, но это не так. Когда я вижу в debbuger, где o хранится, он показывает мне только слабую ссылку _o , в то время как я думал, что слабые ссылки создаются для освобождения их экземпляра, когда на них нет другой сильной ссылки…

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

РЕДАКТИРОВАТЬ : даже этот супер простой код работает не так, как ожидалось, это не имеет смысла…

 O o = new O("lol");

WeakReference<O> _o = new WeakReference<>(o);

Log.i(LOG_TAG, "1:"   _o.get().toString());

o = null ;

Log.i(LOG_TAG, "2:"   _o.get().toString());

System.gc();
Log.i(LOG_TAG, "3:"   _o.get().toString()); // output not null here 
  

ПРАВКА2 :
Я хотел использовать GC, потому что в другом статическом классе у меня есть массив lastUpdates, который иногда заполняется обновлениями приложений, отправляемыми моим сервером wia TCP.

Все мои действия отслеживают этот массив LastUpdate (путем реализации интерфейса Observer / Observable), и когда он изменяется, все получают уведомления и вызывают определенную функцию onUpdate(ArrayList u) со списком новых поступивших обновлений в качестве параметра. Эта функция обрабатывает обновления, только если действие возобновлено (изменение пользовательского интерфейса во время сна активности приводит к сбою приложения).

Если приложение entiere находится в «спящем режиме» (например, пользователь находится в меню устройства), я хочу, чтобы только первое действие просыпалось, чтобы иметь возможность обрабатывать новые обновления.

Для этого я создал в своем классе activity массив pendingPersistentUpdates , в котором хранятся слабые ссылки на все обновления, которые были перехвачены, когда activity спал. Когда первое действие просыпается, обновления, полученные во время ожидания приложения, все еще находятся в массиве lastUpdates, поэтому я ожидаю, что слабые ссылки, хранящиеся в pendingPersistentUpdates activity prop, вернут фактические обновления, поэтому моя activity может обрабатывать их в пользовательском onResume интерфейсе.

Я ожидал такой ситуации :

  • Выполняется действие A, действие B приостанавливается (например, после действия A)
  • Приложение получает обновление U. Все действия (A и B) уведомляются. Обработайте обновление, потому что оно запущено, как и ожидалось. B сохраните это обновление в its pendingPersistentUpdates , потому что оно приостановлено.
  • Пауза, пользователь возвращается к B. При паузе массив lastUpdates очищается, поэтому слабые ссылки обновлений в B pendingPersistentUpdates возвращают значение null
  • B возобновляется, но pendingPersistentUpdates слабые ссылки равны нулю (lastUpdates был очищен), поэтому U не обрабатывается.

Хотя, поскольку LastUpdate.clear() не запускает GC, pendingPersistentUpdates обновления B все еще существуют и обрабатываются во второй раз (поведение нежелательно)

Комментарии:

1. Runtime.getRuntime().gc() может не всегда запускать сборщик мусора

2. Я также пробовал с System.gc(), но это тоже не имеет никакого эффекта..

3. @JeremLachkar оба ответа, уже опубликованные при последнем редактировании вашего вопроса, также объясняют, что вы наблюдаете с помощью недавно добавленной вставки.

Ответ №1:

Принудительно запустить сборщик мусора невозможно. Runtime.getRuntime().gc() это просто псевдоним для System.gc() , и оба они просто подсказка (прочитайте документы; они упоминают об этом). В частности, вызов gc() обычно означает, что сбор будет происходить в каком-то другом потоке; так что это скорее «начальный знак» для сборщика; gc() не обязательно приостанавливается и ждет, пока gc завершит полный цикл, он просто сообщает gc запустить его. Хотя, опять же, нет гарантии, Thread.sleep(10000L); это повышает вероятность того, что вы увидите последствия вызова GC.

A WeakReference просто гарантирует, что существование этого объекта не будет препятствовать сборке мусора объекта, на который он ссылается. Вот и все. Он не теряет свой референт мгновенно, когда единственный способ добраться до референта — через WeakReference объекты. Это произойдет позже (фактически, это произойдет до того, как объект будет GCed: сначала сборщик уничтожит все слабые ссылки, а когда-нибудь позже память, занимаемая объектом, будет действительно свободной для использования). Итак, то, что вы наблюдаете в своем отладчике (что единственный способ добраться до объекта — через этот WR), по сути, не нарушено.

Обратите также внимание, что проблема может быть в самом отладчике. Простое нахождение там и наблюдение за ним может иметь эффект (поэтому убедитесь, что вы запускаете его без него).

Если вы хотите, чтобы GC старался усерднее, вы можете выделить несколько довольно больших массивов и приостановить поток, это может помочь, но вы ничего не можете сделать, чтобы гарантировать выполнение GC. Если вы хотите наблюдать, запустите java с java -verbose:gc restOfArgsHere помощью и следите за консолью; если GC действительно происходит, вы это увидите.

Однако.

Ваш вставленный код даже не компилируется, что говорит о том, что вы либо вставили только половину, либо немного отредактировали его «для ясности» (часто плохая идея, лучше вставить именно то, с чем вы работаете!).). В частности, вы пишете o = new O("hello"); , но o нигде не определено. Если это поле вашего класса, это будет означать, что референт вообще не может быть собран!Поэтому вы можете захотеть проверить это. Сделайте это O o = new O("hello"); вместо того, что у вас есть. Предполагая, что вы правильно прочитали свой отладчик и он работает должным образом, это не так, но это подозрительно.

Комментарии:

1. Ах, android. Это делает -verbose:gc намного сложнее.

2. Спасибо за ваш ответ! Извините, вы правы, я забыл строку в коде (я добавил ее), o является свойством класса. Что вы подразумеваете под «Если это поле вашего класса, это будет означать, что референт вообще не может быть собран»? Я отредактирую вопрос, чтобы прояснить контекст.

3. С кодом в том виде, в каком вы его написали, этот объект не может быть собран , потому что основной поток ссылается на UpdateAlertActivity , и он ссылается на этот объект через поле o — вам придется явно обнулить его, в дополнение к правилам о том, как System.gc() не просто собирать встроенный мусор.

Ответ №2:

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

https://developer.android.com/reference/java/lang/System#gc ()

Вызов метода gc предполагает, что виртуальная машина Java затрачивает усилия на переработку неиспользуемых объектов, чтобы сделать память, которую они в настоящее время занимают, доступной для быстрого повторного использования. Когда управление возвращается из вызова метода, виртуальная машина Java приложила все усилия, чтобы освободить место для всех отброшенных объектов.

Обратите внимание на следующее предложение «метод gc предполагает, что виртуальная машина Java затрачивает усилия на переработку неиспользуемых объектов»

Внутренне сборщик мусора использует эвристику / пороговые значения, чтобы решить, когда собирать неиспользуемые объекты, поэтому, в основном, когда JVM нужна память.

Комментарии:

1. Спасибо за ваш ответ. Я не знал, что он не выполнял GC немедленно. Для правильной работы моей программе требуется запуск сборщика мусора. Я уточню контекст