Comments 36
Шикарная статья. Спасибо!
Действительно шикарная, и комикс порадовал.
Статья про утечки памяти и ни слова про valgrind. Хм…
Для людей делающих все из консоли или для любителей автоматизировать (требуется root доступ):
Можно получить *.hprof дамп сделав «adb shell kill -10 » для вашего dalvikvm-процесса. Стянув дамп из /data/misc к себе на компьютер с помощью adb pull его можно поизучать даже с помощью стандарной jvisualvm (standalone-приложение, кажется идет вместе с JDK, не требудет установки эклипса).
Можно получить *.hprof дамп сделав «adb shell kill -10 » для вашего dalvikvm-процесса. Стянув дамп из /data/misc к себе на компьютер с помощью adb pull его можно поизучать даже с помощью стандарной jvisualvm (standalone-приложение, кажется идет вместе с JDK, не требудет установки эклипса).
Статья, бесспорно, полезная для новичков.
Но я бы половину текста заменил 3-мя пунктами:
1. Придерживайтесь принципов ООП.
2. Используйте инструменты (классы, менеджеры, механизмы), предоставленные Android SDK.
3. Прочтите какую-нибудь книгу про «чистый код».
Например:
— Абстракция и инкапсуляция в кУпе с различными менеджерами SDK не дадут образоваться большей части описанных утечек.
— Использование локальных переменных вместо полей класса значительно облегчит работу сборщика мусора.
— …
И таких моментов много, которые следуют из основ. Читайте книги, они очень полезны.
Но я бы половину текста заменил 3-мя пунктами:
1. Придерживайтесь принципов ООП.
2. Используйте инструменты (классы, менеджеры, механизмы), предоставленные Android SDK.
3. Прочтите какую-нибудь книгу про «чистый код».
Например:
— Абстракция и инкапсуляция в кУпе с различными менеджерами SDK не дадут образоваться большей части описанных утечек.
— Использование локальных переменных вместо полей класса значительно облегчит работу сборщика мусора.
— …
И таких моментов много, которые следуют из основ. Читайте книги, они очень полезны.
И еще «4. питайтесь правильно».
На мой взгляд ваши советы довольно бесполезны относительно темы статьи. Ни ООП, не инструменты SDK не гарантируют вам отсутствие утечек памяти, т.к. все примеры с утечками выши включат в себя использование SDK и ООП. Тут нужно конкретно понимать какой код может приводить к утечкам и как это обходить.
Говоря про механизмы Android SDK тоже надо понимать, некоторые механизмы оказываются практически бесполезными для задач для которых они предназначались (у меня в голове пример с AsyncTask), так что для упрощения некоторых задач приходится использовать сторонние механизмы.
На мой взгляд ваши советы довольно бесполезны относительно темы статьи. Ни ООП, не инструменты SDK не гарантируют вам отсутствие утечек памяти, т.к. все примеры с утечками выши включат в себя использование SDK и ООП. Тут нужно конкретно понимать какой код может приводить к утечкам и как это обходить.
Говоря про механизмы Android SDK тоже надо понимать, некоторые механизмы оказываются практически бесполезными для задач для которых они предназначались (у меня в голове пример с AsyncTask), так что для упрощения некоторых задач приходится использовать сторонние механизмы.
ООП бесполезно? Нуну. А как же классика в лице утечки через Context? Стоит ли мне рассказывать как много утечек связано с незнанием принципа наследования и использования в дальнейшем контекста активити в получении чего-либо?
Вам вообще ничего не может гарантировать их отсутствие, как и это статья. Так стоит ли об этом говорить вообще?
инструменты SDK не гарантируют вам отсутствие утечек памяти
Вам вообще ничего не может гарантировать их отсутствие, как и это статья. Так стоит ли об этом говорить вообще?
Минусующие, вы не на GeekTimes, умейте аргументировать свои доводы, особенно в такой важной теме. А коль не знаете что сказать, то лучше и мышку не клацать, ибо ваше анонимное мнение может быть ошибочно воспринято другими.
Вам вообще ничего не может гарантировать их отсутствие, как и это статья. Так стоит ли об этом говорить вообще?
Жизнь вообще бессмысленная штука :)
Но вы правы — ничто не может гарантировать отсутствие ошибок. Даже если вы прекрасно понимаете всю теорию, вы все равно будете иногда делать опечатки или просто забывать про какие-то моменты.
Именно поэтому я рекомендую перед релизом приложения один раз пройтись по всему приложению и отловить все такие ошибки с помощью алгоритма, приведенного в конце статьи. Это занимает достаточно много времени, но это полезная практика. А теория — она больше для того, чтобы понимать что нужно искать.
MAT безусловно лучший инструмент которым можно отлавливать утечки и я трёмя руками за него, потому как и сам его активно использую. Правда я бы порекомендовал перед этим пройтись статическим анализатором кода, например Lint или FindBugs. Они порой указывают на места в коде с проблемами, при этом приводят текст проблемы. Бывает очень полезно.
Вы наверное как-то шире чем я трактуете слово ООП. Лично я не увидел какого-то нарушения ООП в примерах с утечками памяти. Речь идет о сохранении ссылок о которых разработчик может забывать или даже не подозревать, а не о таких вещах как инкапсуляция или полиморфизм.
Ну в этой статье есть во первых рекомендации по работе с инструментами по поиску утечек. Во-вторых более-менее типичные проблемы с утечками в Android UI программировании. Наверное классика утечек через Context тоже могла бы быть здесь.
Вам вообще ничего не может гарантировать их отсутствие, как и это статья. Так стоит ли об этом говорить вообще?
Ну в этой статье есть во первых рекомендации по работе с инструментами по поиску утечек. Во-вторых более-менее типичные проблемы с утечками в Android UI программировании. Наверное классика утечек через Context тоже могла бы быть здесь.
Никто и не говорил о нарушениях ООП. Говорили о
[quote]Речь идет о сохранении ссылок о которых разработчик может забывать или даже не подозревать[/quote]
О чем речь идет в принципе понятно. Просто это база для людей. Да, материал неплохой, но он содержит рекомендации для новичков, при этом говорит о MAT, который в свою очередь анализирует хип памяти, а эта тема уже далеко не для новичков.
И да, я не придираюсь, я просто заметил что ООП тут «причем». Инкапсуляции и полиморфизма тут нет, но вот наследование есть. Народ привык передавать контекст в методы и их не парит что переданная Activity или контекст активности = сама Activity. Нуачо, работает же.
Но я бы половину текста заменил 3-мя пунктами:
1. Придерживайтесь принципов ООП.
[quote]Речь идет о сохранении ссылок о которых разработчик может забывать или даже не подозревать[/quote]
О чем речь идет в принципе понятно. Просто это база для людей. Да, материал неплохой, но он содержит рекомендации для новичков, при этом говорит о MAT, который в свою очередь анализирует хип памяти, а эта тема уже далеко не для новичков.
И да, я не придираюсь, я просто заметил что ООП тут «причем». Инкапсуляции и полиморфизма тут нет, но вот наследование есть. Народ привык передавать контекст в методы и их не парит что переданная Activity или контекст активности = сама Activity. Нуачо, работает же.
Я не писал про гарантии и панацею. И я ни в коем случае не отрицал полезность тулзов для мониторинга памяти и статьи в целом.
Но проблемы с лишними ссылками на объекты (коим посвящено >50% статьи) возникают именно по причинам не следования принципам ООП.
Конечно же есть много других причин утечек памяти. Но именно описанные в статье проблемы решаются продуманной архитектурой и знанием инструментов SDK.
Но проблемы с лишними ссылками на объекты (коим посвящено >50% статьи) возникают именно по причинам не следования принципам ООП.
Конечно же есть много других причин утечек памяти. Но именно описанные в статье проблемы решаются продуманной архитектурой и знанием инструментов SDK.
Никогда не сохраняйте ссылки на activity (view, fragment, service) в статических переменных
Думаю что нужно просто очищать (=null) эту ссылку в onDestroy(). После вызова onDestroy() activity становится не валидной (т.е. не живой), поэтому ссылку в любом случае правильно было бы инвалидировать чтобы никто не воспользовался.
Если пройдете дальше по вашим ссылкам то увидете что полагаться вполне можно. onDestroy не вызовется если Android убьет процесс который хостит Activity — в этом случае беспокоится об утечках памяти не стоит. onDestory не рекомендуют для сохранения данных.
Впрочем я не настаиваю на onDestroy(). Если ссылка на activity нужна только когда activity видима можно управлять ею в onResume/OnPause, onStart/onStop. Однако пока выглядит так что обнуление ссылкив onDestroy() решит проблему утечек памяти.
Впрочем я не настаиваю на onDestroy(). Если ссылка на activity нужна только когда activity видима можно управлять ею в onResume/OnPause, onStart/onStop. Однако пока выглядит так что обнуление ссылкив onDestroy() решит проблему утечек памяти.
Приличных размеров статья о том, как избегать проблем с менеджментом памяти в языке, который создан чтобы упростить менеджмент памяти.
Все таки ИМХО куда проще писать на языке без GC, где всегда четко видно время жизни объекта и для избегания утечек достаточно придерживаться только одного правила: объект должен быть удален там же, где был создан.
Все таки ИМХО куда проще писать на языке без GC, где всегда четко видно время жизни объекта и для избегания утечек достаточно придерживаться только одного правила: объект должен быть удален там же, где был создан.
> Приличных размеров статья о том, как избегать проблем с менеджментом памяти в языке, который создан чтобы упростить менеджмент памяти.
Упростить != сделать неважным.
Представьте сколько способов накосячить в плюсах…
Упростить != сделать неважным.
Представьте сколько способов накосячить в плюсах…
Пожалуй первый за долгое время блог компании, стартовавший отличным постом. Спасибо и так держать.
Один из первых вопросов, с которым сталкивается каждый начинающий разработчик, это как передать объект из одного activity в следующий.
Используйте передачу объектов через Intent, либо вообще передавайте не объект, а id объекта (если у вас есть база данных, из которой этот id потом можно достать)
Каким же образом поступать, если надо передать сложный несериализуемый объект? Имхо, из-за невозможности сделать это простым, очевидным способом и растут кучи костылей.
если у вас сложный несериализуемый объект, то как поведёт себя приложение при принудительной его выгрузке и перезапуске потом?
Создается заново при каждом открытии приложения. Пример такого объекта менеджер закачек, кэширования и прочего. Служит для того чтобы не запрашивать много кратно одни и те-же данные, но так же не держать их устаревшими.
зачем передавать объект? хороший пример тут — наличие менеджера, где хранить эти сложные объекты. а между Activity передавать ID объектов.
Благодаря вашей статье увидел, что реклама от Гугла очень «течет» у меня в приложении.
Оказалось достаточно было в onStop удалять adView
И после повторных тестов утечек уже не было!
Я бы, даже не подумал туда посмотреть, если бы не вы.
Спасибо за отличный пост!
Что было



Оказалось достаточно было в onStop удалять adView
Вот так
AdView adView = (AdView) findViewById(R.id.adView);
adView.destroy();
adView.removeView(adView);
adView.destroy();
adView.removeView(adView);
И после повторных тестов утечек уже не было!
Я бы, даже не подумал туда посмотреть, если бы не вы.
Спасибо за отличный пост!
который продолжает существовать после того, как он должен быть уничтоженпосле того, как он перестал быть нужным.
А для Android Studio (IDEA) есть тулза, аналогичная MAT?
Цель — получить список ссылок на тот или иной объект.
Цель — получить список ссылок на тот или иной объект.
На данный момент плагинов для Android Studio, решающих эту задачу, нет.
Поставьте MAT как отдельную программу и воспользуйтесь инструкцией со stackoverflow.
Либо поставьте Eclipse с ADT и MAT и воспользуйтесь им. Вам даже проект импортировать в eclipse не потребуется.
Поставьте MAT как отдельную программу и воспользуйтесь инструкцией со stackoverflow.
Либо поставьте Eclipse с ADT и MAT и воспользуйтесь им. Вам даже проект импортировать в eclipse не потребуется.
Я открыл приложение и начал прокликивать экраны, ожидая, когда же приложение наконец упадет на моем Nexus 5. Наконец, через 5 минут и 55 экранов, приложение упало.
Вот поэтому я и проверяю плавность работы анимаций, скорость перехода между экраанами и прочие визуальные штуки (включая и OutOfMemoryError, причины которых я нет-нет, и допускаю в коде) в приложениях, которые я пишу, не на Nexus 5, а на довольно хилом HTC Desire X.
Не только потому, что он моментально приводит к утечке памяти (статическая переменная продолжит существовать пока существует приложение, и activity, на который она ссылается, никогда не будет выгружен). Этот подход также может привести к ситуации, когда вы будете обмениваться информацией не с тем экраном, ведь экран, невидимый пользователю, может в любой момент быть уничтожен и пересоздан лишь когда пользователь к нему вернется.
Вижу противоречие. Либо не будет выгружен, либо все-таки будет в любой момент уничтожен. Собственно, ссылки на GUI элементы и держат, когда нужно чтобы их не убило, и не надо было заново создавать и грузить экран при каждом переключении.
Я неправильно выразился. Он не может быть уничтожен, он может быть «забыт» системой.
Я поясню на примере:
Представьте, что ваше приложение состоит из двух Activity: ListActivity и DetailsActivity.
Когда пользователь запускает приложение, он попадает на ListActivtiy. Когда он выбирает элемент в ListActivity, открывается DetailsActivity.
Пользователь запускает приложение и система автоматически создает экземпляр класса ListActivity; назовем его объект 1. Вы сохраняете ссылку на этот объект в статической переменной. После этого пользователь переходит на экран DetailsActivity. В этот момент система создает экземпляр класса DetailsActivity; назовем его объект 2. После некоторого времени пользователь нажимает кнопку back и возвращается на экран ListActivity. Вот тут и начинаются проблемы.
В любой момент между тем, как пользователь ушел с экрана ListActivity и вернулся к нему обратно, система может «забыть» про объект 1. Это не значит, что объект будет обязательно уничтожен сборщиком мусора (вы же держите на него ссылку). Это значит, что в момент, когда пользователь вернется на экран ListActivity вместо того, чтобы использовать старый объект 1, система создаст новый экземпляр класса ListActivity, объект 3. У вас в системе будет одновременно два экземпляра класса ListActivity: объект 1, хранящийся в вашей статической переменной и объект 3, отображаемый пользователю.
Про объект 1 будете знать только вы — он уже никогда не будет показан пользователю. Вы можете изменять его поля, вызывать его методы, но визуально это не будет приводить ни к чему — ведь пользователь видит на экране объект 3, а не объект 1.
Вы можете легко добиться описанного эффекта с двумя экземплярами ListActivity включив опцию Settings>Developer options>Don't keep activities.
Поэтому ваше утверждение о том, что
в корне неверно относительно Activity (с fragments или views так получится — их созданием управляете вы сами, а не система).
Я поясню на примере:
Представьте, что ваше приложение состоит из двух Activity: ListActivity и DetailsActivity.
Когда пользователь запускает приложение, он попадает на ListActivtiy. Когда он выбирает элемент в ListActivity, открывается DetailsActivity.
Пользователь запускает приложение и система автоматически создает экземпляр класса ListActivity; назовем его объект 1. Вы сохраняете ссылку на этот объект в статической переменной. После этого пользователь переходит на экран DetailsActivity. В этот момент система создает экземпляр класса DetailsActivity; назовем его объект 2. После некоторого времени пользователь нажимает кнопку back и возвращается на экран ListActivity. Вот тут и начинаются проблемы.
В любой момент между тем, как пользователь ушел с экрана ListActivity и вернулся к нему обратно, система может «забыть» про объект 1. Это не значит, что объект будет обязательно уничтожен сборщиком мусора (вы же держите на него ссылку). Это значит, что в момент, когда пользователь вернется на экран ListActivity вместо того, чтобы использовать старый объект 1, система создаст новый экземпляр класса ListActivity, объект 3. У вас в системе будет одновременно два экземпляра класса ListActivity: объект 1, хранящийся в вашей статической переменной и объект 3, отображаемый пользователю.
Про объект 1 будете знать только вы — он уже никогда не будет показан пользователю. Вы можете изменять его поля, вызывать его методы, но визуально это не будет приводить ни к чему — ведь пользователь видит на экране объект 3, а не объект 1.
Вы можете легко добиться описанного эффекта с двумя экземплярами ListActivity включив опцию Settings>Developer options>Don't keep activities.
Поэтому ваше утверждение о том, что
Собственно, ссылки на GUI элементы и держат, когда нужно чтобы их не убило, и не надо было заново создавать и грузить экран при каждом переключении.
в корне неверно относительно Activity (с fragments или views так получится — их созданием управляете вы сами, а не система).
Это значит, что в момент, когда пользователь вернется на экран ListActivity вместо того, чтобы использовать старый объект 1, система создаст новый экземпляр класса ListActivity, объект 3. У вас в системе будет одновременно два экземпляра класса ListActivity: объект 1, хранящийся в вашей статической переменной и объект 3, отображаемый пользователю.
А, понятно, специфическое поведение активити.
Sign up to leave a comment.
Что такое утечки памяти в android, как проверить программу на их отсутствие и как предотвратить их появление