Я потратил 4 полных дня, пытаясь изо всех сил выяснить утечку памяти в разрабатываемом мной приложении, но все это давно перестало иметь смысл.
Приложение, которое я разрабатываю, носит социальный характер, поэтому подумайте о профильных мероприятиях (P) и перечислите действия с данными - например, значки (B). Вы можете переходить из профиля в список значков, в другие профили, в другие списки и т. Д.
Итак, представьте себе такой поток: P1 -> B1 -> P2 -> B2 -> P3 -> B3 и т. Д. Для единообразия я загружаю профили и значки одного и того же пользователя, поэтому каждая страница P одинакова, как и каждую страницу B.
Общая суть проблемы такова: после некоторой навигации, в зависимости от размера каждой страницы, я получаю исключение нехватки памяти в случайных местах - растровых изображениях, строках и т. Д. - это не похоже на согласованность.
Сделав все возможное, чтобы выяснить, почему у меня заканчивается память, я ничего не придумал. Я не понимаю, почему Android не убивает P1, B1 и т. Д., Если ему не хватает памяти при загрузке и вместо этого происходит сбой. Я ожидал, что эти предыдущие действия умрут и воскреснут, если я когда-нибудь вернусь к ним через onCreate () и onRestoreInstanceState ().
Не говоря уже об этом - даже если я сделаю P1 -> B1 -> Back -> B1 -> Back -> B1, у меня все равно будет сбой. Это указывает на какую-то утечку памяти, но даже после сброса hprof и использования MAT и JProfiler я не могу ее точно определить.
Я отключил загрузку изображений из Интернета (и увеличил количество загружаемых тестовых данных, чтобы компенсировать это и сделать тест честным) и убедился, что кеш изображений использует SoftReferences. Android фактически пытается освободить несколько имеющихся у него SoftReferences, но прямо перед тем, как он вылетает из памяти.
Страницы значков получают данные из Интернета, загружают их в массив EntityData из BaseAdapter и передают их в ListView (на самом деле я использую отличный MergeAdapter CommonsWare , но в этом действии Badge действительно есть только 1 адаптер, но я в любом случае хотел упомянуть этот факт).
Я просмотрел код и не смог найти ничего, что могло бы протечь. Я очистил и аннулировал все, что смог найти, и даже System.gc () слева и справа, но приложение все равно вылетает.
Я до сих пор не понимаю, почему неактивные действия, которые находятся в стеке, не собираются, и мне бы очень хотелось в этом разобраться.
На данный момент я ищу любые подсказки, советы, решения ... все, что могло бы помочь.
Спасибо.
источник
outOfMemory
ошибку Благодарность!Ответы:
Все не так. Единственное управление памятью, которое влияет на жизненный цикл активности, - это глобальная память во всех процессах, поскольку Android решает, что ей не хватает памяти, и поэтому для ее восстановления необходимо завершить фоновые процессы.
Если ваше приложение находится на переднем плане, начиная все больше и больше действий, оно никогда не переходит в фоновый режим, поэтому оно всегда достигает своего локального лимита памяти процесса, прежде чем система когда-либо приблизится к тому, чтобы убить свой процесс. (И когда он действительно убивает свой процесс, он убивает процесс, в котором выполняются все действия, включая все, что в данный момент находится на переднем плане.)
Мне кажется, ваша основная проблема заключается в следующем: вы позволяете запускать слишком много действий одновременно, и / или каждое из этих действий требует слишком большого количества ресурсов.
Вам просто нужно изменить дизайн вашей навигации, чтобы не полагаться на произвольное количество потенциально тяжелых действий. Если вы не сделаете серьезный объем работы в onStop () (например, вызовете setContentView (), чтобы очистить иерархию представления активности и очистить переменные от всего, что она может удерживать), у вас просто закончится нехватка памяти.
Вы можете рассмотреть возможность использования новых API-интерфейсов Fragment, чтобы заменить этот произвольный стек действий одним действием, которое более жестко управляет своей памятью. Например, если вы используете средства обратного стека фрагментов, когда фрагмент попадает в задний стек и больше не отображается, вызывается его метод onDestroyView (), чтобы полностью удалить его иерархию представлений, значительно уменьшая его размер.
Теперь, если вы терпите крах в потоке, когда вы нажимаете назад, переходите к действию, нажимаете назад, переходите к другому действию и т.д. и никогда не имеете глубокого стека, тогда да, у вас просто утечка. Это сообщение в блоге описывает, как отлаживать утечки: http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html
источник
Activity
JavaDocs.Несколько советов:
Убедитесь, что вы не используете контекст активности утечки.
Убедитесь, что вы не храните ссылки на растровые изображения. Очистите все ваши ImageView в Activity # onStop, примерно так:
Drawable d = imageView.getDrawable(); if (d != null) d.setCallback(null); imageView.setImageDrawable(null); imageView.setBackgroundDrawable(null);
Утилизируйте растровые изображения, если они вам больше не нужны.
Если вы используете кеш памяти, например memory-lru, убедитесь, что он не использует слишком много памяти.
Не только изображения занимают много памяти, убедитесь, что вы не храните в памяти слишком много других данных. Это легко может произойти, если в вашем приложении есть бесконечные списки. Попробуйте кэшировать данные в базе данных.
В android 4.2 есть ошибка (stackoverflow # 13754876) с аппаратным ускорением, поэтому, если вы используете
hardwareAccelerated=true
в своем манифесте, произойдет утечка памяти.GLES20DisplayList
- продолжайте удерживать ссылки, даже если вы выполнили шаг (2), и никто другой не ссылается на это растровое изображение. Здесь вам понадобятся:а) отключить аппаратное ускорение для api 16/17;
или
б) отсоединить представление, которое удерживает растровое изображение
Для Android 3+ вы можете попробовать использовать
android:largeHeap="true"
в своемAndroidManifest
. Но это не решит ваших проблем с памятью, просто отложите их.Если вам нужна как бы бесконечная навигация, то Фрагменты - ваш выбор. Таким образом, у вас будет 1 действие, которое будет просто переключаться между фрагментами. Таким образом вы также решите некоторые проблемы с памятью, например, номер 4.
Используйте анализатор памяти, чтобы выяснить причину утечки памяти.
Вот очень хорошее видео с Google I / O 2011: Управление памятью для приложений Android
Если вы имеете дело с растровыми изображениями, это необходимо прочитать: Эффективное отображение растровых изображений
источник
outOfMemory
ошибку Благодарность!Растровые изображения часто являются виновниками ошибок памяти на Android, так что это хорошая область для двойной проверки.
источник
outOfMemory
ошибку Благодарность!У вас есть ссылки на каждое действие? AFAIK, это причина, по которой Android не может удалять действия из стека.
Мы можем воспроизвести эту ошибку и на других устройствах? Я испытал странное поведение некоторых устройств Android в зависимости от ПЗУ и / или производителя оборудования.
источник
Я думаю, что проблема может быть связана с сочетанием многих факторов, указанных здесь в ответах, и это то, что вызывает у вас проблемы. Как сказал @Tim, (статическая) ссылка на действие или элемент в этом действии может заставить GC пропустить действие. Вот статья, в которой обсуждается этот аспект. Я думаю, что вероятная проблема возникает из-за того, что что-то держит Activity в состоянии «Visible Process» или выше, что в значительной степени гарантирует, что Activity и связанные с ним ресурсы никогда не будут восстановлены.
Некоторое время назад я столкнулся с противоположной проблемой с помощью службы, поэтому я подумал: есть что-то, что держит вашу активность на высоком уровне в списке приоритетов процесса, так что она не будет подчиняться системному GC, например ссылка (@Tim) или цикл (@Alvaro). Цикл не обязательно должен быть бесконечным или длительным элементом, просто чем-то, что работает во многом как рекурсивный метод или каскадный цикл (или что-то в этом роде).
РЕДАКТИРОВАТЬ: насколько я понимаю, onPause и onStop автоматически вызываются Android по мере необходимости. Методы предназначены в основном для вас, чтобы вы могли позаботиться о том, что вам нужно, прежде чем процесс хостинга будет остановлен (сохранение переменных, сохранение состояния вручную и т. Д.); но обратите внимание, что четко указано, что onStop (вместе с onDestroy) не может быть вызван в каждом случае. Кроме того, если в процессе хостинга также размещается Activity, Service и т. Д. Со статусом «Forground» или «Visible», ОС может даже не смотреть на остановку процесса / потока. Например: действие и служба запускаются в одном процессе, и служба возвращается
START_STICKY
изonStartCommand()
процесс автоматически принимает хотя бы видимый статус. Это может быть ключевым моментом, попробуйте объявить новую процедуру для Activity и посмотрите, изменится ли это что-нибудь. Попробуйте добавить эту строку к объявлению вашей Activity в манифесте как:android:process=":proc2"
а затем снова запустите тесты, если ваша Activity разделяет процесс с чем-либо еще. Мысль здесь в том, что если вы очистили свою Activity и почти уверены, что проблема не в вашей Activity, то проблема в другом, и пора заняться этим.Кроме того, я не могу вспомнить, где я это видел (если я даже видел это в документации Android), но я помню, что что-то о
PendingIntent
ссылке на Activity может привести к тому, что Activity ведет себя таким образом.Вот ссылка на
onStartCommand()
страницу с некоторыми взглядами на процесс, не связанный с убийством.источник
поэтому единственное, о чем я действительно могу думать, - это иметь ли у вас статическую переменную, которая прямо или косвенно ссылается на контекст. Даже что-то вроде ссылки на часть приложения. Я уверен, что вы уже пробовали это, но я предлагаю это на всякий случай, попробуйте просто обнулить ВСЕ ваши статические переменные в onDestroy (), чтобы убедиться, что сборщик мусора получает это
источник
Самый большой источник утечки памяти, который я обнаружил, был вызван некоторой глобальной, высокоуровневой или давней ссылкой на контекст. Если вы где-нибудь храните «контекст» в переменной, вы можете столкнуться с непредсказуемыми утечками памяти.
источник
Попробуйте передать getApplicationContext () всему, что требует контекста. У вас может быть глобальная переменная, которая содержит ссылку на ваши действия и предотвращает их сборку мусора.
источник
Одна из вещей, которая действительно помогла проблеме с памятью в моем случае, заключалась в том, что для моих растровых изображений inPurgeable было установлено значение true. См. Раздел « Почему бы мне вообще НЕ использовать параметр inPurgeable BitmapFactory?» и обсуждение ответа для получения дополнительной информации.
Ответ Дайанн Хакборн и наше последующее обсуждение (также спасибо CommonsWare) помогли прояснить некоторые вещи, которые меня смущали, так что спасибо вам за это.
источник
Я столкнулся с той же проблемой и с вами. Я работал над приложением для обмена мгновенными сообщениями, для того же контакта можно запустить ProfileActivity в ChatActivity и наоборот. Я просто добавляю дополнительную строку в намерение начать другое действие, оно берет информацию о типе класса стартовой активности и идентификаторе пользователя. Например, ProfileActivity запускает ChatActivity, затем в ChatActivity.onCreate я отмечаю тип класса invoker «ProfileActivity» и идентификатор пользователя, если он собирается запустить Activity, я бы проверил, является ли это «ProfileActivity» для пользователя или нет. . Если это так, просто вызовите finish () и вернитесь к прежнему ProfileActivity вместо того, чтобы создавать новый. Другое дело - утечка памяти.
источник