Testing RAM consumption of a mobile application

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Testing RAM consumption of a mobile application
Medium
~2-3 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1050
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Memory Consumption Testing for Mobile Applications

App doesn't crash immediately — it gradually grows in memory. After 20 minutes of use slight sluggishness appears. After 40 — system Memory Pressure kills background processes. After hour — SIGKILL from iOS or OOM-killer Android terminates app. User thinks app glitches. Firebase Crashlytics shows nothing — not a crash, it's kill by system.

iOS: Instruments Allocations and Leaks

Two templates for memory analysis in Instruments:

Allocations — all memory allocations, live and dead objects. Generations (Generation button) allow comparing objects before and after action. If action screen objects remain in live memory after screen closed — leak.

Leaks — automatic retain-cycle detector. Red icon = found cycle. Shows dependency graph with culprits.

Classic retain-cycle in Swift:

// Leak: ViewController holds closure, closure captures ViewController
class PhotoViewController: UIViewController {
  var onPhotoLoaded: (() -> Void)?

  override func viewDidLoad() {
    super.viewDidLoad()
    onPhotoLoaded = {
      self.imageView.image = UIImage(named: "photo") // strong capture
    }
  }
}

// Correct:
onPhotoLoaded = { [weak self] in
  self?.imageView.image = UIImage(named: "photo")
}

[weak self] — standard for any closure capturing self in long-lived objects. Instruments Leaks will find it, but often shows symptom, not cause. Follow graph path in stack, find root strong reference.

UIImage and Memory

UIImage(named:) caches image in system cache. Good for often-used icons. Bad for large photos loaded once. Use UIImage(contentsOfFile:) — doesn't cache.

Image decoding happens at first display, not at UIImage creation. Pre-decode on 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
}

After this call image already decoded and lies in memory as bitmap. Pass to UI without decode delay.

Android: Memory Profiler and LeakCanary

Android Studio Memory Profiler shows Heap in real time: Java Heap, Native Heap, Stack, Code, Graphics. Dump Heap button saves snapshot — analyze in hprof viewer or convert for Eclipse Memory Analyzer.

But most useful tool in battle — LeakCanary. Plugs into debugImplementation, works automatically:

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

When leak found LeakCanary shows notification with full stack: what holds what, through what chain. No manual heap-dump analysis needed.

Common leak causes on Android:

Context in static fields or singletons:

// Bad
object ImageCache {
  var context: Context? = null  // holds Activity
}

// Right: applicationContext
object ImageCache {
  lateinit var appContext: Context

  fun init(context: Context) {
    appContext = context.applicationContext  // not Activity
  }
}

Unclosed Cursor from ContentProvider or SQLiteDatabase:

val cursor = db.query(...)
try {
  // work with cursor
} finally {
  cursor.close()  // mandatory, even on exception
}

Listener not removed on onDestroy:

override fun onStart() {
  super.onStart()
  locationManager.requestLocationUpdates(provider, 0, 0f, this)
}

override fun onStop() {
  super.onStop()
  locationManager.removeUpdates(this)  // otherwise Activity won't die
}

Flutter: Observatory and DevTools Memory

Flutter DevTools → Memory tab — snapshot profiler. Shows object groups by type. Dart:core, package:myapp — look at classes with unexpected large instance counts.

Typical leak in Flutter — StreamSubscription without 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();  // mandatory
    super.dispose();
  }
}

Without _sub.cancel() in dispose() subscription lives longer than widget, holds closure with State reference.

What's Included

  • Memory profiling via Instruments Allocations / Memory Profiler / DevTools
  • LeakCanary setup for Android project
  • Heap-dump analysis and retain-cycle search
  • Image handling audit (cache, decoding)
  • Pattern check for listeners, subscriptions, closures
  • Report with specific leaks and fixes

Timeline

2–3 days depending on app size and found problems count. Cost is calculated individually.