Intereting Posts

В Android-приложении проблемы с памятью – пробовал все и все еще в недоумении

Я потратил 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 , но в этом значении активность действительно есть только 1 адаптер, но я Хотел упомянуть об этом в любом случае).

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

Я до сих пор не понимаю, почему неактивные действия, которые находятся в стеке, не получаются, и мне очень хотелось бы разобраться в этом.

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

Спасибо.

Solutions Collecting From Web of "В Android-приложении проблемы с памятью – пробовал все и все еще в недоумении"

Я до сих пор не понимаю, почему неактивные действия, которые находятся в стеке, не получаются, и мне очень хотелось бы разобраться в этом.

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

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

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

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

Вы можете захотеть использовать новые API-интерфейсы Fragment, чтобы заменить этот произвольный стек действий одним действием, которое более эффективно управляет его памятью. Например, если вы используете объекты заднего стека для фрагментов, когда фрагмент переходит в задний стек и больше не отображается, его метод onDestroyView () вызывается для полного удаления иерархии его представлений, что значительно уменьшает его площадь.

Теперь, когда вы ругаетесь в потоке, где вы нажимаете назад, переходите к активности, отжимаете назад, переходите к другому действию и т. Д. И никогда не имеете глубокого стека, тогда да, у вас просто есть утечка. В этом блоге описывается, как отлаживать утечки: http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html

Некоторые советы:

  1. Убедитесь, что вы не являетесь контекстом активности утечки.

  2. Убедитесь, что вы не храните ссылки на растровые изображения. Очистите все ваши ImageView в Activity # onStop, примерно так:

     Drawable d = imageView.getDrawable(); if (d != null) d.setCallback(null); imageView.setImageDrawable(null); imageView.setBackgroundDrawable(null); 
  3. Переработайте растровые изображения, если они вам больше не нужны.

  4. Если вы используете кеш памяти, например memory-lru, убедитесь, что он не использует много памяти.

  5. Не только изображения занимают много памяти, убедитесь, что вы не храните слишком много других данных в памяти. Это легко может произойти, если в вашем приложении есть бесконечные списки. Попробуйте кэшировать данные в DataBase.

  6. В Android 4.0 есть ошибка (stackoverflow # 13754876) с аппаратным ускорением, поэтому, если вы используете hardwareAccelerated=true в своем манифесте, это приведет к утечке памяти. GLES20DisplayList – сохранить ссылки на холдинг, даже если вы сделали шаг (2), и никто другой не ссылается на это растровое изображение. Здесь вам нужно:

    A) отключить аппаратное ускорение для api 16/17;
    или
    Б) отделить представление, которое поддерживает растровое изображение

  7. Для Android 3+ вы можете использовать android:largeHeap="true" в AndroidManifest . Но это не решит проблемы с памятью, просто отложите их.

  8. Если вам нужно, например, бесконечная навигация, то Фрагменты – это ваш выбор. Таким образом, у вас будет 1 активность, которая будет просто переключаться между фрагментами. Таким образом, вы также сможете решить некоторые проблемы с памятью, например номер 4.

  9. Используйте Memory Analyzer, чтобы узнать причину утечки памяти.
    Здесь очень хорошее видео из Google I / O 2011: Управление памятью для Android-приложений
    Если вы имеете дело с растровыми изображениями, это должно быть обязательно прочитано: Эффективное отображение битмапов

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

У вас есть ссылки на каждую активность? AFAIK – это причина, из-за которой Android не удаляет действия из стека.

Мы можем воспроизвести эту ошибку и на других устройствах? Я испытал странное поведение некоторых устройств Android в зависимости от ПЗУ и / или производителя оборудования.

Я думаю, что проблема, может быть, сочетание многих факторов, изложенных здесь в ответах, – вот что дает вам проблемы. Как и @Tim, ссылка (статическая) на активность или элемент в этом действии может привести к тому, что GC пропустит Activity. Вот статья, обсуждающая этот аспект. Я думаю, что вероятная проблема возникает из-за того, что активность в состоянии «видимого процесса» или выше, что в значительной степени гарантирует, что активность и связанные с ней ресурсы никогда не будут восстановлены.

Некоторое время назад я сталкивался с противоположной проблемой со Службой, так вот что заставило меня переходить к этой мысли: есть что-то, что удерживает вашу активность в списке приоритетов процессов, чтобы она не подвергалась системному GC, например, Ссылку (@Tim) или цикл (@Alvaro). Цикл не обязательно должен быть бесконечным или долго выполняемым элементом, просто что-то, что много работает, как рекурсивный метод или каскадный цикл (или что-то в этих строках).

EDIT: Как я понимаю, onPause и onStop автоматически вызываются Android. Методы в основном предназначены для вас, чтобы вы могли позаботиться о том, что вам нужно, прежде чем процесс хостинга будет остановлен (сохранение переменных, сохранение состояния вручную и т. Д.); Но обратите внимание, что четко указано, что onStop (вместе с onDestroy) не может быть вызван в каждом случае. Кроме того, если в процессе хостинга также размещен Activity, Service и т. Д., Который имеет статус «Forground» или «Visible», ОС может даже не смотреть на остановку процесса / потока. Например: активность и служба обеимируются в одном и том же процессе, и Служба возвращает START_STICKY из onStartCommand() процесс автоматически принимает хотя бы видимый статус. Это может быть ключом здесь, попробуйте объявить новый proc для Activity и посмотреть, не изменит ли это что-либо. Попробуйте добавить эту строку к объявлению своей деятельности в манифесте как: android:process=":proc2" а затем снова запустить тесты, если ваша активность разделяет процесс с чем-либо еще. Мысль здесь состоит в том, что если вы очистили свою деятельность и уверены, что проблема не в вашей деятельности, тогда еще одна проблема и ее время охотиться за этим.

Кроме того, я не могу вспомнить, где я его видел (если я даже видел это в документах Android), но я помню, что что-то о PendingIntent ссылающемся на Activity, может привести к тому, что Activity будет вести себя таким образом.

Вот ссылка на onStartCommand() с некоторыми сведениями о процессе, не убивающем фронт.

Поэтому единственное, о чем я могу думать, – это если у вас есть статическая переменная, которая прямо или косвенно ссылается на контекст. Даже что-то такое, что касается ссылки на часть приложения. Я уверен, что вы уже пробовали это, но я буду предлагать его на всякий случай, попробуйте просто отключить ВСЕ ваши статические переменные в onDestroy (), чтобы убедиться, что сборщик мусора получает его

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

Попробуйте передать getApplicationContext () на все, что требует контекста. У вас может быть глобальная переменная, содержащая ссылку на ваши действия и предотвращающую сбор мусора.

Одна из вещей, которая действительно помогла проблеме памяти в моем случае, оказалась для параметра inPurgeable истинным для моих битмапов. См. Почему я никогда не использовал параметр BitPapgeable BitmapFactory? И обсуждение ответа для получения дополнительной информации.

Ответ Дайанн Хакборн и наше последующее обсуждение (также спасибо, CommonsWare) помогли прояснить некоторые вещи, с которыми я был смущен, поэтому спасибо за это.

Я столкнулся с той же проблемой с тобой. Я работал над приложением мгновенного обмена сообщениями, для одного и того же контакта можно запустить ProfileActivity в ChatActivity и наоборот. Я просто добавляю строку в намерение начать другое действие, оно берет информацию о типе класса активности стартера и идентификаторе пользователя. Например, ProfileActivity запускает ChatActivity, а затем в ChatActivity.onCreate, я отмечаю тип класса invoker 'ProfileActivity' и идентификатор пользователя, если он собирается запустить Activity, я бы проверял, является ли это 'ProfileActivity' для пользователя или нет , Если это так, просто назовите «finish ()» и вернитесь к прежней ProfileActivity, а не создайте новую. Утечка памяти – это еще одна вещь.