Никому не нравятся креши с OutOfMemoryError
Работая над Square Register, мы рисуем подпись клиента используя битмап-кеш. Поскольку этот битмап размером с экран устройства, у нас было очень много OutOfMemory крешей во время создания его.
Мы пробовали несколько подходов ни один из которых не решил проблему:
- Использовали Bitmap.Config.ALPHA_8
- Ловили OutOfMemoryError, вызывали сборку мусора и пробовали снова (подглядели в GCUtils),
- Мы не рассматривали вариант с размещением битмапов вне кучи Java. К счастью Fresco еще не существовало,
Мы шли по ложному пути
Проблема была не в размере битмапа. Когда память почти полностью использована OOM может возникнуть где угодно. Конечно, вероятность что это произойдет во время выделения больших объектов, вроде битмапов, увеличивается. OOM — это лишь симптом более глубокой проблемы: утечек памяти.
Что такое утечка памяти?
Многие объекты имеют ограниченное время жизни. Когда их время жизни заканчивается, ожидаются что они будут собранны сборщиком мусора. Если цепочка ссылок указывает на объект в памяти, после ожидаемого завершения времени жизни объект, то это создает утечку памяти. При накоплении некоторого количества утечек, у программы начинает заканчивается память.
Например, после того как был вызван Activity.onDestroy, активити, вся ииерархия вьюшек, и связанных с ними битмапов должны быть доступны для сборки мусора. Если фоновый поток во время работы удерживает ссылку на активити, то память занимаемая ей память не может быть освобождена. Рано или поздно это приведет к выбросу OutOfMemoryError.
Охота на утечки
Охота на утечки памяти ручной процесс, хорошо описанный в серии статей Wrangling Dalvik от Raizlabs.
Вот ключевые этапы:
- Узанть о крешах OutOfMemoryError через Bugsnag, Crashlytics или консоль разработчика
- Попытаться воспроизвести проблему. Возможно вам понадобится купить, одолжить или украсть конкретное устройство подверженное крешам (Не на всех девайсах проявляются все утечки!). Также необходимо восстановить последовательность действий приводящих к утечке, возможно грубым перебором.
- Сделать дамп памяти при OutOfMemoryError (Здесь можно узнать как).
- Изучить дамп памяти с помощью MAT или YourKit и обнаружить объекты которые должны были быть собраны сборщиком мусора.
- Найти самые короткие пути ссыльных ссылок от объекта до корней сборщика мусора (GC Roots).
- Найти ссылку которой не должно быть и исправить утечку памяти.
Что если существовала бы библиотека которая могла сделать это все, еще до возникновения OOM и позволила бы сосредоточится на исправлении утечки памяти?
Представляем LeakCanary
LeakCanary — это Open Source Java библиотека для обнаружения утечек памяти в отладочных сборках.
Давайте взглянем на следующий пример:
class Cat {
}
class Box {
Cat hiddenCat;
}
class Docker {
static Box container;
}
// ...
Box box = new Box();
Cat schrodingerCat = new Cat();
box.hiddenCat = schrodingerCat;
Docker.container = box;
Затем вы создаете объект RefWatcher и предоставляете ему объект для отслеживания:
// Мы ожидаем что schrodingerCat должен исчезнуть (или нет). Давайте отслеживать его.
refWatcher.watch(schrodingerCat);
После того как утечка была обнаружена, вы получаете неплохой трейс утечки.
* GC ROOT static Docker.container
* references Box.hiddenCat
* leaks Cat instance
Поскольку мы знаем, что вы то и дело занимаетесь новыми фичами, мы сделали установку очень простой. С помощью всего одной строчки кода, LeakCanary автоматически будет обнаруживать утечки активити:
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}
Вы получаете уведомление в неплохом виде:
Заключение
После начала использования LeakCanary, мы обнаружили и исправили много утечек в нашем приложении. Мы даже нашли несколько утечек в Android SDK.
Результаты потрясающие: на 94% меньше OutOfMemoryError крешей!
Если вы хотите избавиться от OOM крешей, установи LeakCanary сейчас!
Комментарии (7)
7voprosov
10.05.2015 23:09+3Square в очередной раз двигают android разработку вперед к светлому будущему!
kemsky
11.05.2015 14:18Я так понимаю объекты трекаются через weakreference, а затем проводится поиск в heap dump. Делал тоже самое для флеша, но там нельзя было получить дамп, а нужно было запускать профайлер и воспроизводить.
HeavyRazzer
15.05.2015 18:16Очень полезный модуль. У нас оно в дебажной версии нашего приложения еще кидает свои логи в HipChat, так что все могут в реальном времени смотреть, как плохеет новой сборке во время активного тестирования.
VioletGiraffe
Интересная работа! Сомневаюсь, что смогу интегрировать в своё приложение, но попробую.
danikula
Интегрируется буквально несколькими строками. Я начал использовать и уже получил полезные данные.