Утечки памяти в Android-приложении

У меня есть некоторые утечки памяти в моем приложении. В первый раз я заподозрил утечку памяти, когда провел стресс-тестирование своего приложения, нажав кнопку, чтобы запустить новый Activity. После использования DDMS и вывода .hprof file этот файл я открыл с помощью Eclipse Memory Analyzer

У меня есть три возможных утечки памяти, показанные на круговой диаграмме: введите здесь описание изображения

И описание к утечкам памяти:

Подозреваемый 1

122 экземпляра "android.widget.LinearLayout", загруженные "" занимают 29 585 384 (38,74%) байт.

Крупнейшие экземпляры:

•android.widget.LinearLayout @ 0x4258c008 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x425c8688 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x425e3988 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x4296e198 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x429d3aa8 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x42a10c78 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x448a1f10 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x44a65d58 - 2 268 848 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x42a14098 - 2 268 824 (2,97%) bytes. 
•android.widget.LinearLayout @ 0x4258bd30 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x425c83b0 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x425ddff8 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x4296df80 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x42a109a0 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x42a13dc0 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x448a1c38 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x448cc338 - 999 528 (1,31%) bytes. 
•android.widget.LinearLayout @ 0x44a65a80 - 999 528 (1,31%) bytes. 

Подозреваемый 2

15 экземпляров "android.widget.FrameLayout", загруженных "" занимают 29 405 016 (38,51%) байт.

Крупнейшие экземпляры:

•android.widget.FrameLayout @ 0x4245b490 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x4247a330 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x425aa1d8 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x425df8b0 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x425efe68 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x42627590 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x42987a70 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x4299df20 - 3 266 728 (4,28%) bytes. 
•android.widget.FrameLayout @ 0x448b6f28 - 3 266 728 (4,28%) bytes. 

Подозреваемый 3

2 682 instances of "java.lang.Class", loaded by "<system class loader>" occupy 8 662 744 (11,34%) bytes. 

Biggest instances:

•class android.content.res.Resources @ 0x41a4f708 - 7 485 176 (9,80%) bytes. 

Моей первой мыслью было заглянуть в файл R.java, потому что я мог видеть некоторые шестнадцатеричные ссылки на возможную утечку памяти. Я попытался найти шестнадцатеричную строку из Eclipse Memory Analyzer, но не смог найти адреса в файле R.java.

Затем я заглянул в дерево доминаторов, и вот результат: введите здесь описание изображения

Это только первая запись в списке, но она самая большая.

С помощью этой информации кто-нибудь может подсказать, как я могу отследить утечку памяти? Добавьте комментарий, если мне нужно добавить дополнительную информацию к этому сообщению.

Заранее спасибо.

ИЗМЕНИТЬ

Проблема может быть в моем базовом классе. Все действия наследуются от этого класса. Работа этого класса заключается в настройке SlidingMenu в левом верхнем углу. Это код для этого класса:

 public class Base extends Activity implements OnSlideMenuItemClickListener {

        public SlideMenu slidemenu;
        ImageButton b;
        Time t; 

        BluetoothCommunicator btCom; 
        BroadcastReceiver btBroadCaster;
        MonitorBluetoothState bluetoothState;


        public void setTab(int id) {
            setContentView(id);
            overridePendingTransition(R.anim.activityfade, R.anim.activityfadeout);
            slidemenu = (SlideMenu) findViewById(R.id.slideMenu);
            slidemenu.init(this, R.menu.slide, this, 450);
            slidemenu.setHeaderImage(getResources().getDrawable(R.drawable.ic_launcher));


            b = (ImageButton) findViewById(R.id.BtnSlide);
            b.setOnClickListener(new OnClickListener() {

                public void onClick(View v) {
                    slidemenu.show();

                }   
            });

            b.setOnTouchListener(new OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {

                    switch (event.getAction()) {

                    case MotionEvent.ACTION_DOWN:
                        b.setImageResource(R.drawable.lincolor);
                        break;
                    case MotionEvent.ACTION_UP:
                        b.setImageResource(R.drawable.lin);
                        break;
                    }
                    return false;
                }
            });
        }

        @Override
        public void onSlideMenuItemClick(int itemId) {

            Class<?> cls = null; 

            switch(itemId) {
            case R.id.item_one:
                cls = Home.class;
                break;
            case R.id.item_two:
                cls = History.class;
                break;
            case R.id.item_three:
                cls = ClearHistoryDialog.class;
                break;
            case R.id.item_four:
                cls = SendLogDialog.class;
                break;
            case R.id.item_five:
                cls = PasswordDialog.class;
                break;
            case R.id.item_six:
                cls = About.class; 
                break;
            }
            Intent intent = new Intent(this, cls); 
            startActivity(intent); 
        }
    }

В других моих действиях этот метод setTab будет вызываться так:

    public class Main extends Base {
        public void onCreate(Bundle b) {
          super.onCreate(b);
          super.setTab(R.layout.Home);
        }
    }

Где Home Layout выглядит следующим образом:

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

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/first"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="0px" >

        <include
            android:id="@+id/tabBar"
            layout="@layout/tab" />

        <com.workspace.tobias
            android:id="@+id/slideMenu"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <Button
            android:id="@+id/nist"
            android:layout_width="match_parent"
            android:layout_height="67dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="3dp"
            android:layout_marginLeft="3dp"
            android:layout_marginRight="3dp"
            android:layout_marginTop="3dp"
            android:background="@drawable/ready"
            android:textColor="#FFFFFF" />

        <ListView
            android:id="@+id/lastCases"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
    </LinearLayout>


    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/loading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="300dp"
        android:orientation="horizontal" >

        <ProgressBar
            android:id="@+id/progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:indeterminate="true" />

        <TextView
            android:id="@+id/loadingCases"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="@string/Loading"
            android:textColor="#707070"
            android:textSize="18dp"
            android:layout_marginLeft="10dp" />
    </LinearLayout>

</RelativeLayout>

person Tobias Moe Thorstensen    schedule 21.11.2012    source источник
comment
Ресурсы Android, которые вы там видите (предположительно 3/дерево доминаторов), не являются проблемой — они существуют в каждом приложении. Кадровые/линейные макеты размером 30 МБ могут стать проблемой.   -  person zapl    schedule 21.11.2012
comment
У кого есть ссылки на ваш макет, они должны их опубликовать. Скорее всего, вы храните ссылку на контекст, который является основной причиной утечек памяти. Может быть, действие или внутренний класс действия сохраняется как значение статического поля?   -  person Snicolas    schedule 21.11.2012
comment
@Snicolas Не могли бы вы взглянуть на мою правку? Спасибо!   -  person Tobias Moe Thorstensen    schedule 21.11.2012
comment
Я думаю, вам нужно опубликовать еще немного кода. вы используете фрагменты? просто деятельность? обработчики?   -  person Ovidiu Latcu    schedule 21.11.2012
comment
адреса находятся в памяти. Идентификаторы ссылок на файлы R   -  person njzk2    schedule 21.11.2012
comment
вам нужно будет опубликовать больше кода из вашей основной и домашней активности. кто-то где-то сохраняет контекст   -  person njzk2    schedule 21.11.2012
comment
@Тобиас Мо Торстенсен. Попробуйте получить в свои руки объект, который содержит ссылки (входящие ссылки, я верю в MAT ??) на объекты ваших макетов. Проблема в том, что я в этом уверен. Я не вижу ничего плохого в обновленном коде базового класса или подклассов.   -  person Snicolas    schedule 21.11.2012
comment
@TobiasMoeThorstensen, вы решили эту проблему, потому что я столкнулся с той же проблемой   -  person N Jay    schedule 19.09.2013
comment
@NaderAyyad Да, я решил эту проблему, главным образом, не сохраняя ссылки на контекст. Я предлагаю вам попробовать установить их на null после того, как вы закончите их использовать.   -  person Tobias Moe Thorstensen    schedule 20.09.2013


Ответы (1)


Используете ли вы статические переменные для кэширования больших ресурсов?
Если да, то распространенной ошибкой в ​​приложениях для Android является использование контекста активности для загрузки таких ресурсов. Это приводит к сохранению действий и всех их ресурсов, даже если они больше не нужны.
Чтобы исправить это, используйте контекст приложения для загрузки таких ресурсов!

Подробную информацию можно найти в этом сообщении в блоге разработчиков Android.

person Marek R    schedule 21.11.2012
comment
Что вы определяете как Большие ресурсы? Bitmaps? Я использую getApplicationContext() при загрузке растровых изображений из SQLite database. И моя ссылка на контекст Activity при загрузке строки из файла string.xml. Лучше всего всегда использовать getApplicationContext() ? - person Tobias Moe Thorstensen; 21.11.2012
comment
Нет, ключевое слово здесь статично. Вы не должны хранить статические ссылки на Действия, Контексты, ресурсы. - person Snicolas; 21.11.2012
comment
@TobiasMoeThorstensen: так в чем именно была проблема? - person Vit Khudenko; 22.11.2012
comment
@Arhimed Есть много проблем, но я принимаю этот ответ, потому что он дал мне некоторые подсказки относительно разницы между Application Context и Activity Context. Также я использовал статический внутренний класс для обработки обновлений пользовательского интерфейса из потока. В настоящее время я пытаюсь исправить проблемы с контекстом, как мы говорим. - person Tobias Moe Thorstensen; 22.11.2012