Optimizing mobile app RAM consumption
Mobile apps often terminate due to lack of memory. iOS silently unloads apps, user data is lost, the session is reset. Android generates onLowMemory, but if you don't react, the process dies. We encounter this daily and know how to turn a resource-hungry APK into a frugal app.
Over 7 years, we have audited the memory of more than 20 projects — from simple CRUD to complex media players. We reduce RAM consumption by 30–50% without changing functionality. Order an audit — get specific recommendations and a ready-made optimization plan.
How to find a memory leak on Android?
Android Memory Profiler in Android Studio is an essential tool. We take a heap dump, sort by retained size, look for unexpectedly large objects or classes that should be few but number in the thousands.
LeakCanary automatically finds leaks in debug builds — we enable it in CI and get notifications for every build. It detects leaks 5 times faster than manual analysis.
Bitmap is the main memory hog. Even with Glide or Coil, it's important to specify the size: override(width, height) for ImageView. A full-size 2048×2048 for a 48dp avatar is 16 MB wasted. Use downsampling: Glide.with(this).load(url).override(100, 100).into(imageView).
Fragment/Activity — a classic case via anonymous classes. Handler, Runnable, lambdas capturing this hold a reference after destruction. LeakCanary shows the exact chain.
ViewModel with unsubscribed Flow. collectAsStateWithLifecycle solves the problem. Without it — Job.cancel() in onDestroy.
RecyclerView — use Paging 3. Adapter does not hold the full list in memory, loads it in portions.
Why does iOS kill the app?
Xcode Memory Graph Debugger visualizes retain cycles. Instruments → Allocations tracks memory growth. Main causes:
- Retain cycles in closures.
[weak self]is a necessity for closures that live longer than the function. Chains are especially insidious: ViewModel → Closure → ViewController → ViewModel. - NSCache without limits. Set
countLimitandtotalCostLimit, otherwise the cache will grow to hundreds of MB. - Images — do not use
UIImage(named:)for large images.UIImage(contentsOfFile:)does not cache. For downsampling, useImageIOwithkCGImageSourceShouldCacheImmediately = false. - NotificationCenter —
removeObserverin deinit is mandatory. Since iOS 9+, block-based observers self-clean, but selector-based ones do not.
What to do about Flutter and React Native?
Flutter: leaks via StreamSubscription without cancel() and AnimationController without dispose(). Dart DevTools → Memory shows the object graph. Each recreation of a widget adds a new subscription.
React Native: navigation with react-navigation — enable unmountOnBlur: true for heavy screens. Flipper with the Memory plugin analyzes native memory separately from the JS heap.
How is a memory audit performed? Step by step
- Meeting and data collection. We learn the architecture, usage scenarios, typical user actions. We agree on test devices.
- Baseline profiling. We run Android Profiler / Xcode Instruments on a "clean" app — we record the numbers.
- Stress test. We simulate long-term use: open the screen 10 times, scroll through lists, load images. We take a heap dump at peak.
- Analysis. We use MAT (Android) or Memory Graph Debugger (iOS) to find retain cycles, excessive caches, bitmaps. We compile a report with 20+ metrics.
- Fixes. We fix the code, optimize caches, add downsampling. In CI, we set up automatic regression checks.
The process takes from 3 business days (diagnostics) to 3 weeks (full optimization). Get a consultation — we will evaluate your project.
Optimization process
| Stage | Tool | Goal |
|---|---|---|
| Baseline consumption measurement | Android Profiler / Xcode Instruments | Record current numbers |
| Heap dump analysis | MAT (Android) / Memory Graph (iOS) | Find retain cycles and unexpected retentions |
| Stress testing | Monkey / XCUITest | Identify leaks during long-running operation |
| CI integration | LeakCanary / Instruments | Prevent regression |
Target values: simple CRUD — 50-80 MB, media player — 150-200 MB.
Table: comparison of tools
| Tool | Platform | Automation | Analysis speed |
|---|---|---|---|
| LeakCanary | Android | + | Instantly |
| Memory Graph Debugger | iOS | - (manual) | Seconds |
| Instruments | iOS | + (templates) | Minutes |
| Dart DevTools | Flutter | + | Real-time |
What is included in the work
- Diagnostics: profiling on real devices, heap dump capture, report with 20+ metrics.
- Code fixes: refactoring retain cycles, cache optimization, image downsampling.
- CI setup: LeakCanary integration, automatic alerts on regression.
- Team training: walkthrough of the issues found, best practices checklist.
- Guarantee: agreed scope and deadlines, post-audit a month after implementation.
Timeline and Cost
Diagnostics — from 3 business days. Full optimization — 1-3 weeks depending on the volume of code. The cost of full optimization is calculated individually after the audit, depending on complexity. Savings on support and infrastructure can be substantial. Contact us to assess your project.
For reference: Apple Memory Management and Android Memory Overview contain detailed recommendations.







