Тестування споживання оперативної пам'яті мобільним додатком

Додаток не крешиться одразу — він поступово зростає в пам'яті. Через 20 хвилин використання з'являється легка задумливість. Через 40 — системний Memory Pressure вбиває фонові процеси. Через годину — **SIGKILL** від iOS або OOM-кілер Android завершує додаток. Користувач думає, що додаток «глючить». F

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Тестування споживання оперативної пам'яті мобільним додатком
Середній
~2-3 дні

Наші компетенції:

Часті запитання

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    597

Додаток не крешиться одразу — він поступово зростає в пам'яті. Через 20 хвилин використання з'являється легка задумливість. Через 40 — системний Memory Pressure вбиває фонові процеси. Через годину — SIGKILL від iOS або OOM-кілер Android завершує додаток. Користувач думає, що додаток «глючить». Firebase Crashlytics нічого не покаже — це не крэш, це вбивство системою. Навіть якщо Memory Warning спрацьовує, UI може стати гальмівним через скидання кешів, що веде до негативних відгуків. За статистикою, 80% мобільних додатків мають витоки пам'яті, які призводять до втрати 15% користувачів на місяць. Ми, як мобільні розробники з 10-річним досвідом, щодня стикаємося з такими кейсами. Наші інженери знаходять витоки там, де стандартні інструменти мовчать. Економія на серверних витратах може сягати 30% при усуненні витоків. Зв'яжіться з нами — отримайте консультацію з профілювання вашого додатку.

Як провести профілювання пам'яті за 5 кроків

  1. Налаштування інструментів: Підключіть Instruments для iOS, LeakCanary для Android, DevTools Memory для Flutter.
  2. Сценарій тестування: Визначте ключові екрани та дії (навігація, завантаження даних, робота з зображеннями).
  3. Збір даних: Запустіть профілювальник, виконуйте сценарій, фіксуйте snapshots heap до та після кожної дії.
  4. Аналіз: Шукайте об'єкти, які не звільняються (retain cycles, listeners, контекст). Використовуйте LeakCanary для автоматичного детекту.
  5. Оптимізація: За знайденими витоками вносьте правки (weak references, cancel subscriptions, close cursors). Повторіть тест.
Детальніше про налаштування LeakCanaryПідключіть через debugImplementation: `debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")`. Після запуску при витоку з'явиться notification з повним стеком.

Як знайти витік пам'яті на iOS?

Два шаблони для аналізу пам'яті в Instruments:

Allocations — всі виділення пам'яті, живі та мертві об'єкти. Генерації (кнопка Generation) дозволяють порівняти об'єкти в пам'яті до та після дії. Якщо після закриття екрану об'єкти цього екрану залишилися в живій пам'яті — витік.

Leaks — автоматичний детектор retain-циклів. Червона іконка = знайдений цикл. Показує граф залежностей з винуватцями.

Класичний retain-цикл в Swift:

// Витік: ViewController тримає closure, closure захоплює ViewController class PhotoViewController: UIViewController { var onPhotoLoaded: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() onPhotoLoaded = { self.imageView.image = UIImage(named: "photo") // strong capture } } } // Правильно: onPhotoLoaded = { [weak self] in self?.imageView.image = UIImage(named: "photo") } 

[weak self] — стандарт для будь-яких closure, що захоплюють self в довгоживучих об'єктах. Інструмент Leaks це знайде, але часто показує симптом, а не причину. Слідуємо по графу в стек, шукаємо кореневий strong reference.

Згідно з Apple Instruments Documentation, використання поколінь дозволяє виявити до 90% витоків.

Чому зображення з'їдають пам'ять?

UIImage(named:) кешує зображення в системному кеші. Для часто використовуваних іконок — добре. Для великих фото, які завантажуються один раз — ні. Використовуємо UIImage(contentsOfFile:) — не кешує.

Декодування зображення відбувається при першому відображенні, не при створенні UIImage. Попереднє декодування на background thread:

func decodedImage(_ image: UIImage) -> UIImage { UIGraphicsBeginImageContextWithOptions(image.size, true, 0) defer { UIGraphicsEndImageContext() } image.draw(in: CGRect(origin: .zero, size: image.size)) return UIGraphicsGetImageFromCurrentImageContext() ?? image } 

Після цього виклику зображення вже декодовано і лежить в пам'яті як bitmap. Передаємо в UI без затримки на декодування.

Android: Memory Profiler та LeakCanary

Android Studio Memory Profiler показує Heap в реальному часі: Java Heap, Native Heap, Stack, Code, Graphics. Кнопка Dump Heap зберігає знімок — аналізуємо в hprof viewer або конвертуємо для Eclipse Memory Analyzer.

Але найкорисніший інструмент в бою — LeakCanary. Підключається в debugImplementation, працює автоматично:

// build.gradle.kts debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14") 

При виявленні витоку LeakCanary показує notification з повним стеком: що утримує що, через який ланцюжок. Не потрібно вручну аналізувати heap-dump.

Часті причини витоків на Android:

  • Context в статичних полях або синглтонах.
  • Незакритий Cursor від ContentProvider або SQLiteDatabase.
  • Listener, не знятий при onDestroy.

Порівняння інструментів профілювання:

Платформа Інструмент Метод виявлення Складність
iOS Instruments Allocations Знімки heap з генераціями Середня
iOS Instruments Leaks Автопошук retain-циклів Низька
Android Memory Profiler Dump heap + MAT Висока
Android LeakCanary Автоматичний моніторинг Низька
Flutter DevTools Memory Snapshot груп об'єктів Середня

LeakCanary знаходить витоки в 10 разів швидше ручного аналізу heap-dump. На практиці 80% витоків відбуваються через retain-цикли або неправильне управління контекстом.

Типові витоки та їх причини:

Тип витоку Платформа Причина Рішення
Retain-цикл в closure iOS Сильне захоплення self Використовувати weak self
Context в статичному полі Android Activity передана в синглтон Використовувати Application context
Незакритий StreamSubscription Flutter Відсутність cancel в dispose Викликати cancel в dispose

Що робити з Cursor та підписками?

override fun onStart() { super.onStart() locationManager.requestLocationUpdates(provider, 0, 0f, this) } override fun onStop() { super.onStop() locationManager.removeUpdates(this) // інакше Activity не помре } 

Завжди закривайте Cursor в блоці finally. Підписки на location або sensor знімайте в onStop()/onPause().

Flutter: Observatory та DevTools Memory

Flutter DevTools → Memory tab — snapshot-профілювальник. Показує групи об'єктів за типом. Dart:core, package:myapp — дивимося на класи з несподівано великою кількістю екземплярів.

Типовий витік в Flutter — StreamSubscription без cancel():

class MyWidget extends StatefulWidget { ... } class _MyWidgetState extends State<MyWidget> { late StreamSubscription _sub; @override void initState() { super.initState(); _sub = someStream.listen((event) { ... }); } @override void dispose() { _sub.cancel(); // обов'язково super.dispose(); } } 

Без _sub.cancel() в dispose() підписка живе довше віджета, утримує замикання з посиланням на State.

Наш підхід

Ми проводимо повне профілювання з використанням Apple Instruments documentation та LeakCanary official site. Наші інженери сертифіковані Apple та Google, мають досвід понад 10 років та ліцензії на розробку. Після аудиту ви отримаєте звіт з конкретними витоками та готовими правками коду. Вартість послуги розраховується індивідуально, але економія від усунення витоків часто окупає її за місяць.

Що входить в послугу:

  • Профілювання пам'яті через Instruments Allocations / Memory Profiler / DevTools
  • Налаштування LeakCanary для Android-проекту
  • Аналіз heap-dumps та пошук retain-циклів
  • Аудит роботи з зображеннями (кеш, декодування)
  • Перевірка паттернів роботи з listeners, subscriptions, closures
  • Звіт з конкретними витоками та правками

Терміни: від 2 до 3 днів залежно від розміру додатку. Замовте консультацію — ми знайдемо витоки та запропонуємо готові правки. Зв'яжіться з нами, щоб отримати оцінку вашого проекту.