Оптимізація потребування оперативної пам'яті мобільною програмою
iOS вбиває програми мовчки — без краху, без OutOfMemoryError. Користувач згорнув, відкрив, а дані зникли — програма перезапустилась. Android надсилає onLowMemory та onTrimMemory, але якщо розробник їх ігнорує — процес теж гине. Надмірне потребування пам'яті — це не тільки краші, це погіршена UX кожен день.
Де втікає пам'ять: Android
Android Memory Profiler в Android Studio — обов'язковий інструмент. Знімаємо heap dump, сортуємо за retained size, шукаємо несподівано великі об'єкти або класи, яких повинно бути кілька, а їх тисячі.
Bitmap утечки. До Glide та Coil розробники вручну декодували Bitmap та тримали в статичних полях або Map. Сьогодні ця проблема трапляється рідше, але неправильно використаний Glide може кешувати повнорозмірні зображення замість downsampled. Правило: override(width, height) у Glide-запитах для ImageView, розмір якого відомий заздалегідь. Bitmap розміром 2048x2048 для аватарки 48dp — це 16 МБ впустую.
Fragment/Activity утечки через анонімні класи. Handler, Runnable, лямбди з захватом this — класичні паттерни, які тримають посилання на Activity після її знищення. LeakCanary знаходить такі утечки автоматично у debug-сборці. Обов'язково включаємо в CI.
ViewModel з неотписаними спостерігачами. LiveData спостерігачі, привязані до LifecycleOwner, автоматично відписуються. Але прямі підписки на Flow без collectAsStateWithLifecycle або без явного Job.cancel при onDestroy створюють утечки.
RecyclerView з великими payload. ListAdapter + DiffUtil — правильний підхід, але якщо Adapter зберігає повний список у полі, а не лише поточний видимий діапазон — це лишня пам'ять. Paging 3 розв'язує це для довгих списків.
Де втікає пам'ять: iOS
Xcode Memory Graph Debugger — головний інструмент. Показує retain cycles візуально. Instruments → Allocations — для відслідковування росту пам'яти в часі.
Retain cycles у closure. [weak self] у замиканнях, які захватують self — це не перестраховка, це необхідність для будь-яких замикань, живучих довше функції. Особливо коварні ланцюжки: ViewModel → Closure → ViewController → ViewModel.
NSCache без лімітів. NSCache автоматично чистится при memory pressure, але якщо не задати countLimit та totalCostLimit — він може виросту до кількох сотень МБ до першого попередження.
Зображення в UIImageView без downsampling. UIImage(named:) кешує зображення та не звільняє його. UIImage(contentsOfFile:) не кешує, але вимагає ручного управління. Для великих зображень — ImageIO з kCGImageSourceShouldCacheImmediately = false та downsampling через CGImageSourceCreateThumbnailAtIndex.
NotificationCenter спостерігачі без removeObserver. У Obj-C це крах, у Swift (до iOS 9) теж. З iOS 9+ block-based спостерігачі самоочищаються, але selector-based ні. У Swift комбо deinit { NotificationCenter.default.removeObserver(self) } — обов'язковий для UIKit.
Flutter та React Native
У Flutter утечки пам'яті найчастіше пов'язані з StreamSubscription без cancel() та AnimationController без dispose(). Dart DevTools → Memory показує граф об'єктів. Типова ловушка: StatefulWidget створює StreamSubscription у initState, але у dispose її не відміняє — кожне пересоздання віджету додає нову підписку.
У React Native головна проблема — накопичення native об'єктів при навігації без правильного unmount. react-navigation з unmountOnBlur: true для важких екранів розв'язує частину проблем. Flipper з Memory plugin показує нативну пам'ять окремо від JS heap.
Процес оптимізації
| Етап | Інструмент | Мета |
|---|---|---|
| Baseline measurement | Android Profiler / Xcode Instruments | Зафіксувати поточне потребування |
| Heap dump analysis | MAT (Android) / Memory Graph (iOS) | Знайти retain cycles та unexpected retentions |
| Stress testing | Monkey / XCUITest | Виявити утечки при довгій роботі |
| LeakCanary / Instruments | CI integration | Не допустити регресію |
Цільові значення залежать від типу програми: простий CRUD — 50-80 МБ в нормі, медіаплеєр або карти — 150-200 МБ прийнятно.
Термін роботи: одна-три тижні: один тиждень на діагностику та профілювання, одна-два на виправлення та верифікацію.







