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 я не могу точно определить ее.

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

страницы значков получают данные из интернета, загружают их в массив EntityData из BaseAdapter и передают его в ListView (я фактически использую CommonsWare отличный MergeAdapter, но в этой деятельности значка, действительно только 1 Адаптер в любом случае, но я хотел упомянуть этот факт в любом случае).

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

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

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

спасибо.

10 84

10 ответов:

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

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

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

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

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

вы можете рассмотреть возможность использования новых API фрагментов чтобы заменить этот произвольный стек действий одним действием, которое более плотно управляет своей памятью. Например, если вы используете средства заднего стека фрагментов, когда фрагмент переходит в задний стек и больше не виден, его метод 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. не только изображения занимают много памяти, убедитесь, что вы не держите слишком много других данных в памяти. Это легко может произойти, если у вас есть бесконечные списки в вашем приложении. Попробуйте кэшировать данные в базе данных.

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

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

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

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

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

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

вы держите некоторые ссылки на каждый вид деятельности? Насколько мне известно, это причина, которая удерживает Android с удалив деятельности из стека.

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

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

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

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

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

здесь ссылка для onStartCommand() страница с некоторыми сведениями о процессе non-killing front.

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

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

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

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

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

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