memory-leaks утечка - Какие инструменты и методы Android лучше всего подходят для поиска утечек памяти / ресурсов?




андроид памятью (7)

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

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

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


Answers

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

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



Ответ от @ hp.android хорошо работает, если вы просто работаете с растровыми фонами, но в моем случае у меня был BaseAdapter обеспечивающий набор ImageView для 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();
  }
}

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


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


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

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


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

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

Сначала установите атрибут «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 () вашей операции вызовите метод unbindDrawables (), передающий подтверждение родительскому представлению, а затем выполните команду 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. Удаляет дочерние элементы в каждой группе просмотра

Распространенным заблуждением является то, что Java использует память по мере ее запуска, и там для нее должна быть возможность вернуть память в ОС. На самом деле Oracle / Sun JVM резервирует виртуальную память как непрерывный блок памяти сразу же после ее запуска. Если недостаточно непрерывной виртуальной памяти, она не работает при запуске, даже если программа не собирается использовать это.

Что происходит, так это то, что ОС достаточно умна, чтобы не выделять физическую память программе до ее использования. Он не может легко восстановить память, но ее можно поменять на диск, если это необходимо, и оно не использовалось какое-то время. Java не справляется с тем, что части кучи меняются на диск очень хорошо, поэтому этого следует избегать.





android memory-leaks