Каковы преимущества использования WeakReferences?

#java #android #memory-leaks #bitmap #weak-references

#java #Android #утечки памяти #растровое изображение #слабые ссылки

Вопрос:

У меня есть некоторые утечки памяти в моем приложении. Все они исходят из определенного кластера представлений, на настройку которого я потратил кучу времени, пытаясь максимально сократить контекстную передачу. Это наводит меня на мысль, что проблема заключается в растровых изображениях, используемых в кластере. Итак, я подумал использовать WeakReferences для всех ссылок на растровые изображения, используемые представлениями. Я никогда не использовал WeakReference и не уверен, что это хорошее приложение. Может ли какой-либо орган предоставить полезные указания или подсказки?

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

1. Вот очень хороший пост в блоге, который поможет вам понять это.

Ответ №1:

Итак, я подумал использовать WeakReferences для всех ссылок на растровые изображения, используемые представлениями. Я никогда не использовал WeakReference и не уверен, что это хорошее приложение. Может ли какой-либо орган предоставить полезные указания или подсказки?

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

Ключевой вопрос о WeakReference заключается в понимании разницы с жесткими ссылками. Если в вашем приложении больше нет жесткой ссылки на bitmap, то GC разрешается атомарно удалять объект из памяти, и все существующие слабые ссылки мгновенно будут указывать на null. В вашем случае вы НЕ можете использовать слабые ссылки по всему вашему коду.

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

Таким образом, если ни одно представление не ссылается на bitmap, то GC соберет объект без побочных эффектов для представлений, поскольку ни у одного из них нет жесткой ссылки на него. При использовании объектов со слабыми ссылками рекомендуется явно присваивать жестким ссылкам значение null, когда объект вам больше не нужен.

Добавление

Вот краткая реализация решения (просто чтобы дать представление):

 public class BitmapContainer {

    public static class Bitmap {
        private final long id;
        public Bitmap(long id) { this.id = id; }
        public long getId() { return id; }
        public void draw() { };
    }

    WeakHashMap<Bitmap, WeakReference<Bitmap>> myBitmaps
        = new WeakHashMap<Bitmap, WeakReference<Bitmap>>();

    public void registerBitMap(Bitmap bm) {

        if ( bm == null ) throw new NullPointerException();

        WeakReference<Bitmap> wr = new WeakReference<Bitmap>(bm);
        myBitmaps.put(bm, wr);

    }

    /** Method returns null if bitmap not available */
    public Bitmap getBitMap(long id) {

        for ( Bitmap item : myBitmaps.keySet() ) {
            if ( item != null) {
                if ( item.getId() == id ) {
                    return item;
                }
            }
        }

        return null;

    }

}
  

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

1. Спасибо. На самом деле это фантастическая идея. Я никогда даже не рассматривал класс контейнера для хранения растровых изображений.

2. Отвечая на ваш первоначальный вопрос, ценность контейнера заключается в том, что у вас не будет повторяющихся растровых изображений в вашей памяти (поэтому нет риска утечек памяти). Вы можете использовать WeakHashMap. Ценность WeakReferences в том, что они позволяют автоматически устранять беспорядок. Нет необходимости внедрять хитрые элементы для выпуска bitmap. Просто позвольте GC выполнять свою работу.

3. Мне больше не нужно будет перерабатывать растровые изображения, не так ли?

4. Кроме того, у вас есть пример возможного контейнера. Я нашел несколько, но они не были полными; Я хотел бы получить четкое представление, чтобы я все хорошо понял.

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

Ответ №2:

Наиболее простое использование слабых ссылок, которое я могу придумать, — это кэш. Вы хотите добавить объекты в кэш, но если в остальной части виртуальной машины нет ссылок на объект, вы хотите, чтобы объект был обработан без необходимости возвращаться и удалять его из кэша самостоятельно. Этого добиваются слабые ссылки. Вы добавляете слабую ссылку на объект в своем кэше. Когда кэш — это единственное, что ссылается на ваш объект, он подходит для GC. Попытки использовать слабую ссылку после того, как объект GC’ed приводит к исключению.

Строго говоря, объект подходит для GC, когда на него не остается сильных ссылок (т. Е. Независимо от того, существуют ли на него какие-либо слабые ссылки).

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

Ответ №3:

Необходимость в WeakReferences возникает из сценария, в котором вам необходимо поддерживать метаданные об объекте, который вы не контролируете.

Надуманным примером может быть String , он окончательный, и мы не можем его расширить, но если мы хотели бы сохранить некоторые дополнительные данные о конкретном String экземпляре, мы, вероятно, использовали бы Map реализацию, которая содержала бы эти метаданные. Для этого примера я предположу, что мы хотим сохранить длину строки в качестве наших метаданных (да, я знаю, что у String объекта уже есть общедоступное свойство length). Итак, мы бы создали Map такой:

 Map<String, Integer> stringLengths = new HashMap<String, Integer>();
  

Предположим, что мы могли бы заполнить эту карту каким-либо методом и не знать, когда мы закончим с данными, поэтому мы не можем явно удалить записи. По мере заполнения этой карты, которая никогда не будет незаселена, наши ссылки будут сохраняться вечно. Если приложение выполняется в течение длительного времени, есть большая вероятность, что мы столкнемся с ошибкой OutOfMemoryError.

Решением этого было бы использование WeakHashMap реализации.

 Map<String, Integer> stringLengths = new WeakHashMap<String, Integer>();
  

Таким образом, когда все (сильные) ссылки на ключ исчезнут, следующий GC приведет к удалению записи WeakHashMap . (Да, я понимаю, что String это занимает особое место в сердце JVM, но я предполагаю, что строки являются GC’d такими же, как обычный объект в этом надуманном примере)

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

Ответ №4:

Я не думаю, что это правильное решение вашей проблемы. Как говорили другие, если вы используете WeakReferences, вы делаете свой код более дорогим и более хрупким. Хрупкость возникает из-за того, что каждый раз, когда вы используете слабую ссылку, вы потенциально можете получить исключение.

(Другая проблема заключается в том, что WeakReferences обходятся GC дороже, чем обычные ссылки. У меня нет никаких фактических показателей производительности, и это, скорее всего, не имеет значения в вашем случае использования, но это, по крайней мере, теоретическая проблема.)

ИМО, лучший подход к вашей проблеме — использовать хороший профилировщик памяти, чтобы отследить, где на самом деле происходят утечки памяти, и исправить это. Запустите приложение на некоторое время с помощью профилировщика памяти, определите некоторый объект, который допустил утечку, и используйте профилировщик для отслеживания пути или путей, по которым объект все еще доступен. Вы, вероятно, обнаружите, что это может быть связано с одной или двумя ошибками или одним и тем же шаблоном ошибок, повторяющимся в нескольких местах. (Я предполагаю, что это будут прослушиватели событий, которые не удаляются в нужное время.)