agentsclimarketplace

Memory profiling

Skill almasumdev/awesome-mobile-performance-agent-skills/.github/skills/memory/memory-profiling

Agent skills for profiling and optimizing mobile app performance (startup, memory, frame-rate, network).

Install
npx -y skills add almasumdev/awesome-mobile-performance-agent-skills --skill memory-profiling

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Capture and analyze heap dumps on Android (Android Studio Profiler, LeakCanary) and iOS (Instruments Allocations/Leaks). Use when memory-related crashes or growth are suspected.

SKILL.md

5.5 KB, as published. Nobody here has run it

Memory Profiling

Instructions

Memory problems on mobile manifest as low-memory kills, OutOfMemoryError, thermal throttling, or visible slowdowns after prolonged use. Profile before changing anything.

1. Budgets and Signals

PlatformForeground budget (p50 device)Kill threshold
iOS≤ 200 MB resident (Xcode "Memory")~1.3 GB hard limit on 2 GB devices
Android≤ 250 MB PSS foregroundonTrimMemory(TRIM_MEMORY_CRITICAL) events

Key signals to gather:

  • Peak memory on cold launch and on the heaviest screen.
  • Retained memory after navigating away (should return to baseline ± 5 MB).
  • onTrimMemory events (Android) and didReceiveMemoryWarning (iOS) frequency.
  • Production: MetricKit MXMemoryMetric and Firebase Performance memory traces.

2. Android — Android Studio Profiler

  1. Android Studio → Profiler → select the device and process.
  2. Click the Memory row. You see a live chart of Java, Native, Graphics, Stack, Code, and Other.
  3. Force GC (trash icon) before capturing. Otherwise garbage skews the picture.
  4. Click Dump Java heap to capture a .hprof.
  5. Filter by Allocated in range to see only objects created during a specific action.
  6. Right-click a class → Go to Instance → inspect References tab for retention path.

Look for:

  • Large bitmap retention under android.graphics.Bitmap (see bitmap-and-image-optimization).
  • Duplicate Activity instances — classic leak signal.
  • Unbounded HashMap/ArrayList in singletons.

3. Android — LeakCanary

Add in debug:

dependencies {
    debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
}

LeakCanary watches for retained Activity, Fragment, View, and ViewModel. When one fails to be collected after the expected lifecycle event, it dumps the heap and prints a leak trace:

GC Root: Thread object
...
* leaks com.example.FeedActivity instance

The offending reference chain pinpoints the fix.

4. iOS — Instruments Allocations + Leaks

  1. Product → Profile → Leaks template (bundles Allocations).
  2. Run the interaction that you suspect grows memory.
  3. Use Mark Generation before and after the interaction. A generation is the set of objects allocated in that window. If the generation's persistent bytes > 0 after the action unwinds, you have retained objects.
  4. Leaks track shows cycle retention detected automatically.
  5. For ARC retain cycles that Leaks does not detect (e.g., DispatchQueue holding a closure), inspect the generation by class and look for types that should have been deallocated.
class FeedViewModel {
    var onUpdate: (() -> Void)?

    func subscribe() {
        Store.shared.observe { [weak self] in   // NOT self. strong = cycle
            self?.onUpdate?()
        }
    }
}

5. Flutter — DevTools Memory View

  • Run flutter run --profile.
  • DevTools → Memory → click GCTake heap snapshot.
  • Use Diff Snapshots between two navigation round-trips. Any class whose instance count grew and did not return to zero is leaking.
  • Filter by runtimeType of your widgets — a State whose count keeps growing is a leak.

Tool: leak_tracker package for CI.

void main() {
  LeakTracking.start();
  runApp(const MyApp());
}

6. React Native — Chrome / Hermes Heap Snapshot

Hermes supports .heapsnapshot via:

import { DevSettings, NativeModules } from 'react-native';
NativeModules.HermesExecutorFactory.takeHeapSnapshot('/sdcard/app.heapsnapshot');

Open in Chrome DevTools → Memory → Load. Compare two snapshots ("Comparison" mode). Look at:

  • Closures (compiled functions) retaining large scopes.
  • Event emitter listeners that were added but never removed.

7. Reading a Retention Path

The fix pattern is always the same: the root is GC-reachable (thread, static, subscription), and it references your object through some chain. Break the chain closest to the leaf:

  • If a singleton holds the leaked object → clear the reference on onDestroy / deinit.
  • If a listener list → unsubscribe in onDispose / deinit / dispose.
  • If a thread / coroutine → cancel on scope exit (viewModelScope, Task { }.cancel(), StreamSubscription.cancel).

8. Native Memory on Android

Java heap is only part of the story. dumpsys meminfo and Android Studio's Native Memory track cover NDK libraries, Skia buffers, MediaCodec.

adb shell dumpsys meminfo com.example.app

Watch the Graphics row — large values usually mean retained Surfaces, textures, or video decoders.

Checklist

  • Baseline memory captured in release/profile on a p50 device.
  • Heap snapshot taken before and after a suspect navigation flow.
  • Retention paths traced to a specific owner and documented in PR.
  • LeakCanary (Android) wired in debug builds; crashes pager for retained Activity/Fragment.
  • iOS generation diff shows no unexpected persistent objects after screen teardown.
  • Flutter leak_tracker integrated in integration tests.
  • MetricKit / Firebase Performance memory traces feeding a dashboard.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.