Какие инструменты и методы Android работают лучше всего для обнаружения утечек памяти / ресурсов? [закрыто]

152

У меня разработано приложение для Android, и я нахожусь на стадии разработки приложения для телефона, где, кажется, все работает хорошо, и вы хотите объявить о победе и победе, но вы знаете, что просто должны быть некоторые утечки памяти и ресурсов там; и на Android всего 16 Мб кучи, и, по-видимому, удивительно легко просочиться в приложение для Android.

Я искал вокруг и до сих пор был в состоянии только выкопать информацию о «hprof» и «traceview», и ни один не получил много положительных отзывов.

С какими инструментами или методами вы столкнулись или разработали и хотите поделиться, возможно, в проекте ОС?

jottos
источник
3
Могу ли я проголосовать за то, чтобы РȢѸСТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ изменил свое имя на что-нибудь удобочитаемое человеком.
JPM
1
Было бы нормально снова открыть этот вопрос, если мы удалим слово «Инструменты Android» из заголовка вопроса? Ответы, найденные здесь, были весьма полезны для решения моих проблем с утечкой памяти
k3b
Итак, у меня есть пользователь, постоянно переключающийся между действиями, то есть он может переключить 15 действий за 20 секунд. Может ли это быть причиной нехватки памяти? Что я должен сделать, чтобы это исправить? Спасибо!
Ручир Барония
1
Не могу предоставить это как ответ, потому что вопрос был закрыт, однако я бы рекомендовал взглянуть на Leak Canary . Просто используйте свое приложение, открывайте и закрывайте действия, и пусть библиотека сделает свое дело. Он даже скажет вам, где произошла утечка. Просто дайте анализатору утечки некоторое время, чтобы выполнить свою работу после того, как произошла утечка - обычно это занимает около 2 минут или более, пока не будет найден источник утечки. После этого он представит его вам аккуратно в приложении. Никаких дополнительных инструментов не требуется!
Ubuntudroid

Ответы:

90

Одной из самых распространенных ошибок, которые я обнаружил при разработке приложений для Android, является ошибка «java.lang.OutOfMemoryError: Размер растрового изображения превышает бюджет виртуальной машины». Я часто обнаруживал эту ошибку в действиях, использующих множество растровых изображений после изменения ориентации: действие уничтожается, создается заново, а макеты «раздуваются» из XML, потребляя память виртуальной машины, доступную для растровых изображений.

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

Сначала установите атрибут «id» в родительском представлении макета XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Затем в методе onDestroy () вашего Activity вызовите метод unbindDrawables (), передав ссылку на родительский View, а затем выполните System.gc ().

@Override
protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Этот метод unbindDrawables () рекурсивно исследует дерево представлений и:

  1. Удаляет обратные вызовы на всех фоновых рисунках
  2. Удаляет детей в каждой группе
hp.android
источник
3
Хорошее решение общей проблемы.
Пока-E
9
Это не работает для подклассов AdapterView (ListView, GridView и т. Д.).
Арджун
@Arjun Да .. это не работает для подклассов AdapterView. Для этого вам нужно обработать исключение. Отдых работает нормально. Это то, что я использую в своем коде, и он отлично работает. Надеюсь это поможет.
hp.android
@ hp.android "Обрабатывать это в исключении" у вас есть пример того, как это будет выглядеть? Я спрашиваю, потому что у меня есть страницы с изображениями в моем, PageAdapterи я работаю над этой ошибкой :(
Jacksonkr
4
@ Джексон просто измените условие: если (просмотр экземпляра ViewGroup &&! (Просмотр экземпляра AdapterView)) это избавит от исключения, которое вы получаете для Adapter
hp.android
28

В основном для путешественников Google из будущего:

К сожалению, большинство инструментов Java не подходят для этой задачи, потому что они анализируют только JVM-кучу. Каждое Android-приложение также имеет собственную кучу, которая также должна умещаться в пределах ~ 16 МБ. Например, он обычно используется для растровых данных. Таким образом, вы можете легко столкнуться с ошибками нехватки памяти, даже если ваша JVM-куча занимает около 3 МБ, если вы используете много отрисовок.

Тимо Ор
источник
6
Начиная с Android 3.0 (соты), рисунки хранятся в куче
Gu1234
@Тимо тогда что бы вы использовали для обнаружения утечек в родной куче?
sydd
1
Тестирование, много испытаний. Проблема в том, что вы даже не можете точно сказать, сколько памяти использует ваше приложение, из-за совместного использования памяти и других методов оптимизации. Вы можете получить показания памяти, используя обычные команды оболочки, но это очень и очень грубые оценки.
Тимо Ор
20

Ответ от @ hp.android хорошо работает, если вы просто работаете с растровым фоном, но в моем случае у меня был BaseAdapterнабор ImageViews для a GridView. Я изменил unbindDrawables()метод в соответствии с рекомендациями, чтобы условие было:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

но проблема в том, что рекурсивный метод никогда не обрабатывает потомков AdapterView. Чтобы решить эту проблему, я вместо этого сделал следующее:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

так что потомки по- AdapterViewпрежнему обрабатываются - метод просто не пытается удалить все потомки (что не поддерживается).

Однако это не совсем решает проблему, поскольку она ImageViewуправляет растровым изображением, которое не является их фоном. Поэтому я добавил следующее. Это не идеально, но работает:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

В целом unbindDrawables()метод тогда:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

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

darrenp
источник
12

Хороший доклад о вводе / выводе в Google (2011) по управлению памятью в Android, а также подробности об инструментах и ​​методах профилирования памяти:
http://www.youtube.com/watch?v=_CruQY55HOk

greg7gkb
источник
4
Или соответствующее сообщение в блоге: android-developers.blogspot.com/2011/03/…
greg7gkb
1
Итак, у меня есть пользователь, постоянно переключающийся между действиями, то есть он может переключить 15 действий за 20 секунд. Может ли это быть причиной нехватки памяти? Что я должен сделать, чтобы это исправить? Спасибо!'
Ручир Барония
1

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

Вы пробовали пробное тестирование областей кода с помощью Android Mock Framework?

Фред Грот
источник
1
не так много, тестирование этой природы не столько вопрос , как запись того , что на самом деле происходит в то время как приложение работает, что мне действительно нужно , это / утечка памяти ресурсов профилирование инструмента
jottos