Почему объем кучи увеличивается при повторном запуске действия? - PullRequest
13 голосов
/ 26 июля 2011

Этот вопрос касается памяти в Android.

Мой метод:

У меня есть два актива, A и B. Из A я запускаю B следующим образом:

Intent i = new Intent(A.this, B.class);
startActivity(i);

По нажатию кнопки в B, я делаю это:

B.this.finish();
  • В B я переопределяю метод onDestroy и устанавливаю все ссылки на null.
  • Я не выделяю новую память в методе onResume для A.
  • Я не пропускаю контекст.
  • Я не использую несколько потоков.
  • Я не пользуюсь услугами.
  • Все переменные в B являются переменными частного класса, и все они имеют значение null в onDestroy объекта B.
  • Кроме того, для ImageViews в B задан нулевой фон в onDestroy из B.
  • Я уверен, что Б. будет уничтожен.

Результат:

Когда я в Деятельности A, объем кучи составляет 7,44 МБ. Затем, когда я запускаю B и вызываю финиш на B (и, следовательно, возвращаюсь к A), куча увеличивается на 0,16 МБ. Повторяя этот процесс снова, куча увеличивается на 0,08 МБ каждый раз.

  • Я не смотрю на предел кучи, я смотрю на выделенную кучу.
  • Я вызываю System.gc () в конце метода onDestroy для B.

Дополнительная информация:

-Я использовал MAT для анализа распределения памяти и пытался найти эту утечку. Что-то странное в том, что у Деятельности B, кажется, есть 5 экземпляров Как это случилось, я повторял процесс startActivity / finish 5 раз. Нижняя запись - это активность, остальные слушатели в активности:

enter image description here

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

Dominator Tree

-Я смотрел оба видео Google IO об использовании памяти (и утечки).

Вопрос:

Возможно ли, что эти 0,08 МБ кучи будут всегда выделяться (и не собираться GC) независимо от того, что я делаю? Если нет, есть идеи о том, что может быть причиной этого?

Обновление:

  1. Я попытался запустить действие B, не устанавливая представление содержимого в B. Это означает, что B - совершенно пустое действие. В результате кучная память НЕ увеличилась, когда я перезапускаю действие несколько раз. Обратите внимание, однако, что это не решение. Я должен иметь возможность настроить представление содержимого.

  2. scorpiodawg: я попытался запустить свое приложение на эмуляторе, и куча все еще растет. Хорошая попытка, хотя.

  3. ntc: я изменил все вхождения «this» на «getApplicationContext ()», где это было возможно. Я не мог вызвать setContentView (getApplicationContext ()); потому что setContentView хочет ссылку на файл макета, а не контекст. Вместо этого я создал пустой файл макета и вызвал setContentView (emptylayout); в методе onDestroy действия B. Это не помогло.

  4. Я попытался удалить весь код, чтобы вызывался только setContentView (mylayout). Проблема сохраняется. Затем я удалил все элементы графического интерфейса в XML-файле макета. Проблема сохраняется. Единственной вещью, которая осталась, были представления контейнера, пара вложенных линейных, относительных и scrolllayouts. Я попытался удалить настройку атрибута «android: scrollbarDefaultDelayBeforeFade» на полосе прокрутки. Результат был великолепен, утечка памяти исчезла. Затем я вернул обратно весь код, который ранее удалил, но не установил атрибут «android: scrollbarDefaultDelayBeforeFade», и утечка памяти вернулась. Насколько это странно?

Ответы [ 5 ]

11 голосов
/ 12 августа 2011

Если у вас есть 5 экземпляров действия B, значит, вы неправильно управляете стеком действий.Я считаю, что лучший способ проверить это с помощью команды CLI:

adb shell dumpsys meminfo 'имя вашего пакета приложений'

У меня былопохожая проблема в двух активном проекте, когда я переключался между ними.Каждый раз, когда я переключался, я получал новый экземпляр в стеке, как показано вышеупомянутой командой.Затем я установил флаги для запущенных действий на FLAG_ACTIVITY_REORDER_TO_FRONT с кодом:

Intent i = new Intent("com.you.yourActivityB");
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(i);

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

3 голосов
/ 08 августа 2011

Похоже, что у вас есть утечка памяти в этом действии.

Возможно, вы пропускаете контекст (действие в этом случае).Поэтому убедитесь, что все ссылки на контекст очищаются при вызове метода onDestroy в действии. Подробнее здесь .

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

2 голосов
/ 10 августа 2011

Что я думаю, это типичный вопрос Java.И уничтожение действия не означает, что связанные с ним объекты должны быть удалены из heap, даже если они находятся на потерянной земле (их ссылка нулевая).потому что он полностью зависит от виртуальной машины, когда она вызывает Garbage collector (независимо от того, что вы говорите System.GC()).поэтому, когда это состояние почти не хватает памяти, оно вызывает его и очищает (не может быть уверен снова, может быть сразу после того, как их ссылка становится null), поэтому я не думаю, что вам следует беспокоиться об этом.

отредактировано: вызов setContentView(getApplicationContext);

и где бы вы ни находились, this ключевое слово для передачи контекста, измените его на this.getApplicationContext()

1 голос
/ 02 августа 2011

Рассмотрите возможность использования

android:launchMode="singleTask"

в вашем AndroidManifest.xml. Смотрите это .

0 голосов
/ 10 августа 2011

Возможно ли, что эти 0,08 МБ кучи всегда будут выделяться (а не собираться> GC) независимо от того, что я делаю?Если нет, есть ли какие-либо идеи о том, что может быть причиной этого?

0.08 МБ кучи, если она не используется, будет возвращена, когда система сочтет, что это необходимо.Сборка мусора не происходит, как только вы вызываете System.gc (), это больше похоже на запрос на сборку мусора как можно скорее.Часто между выделенным объектом и его удалением из памяти существует время LONG .Спецификация виртуальной машины Java и спецификация языка Java определяют время жизни объекта, проходящего следующие этапы:

  1. Создано
  2. Используется (доступно)
  3. Невидимый
  4. недоступен
  5. собран
  6. завершен
  7. освобожден

Обратите внимание, что когда объект недоступен, он просто становится кандидатом насборка мусора, как и когда это может произойти.Даже с такой маленькой JVM-сетью, как Dalvik, я не думаю, что объект подвергнется полной жизни за столь короткий промежуток времени.GC - дорогостоящая операция, поэтому выполняется только тогда, когда это необходимо.

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

Для получения дополнительной информации о том, как происходит GC в Java, это довольно хорошая статья

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...