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 не убивает действия из стека, когда память низкая.
Ответы
Ответ 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 для указания меня в правильном направлении.