Подтвердить что ты не робот

Почему память кучи увеличивается при повторном запуске?

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

Мой метод:

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

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

Нажмите кнопку B в B, я сделаю следующее:

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

Результат:

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

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

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

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

enter image description here

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

Dominator Tree

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

Вопрос:

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

Update:

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

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

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

  • Я попытался удалить весь код, чтобы вызвать только setContentView (mylayout). Проблема сохранилась. Затем я удалил все элементы gui в XML файле макета. Проблема сохранилась. Единственное, что осталось, это просмотр контейнеров, несколько вложенных линейных, относительных и scrolllayouts. Я попытался удалить настройку атрибута "android: scrollbarDefaultDelayBeforeFade" в полосе прокрутки. Результат был большим, утечка памяти исчезла. Затем я вернул весь код, который я ранее удалил, но не установил атрибут "android: scrollbarDefaultDelayBeforeFade", и утечка памяти вернулась. Как это странно?

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

android:launchMode="singleTask"

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

Ответ 5

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

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

  • Созданный
  • В использовании (сильно доступно)
  • Невидимый
  • Недоступен
  • Собранная
  • Завершена
  • высвобождены

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

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

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