Обмен технологиями

Оптимизация производительности Android, оптимизация памяти

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Оптимизация производительности Android, оптимизация памяти

проблема с памятью

  • память бьется
  • утечка памяти
  • переполнение памяти

память бьется

Перегрузка памяти означает создание и уничтожение большого количества объектов за короткий период времени, что приводит к частым действиям по сборке мусора (сборке мусора, GC). Эта частая активность сборщика мусора отнимает много ресурсов ЦП и может привести к задержкам работы приложений или снижению производительности.

Производительность: кривая памяти неровная.

утечка памяти

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

переполнение памяти

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

Инструмент обнаружения

  • Профилировщик памяти
  • Анализатор памяти
  • LeakCanary

Профилировщик памяти

  • Memory Profiler — это инструмент анализа памяти, входящий в состав Android Studio.
  • График в реальном времени, показывающий использование памяти программой.
  • Выявление утечек памяти, зависаний и т. д.
  • Предоставляет возможность захвата дампов кучи, принудительного сбора мусора и отслеживания распределения памяти.

Анализатор памяти

  • Мощный инструмент анализа кучи Java для поиска утечек памяти и ее использования.
  • Создавайте общие отчеты, анализируйте проблемы и многое другое.

LeakCanary

  • Автоматическое обнаружение утечек памяти.
    • LeakCanary автоматически обнаруживает утечки в этих объектах: Activity, Fragment, View, ViewModel, Service.
  • Официальный сайт: https://github.com/square/leakcanary

Механизм управления памятью

Джава

Структура памяти Java: куча, стек виртуальной машины, область методов, счетчик программ, стек локальных методов.

Алгоритм переработки памяти Java:

  • Алгоритм маркировки и развертки:
    1. Отметьте объекты, которые необходимо переработать
    2. Утилизируйте все отмеченные объекты единообразно.
  • Алгоритм копирования:
    1. Разделите память на два куска одинакового размера.
    2. После того, как один блок памяти израсходован, уцелевшие объекты копируются в другой блок.
  • Алгоритм сортировки маркировки:
    1. Процесс маркировки аналогичен алгоритму «маркировка и развертка».
    2. Объекты выживания перемещаются в один конец.
    3. Очистите оставшуюся память.
  • Алгоритм сбора поколений:
    • Объедините преимущества нескольких алгоритмов сбора данных.
    • Выживаемость объектов нового поколения низкая и используется алгоритм копирования.
    • Выживаемость объектов старого поколения высока и используется алгоритм сортировки по меткам.

Недостатки алгоритма очистки-маркировки: Маркировка и очистка неэффективны и приводят к образованию большого количества прерывистых фрагментов памяти.

Алгоритм репликации: прост в реализации и эффективен в эксплуатации. Недостатки: Потеряна половина места.

Алгоритм компактной пометки: избегайте фрагментации памяти, вызванной очисткой пометок, и избегайте пустой траты места алгоритма копирования.

андроид

Эластичное распределение памяти Android, значение выделения и максимальное значение зависят от конкретных устройств.

Алгоритм переработки Dalvik и алгоритм переработки ART — это механизмы сбора мусора, используемые для управления памятью в операционной системе Android.

Алгоритм переработки Dalvik:

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

Алгоритм переработки предметов искусства:

  • Алгоритм уплотнения сборки мусора. На основе алгоритма разметки были внесены улучшения, позволяющие сократить время пауз во время сборки мусора.
  • Параллельная маркировка: ART вводит фазу параллельной маркировки, что означает, что она может происходить одновременно с выполнением приложения. Это сокращает время пауз из-за сборки мусора.
  • Очистка и сжатие. На этапе очистки ART не только очищает немаркированные объекты, но и сжимает память, то есть перемещает уцелевшие объекты вместе, чтобы уменьшить фрагментацию памяти. Это делает управление памятью более эффективным и снижает вероятность сбоев при выделении памяти.
  • Адаптивная сборка: ART также представляет концепцию адаптивной сборки, которая означает, что она автоматически регулирует частоту и способ сборки мусора в зависимости от поведения приложения и моделей использования памяти. Это позволяет ART лучше адаптироваться к различным требованиям приложений.

Механизм ЛМК:

  • Механизм Low Memory Killer.
  • Его основная функция — завершить некоторые фоновые процессы в соответствии с определенной политикой приоритетов, когда системной памяти недостаточно для освобождения памяти и обеспечения стабильности и быстродействия системы.

решать

Проблема с перегрузкой памяти

Код задачи моделирования
public class ShakeActivity extends AppCompatActivity {

    private static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            String str = "";
            for (int i = 0; i < 10000000; i++) {
                str += i;
            }
            mHandler.sendEmptyMessageDelayed(1, 30);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shake);
    }

    public void startClick(View view) {
        mHandler.sendEmptyMessage(1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
Используйте инструмент Memory Profiler для обнаружения

Профилировщик памяти может просмотреть распределение памяти, нажмите «Записать выделение Java/Kotlin».

Вставьте сюда описание изображения

Смысл вышесказанного:

  • Java: память, выделенная кодом Java или Kotlin.
  • Собственный: память, выделенная кодом C или C++.
  • Графика: память, используемая очередью графического буфера для отображения пикселей на экране (включая поверхности GL, текстуры GL и т. д.). Общая память процессора.
  • Стек: память, используемая собственным стеком и стеком Java в приложении. Обычно это связано с количеством потоков, выполняемых вашим приложением.
  • Память, используемая приложениями для обработки кода и ресурсов, таких как байт-код dex, оптимизированный или скомпилированный код dex, библиотеки .so и шрифты.
  • Приложение использует память, которую система не знает, как классифицировать.
  • Количество объектов Java/Kotlin, выделенных приложением. Это число не учитывает объекты, выделенные в C или C++.

Следующие значения:

  • Распределения: пройти malloc() илиnew Количество объектов, выделенных оператором.
  • Делокации: через free() илиdelete Количество объектов, освобожденных оператором.
  • Размер выделений: общий размер всех выделений за выбранный период времени в байтах.
  • Размер освобождений: общий размер памяти, освобожденной за выбранный период времени, в байтах.
  • Общее количество: распределения минус освобождения.
  • Оставшийся размер: размер выделений минус размер освобождений.
  • Небольшой размер: общий размер всех экземпляров в куче в байтах.

Анализ изображения выше:

Вставьте сюда описание изображения

Значения «Выделение» и «Освобождение» в этой области относительно схожи, а «Мелкий размер» относительно велик, что указывает на то, что объекты могут часто создаваться и уничтожаться.

Вставьте сюда описание изображения

После щелчка вы можете просмотреть информацию о стеке вызовов и в сочетании с кодом сделать вывод о проблеме дрожания памяти в обработчике.

Советы по оптимизации
  • Избегайте частого создания и уничтожения объектов.

Проблема с утечкой памяти

Код задачи моделирования
public class CallbackManager {
    public static ArrayList<Callback> sCallbacks = new ArrayList<>();

    public static void addCallback(Callback callback) {
        sCallbacks.add(callback);
    }

    public static void removeCallback(Callback callback) {
        sCallbacks.remove(callback);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class LeakActivity extends AppCompatActivity implements Callback {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak);
        ImageView imageView = findViewById(R.id.imageView);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
        imageView.setImageBitmap(bitmap);
        CallbackManager.addCallback(this);
    }

    @Override
    public void onOperate() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
Используйте инструмент LeakCanary для обнаружения

Добавьте зависимые библиотеки:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
  • 1

После того, как произойдет утечка памяти, LeakCanary сгенерирует соответствующую информацию и автоматически выгрузит ее:

Вставьте сюда описание изображения

Как видно из рисунка выше: LeakActivity имеет утечку памяти, и отображается связь цепочки ссылок.

Конечно, вы также можете создать файл hprof и просмотреть конкретную информацию с помощью инструмента Profiler:

Вставьте сюда описание изображения

Как видно из рисунка выше: произошло 10 точек утечки, включая LeakActivity. Нажмите LeakActivity, чтобы просмотреть объект утечки памяти и проверить цепочку ссылок. Видно, что он удерживается ArrayList.

Советы по оптимизации
  • Своевременная утилизация элементов сбора.
  • Избегайте статических ссылок на слишком большое количество экземпляров.
  • Используйте статические внутренние классы.
  • Немедленно закрывайте объекты ресурсов.

Оптимизация растрового изображения

Если вы не освободите ресурсы изображения после использования Bitmap, это легко вызватьУтечка памяти, приводящая к переполнению памяти

Модель растровой памяти
  • До API10 (Android 2.3.3): растровые объекты размещаются в динамической памяти, а данные пикселей — в локальной памяти.
  • После api10: все в куче памяти.
  • После api26 (Android8.0): данные пикселей помещаются в локальную память. Это позволяет быстро высвобождать пиксельные данные растрового изображения собственного слоя вместе с объектами слоя Java.

Переработка памяти:

  • До Android 3.0 вам нужно вызывать его вручнуюBitmap.recycle()Выполните переработку растрового изображения.
  • Начиная с Android 3.0, система обеспечивает более интеллектуальное управление памятью, и в большинстве случаев нет необходимости вручную перерабатывать Bitmap.

Конфигурация растрового пикселя:

КонфигурацияЗанимаемый размер байта (байт)иллюстрировать
АЛЬФА_81один прозрачный канал
РГБ_5652Простой оттенок RGB
ARGB_8888424-битный настоящий цвет
RGBA_F168Android 8.0 Новый (HDR)

Рассчитаем память, занимаемую Btimap:

  • Растровое изображение#getByteCount()
  • getWidth() * getHeight() * 1 пиксель занимает память
Каталог файлов ресурсов

Проблема с файлом ресурсов:

  • mdpi (средняя плотность): примерно 160 точек на дюйм, ресурс 1x.
  • hdpi (высокая плотность): примерно 240 точек на дюйм, ресурсы в 1,5 раза больше.
  • xhdpi (сверхвысокая плотность): примерно 320 точек на дюйм, 2 ресурса.
  • xxhdpi (сверхвысокая плотность): примерно 480 точек на дюйм, 3-кратное увеличение ресурсов.
  • xxxhdpi (сверхвысокая плотность): примерно 640 точек на дюйм, 4x ресурсов.

Тестовый код:

private void printBitmap(Bitmap bitmap, String drawable) {
    String builder = drawable +
            " Bitmap占用内存:" +
            bitmap.getByteCount() +
            " width:" +
            bitmap.getWidth() +
            " height:" +
            bitmap.getHeight() +
            " 1像素占用大小:" +
            getByteBy1px(bitmap.getConfig());
    Log.e("TAG", builder);
}

private int getByteBy1px(Bitmap.Config config) {
    if (Bitmap.Config.ALPHA_8.equals(config)) {
        return 1;
    } else if (Bitmap.Config.RGB_565.equals(config)) {
        return 2;
    } else if (Bitmap.Config.ARGB_8888.equals(config)) {
        return 4;
    }
    return 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
// 逻辑密度
float density = metrics.density;
// 物理密度
int densityDpi = metrics.densityDpi;
Log.e("TAG", density + "-" + densityDpi);

// 1倍图
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.splash1);
printBitmap(bitmap1, "drawable-mdpi");

// 2倍图
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.splash2);
printBitmap(bitmap2, "drawable-xhdpi");

// 3倍图
Bitmap bitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.splash3);
printBitmap(bitmap3, "drawable-xxhdpi");

// 4倍图
Bitmap bitmap4 = BitmapFactory.decodeResource(getResources(), R.drawable.splash4);
printBitmap(bitmap4, "drawable-xxxhdpi");

// drawable
Bitmap bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
printBitmap(bitmap5, "drawable");
/*
        3.0-480
        drawable-mdpi Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
        drawable-xhdpi Bitmap占用内存:9281844 width:1287 height:1803 1像素占用大小:4
        drawable-xxhdpi Bitmap占用内存:4125264 width:858 height:1202 1像素占用大小:4
        drawable-xxxhdpi Bitmap占用内存:2323552 width:644 height:902 1像素占用大小:4
        drawable Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
         */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

проиллюстрировать:

1dp на устройствах mdpi1px, 1dp на устройствах xhdpi2px, 1dp==3px на устройстве xxhdpi.

Следовательно, текущее устройство - xxhdpi, поэтому ширина той же картинки под ресурсом xxhdpi равна 858, под ресурсом mdpi она будет увеличена в 3 раза и ширина равна 2574, а под ресурсом xhdpi она будет увеличена в 1,5 раза и ширина 1287.

Советы по оптимизации
  • Настройте несколько наборов ресурсов изображений.
  • Выберите подходящий метод декодирования.
  • Настройте кэш изображений.

Загрузка исходного кода