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

Clueless О (возможно) утечке памяти Android

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

Для тестирования и устранения ошибок Ive переключался между двумя действиями (позволяет называть их Main и List) с помощью моего навигационного ящика (не используя кнопку "Назад" ). В DDMS я могу видеть, что выделенная память увеличивается каждый раз до 180 КБ.

Ive сделал дампы памяти и использовал Eclipse MAT для анализа 3 разных момента времени:

Screen1

Screen2

Screen3

Я подозреваю, что происходит утечка памяти, но я не могу найти причину. Согласно дампам памяти, это похоже на то, что "Остальные" и java.lang.FinalizerReference продолжают расти. Пользователь в этом вопросе также имеет много FinalizerReferences в своем дампе памяти, но ответ не совсем ясен.

Отчет о подозрениях на утечку, который я сделал в последний момент времени, не очень полезен, поскольку он подозревает android.content.res.Resources и android.graphics.Bitmap, которые, похоже, со временем не растут:

Screen3LeakReport

В одном из отчетов (к сожалению, здесь нет) я видел, как 13 экземпляров android.widget.ListView указывали на потенциальную вероятность утечки.

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

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

EDIT:

  • Растровые изображения (@OrhanC1): Я прокомментировал любые Bitmap экземпляры в двух действиях, упомянутых выше, и память все еще увеличивается. Дамп памяти по-прежнему показывает некоторые растровые изображения, но я считаю, что они связаны с ресурсами, а не с фактическими растровыми изображениями, выделенными мной.

  • Что касается пользовательских шрифтов (@erakitin): я использую их, но я сохраняю один экземпляр каждого Typeface в моем Application контексте (public class MyApp extends Application) с использованием одноэлементного. Я пробовал комментировать любые ссылки на шрифты в двух упомянутых выше действиях, и память все еще увеличивается.

  • Я не думаю, что я утечка Context (@DigCamara): у меня нет никаких статических ссылок внутри этих двух действий, я использую контекст Application вместо Activity кроме адаптера. Если я остаюсь в том же Activity и выполняю некоторые вращения экрана, память не увеличивается.

  • На основе комментария @NickT: я вижу, что у меня есть много примеров обоих действий. Могла ли эта память увеличиваться только в результате увеличения количества действий заднего стека, а не утечки памяти (я, хотя ОС справился с этим, по-видимому, не)? Если я использую флаг намерения FLAG_ACTIVITY_REORDER_TO_FRONT, тогда память увеличивается только до тех пор, пока все различные действия не будут созданы (один раз). Полезно для этого: Android не убивает действия из стека, когда память низкая.

4b9b3361

Ответ 1

Похоже, причина, по которой растет Remainder, - это рост числа экземпляров Activity в обратном стеке (например, 13 экземпляров "List" Activity + 13 экземпляров "Main" Activity).

Я изменил свой навигационный ящик таким образом, что, когда пользователь нажимает кнопку "Главная" (что приводит его к панели "App" ), я устанавливаю флаги Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK: активность повторно используется, а задний стек (в соответствии с рекомендациями Android на самом деле). Фактически, я должен был сделать это уже, так как я не хочу создавать несколько экземпляров деятельности Dashboard ( "Главная" ).

Посредством этого я вижу, что действия уничтожены, а выделенный размер кучи (включая фрагмент Remaining) уменьшается:

Fixing problem with increasing memory.

Зафиксировав это, я также заметил, что один из моих действий и битмап, используемых им, не уничтожаются, даже если задний стек был очищен (утечка). После анализа с помощью MAT я пришел к выводу, что источником этой подзадачи была ссылка на ImageView, которую я сохранил в Activity. Добавив этот код к методу onStop(), мне удалось уничтожить как активность, так и Bitmap:

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

    ImageView myImage = (ImageView) findViewById(R.id.myImage );
    if(myImage .getDrawable() != null)
        myImage.getDrawable().setCallback(null);

    RoundedImageView roundImage = (RoundedImageView) findViewById(R.id.roundImage); // a custom View
    if(roundImage.getDrawable() != null)
        roundImage.getDrawable().setCallback(null);


}

Затем я обобщил все мои Activity и FragmentActivity, чтобы они вызывали unbindDrawables(View view) в onDestroy():

private void unbindDrawables(View view)
{
    if (view.getBackground() != null)
    {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup && !(view instanceof AdapterView))
    {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
        {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}

Благодаря @NickT для указания меня в правильном направлении.