react-native-memory-footprint
v0.3.0
Published
Get memory footprint of your app
Maintainers
Readme
react-native-memory-footprint
A tiny React Native library for reading how much memory your app is really using, on iOS and Android.
This library reports each platform's own definition of "real" usage:
- iOS:
phys_footprint, the same number shown in Xcode's memory gauge, and what Jetsam uses to decide whether to kill your app - Android: anonymous RSS + swap, the same metric Google's own Play Vitals uses to flag apps for excessive memory usage
Installation
npm install react-native-memory-footprintUsage
import { getMemoryFootprint } from 'react-native-memory-footprint';
const bytes = getMemoryFootprint();
console.log(`${(bytes / 1024 / 1024).toFixed(2)} MB`);import { sampleMemoryFootprint } from 'react-native-memory-footprint';
// Read the footprint every 500ms for 5s (both configurable).
const { samples } = await sampleMemoryFootprint();
const valid = samples.filter((bytes) => bytes >= 0);
const toMB = (bytes) => (bytes / 1024 / 1024).toFixed(2);
if (valid.length === 0) {
console.log('no readings succeeded');
} else {
console.log(`first: ${toMB(valid[0])} MB`);
console.log(`last: ${toMB(valid[valid.length - 1])} MB`);
console.log(`peak: ${toMB(Math.max(...valid))} MB`);
}API
getMemoryFootprint(): number
Returns the current memory footprint of the app process, in bytes.
| Platform | Underlying metric |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| iOS | phys_footprint from task_info(TASK_VM_INFO), the same value shown in Xcode's memory gauge and used by Jetsam |
| Android | Anonymous RSS + swap (RssAnon + VmSwap from /proc/self/status), the same metric behind Play Console's "excessive memory usage" vitals |
sampleMemoryFootprint(options?): Promise<SampleMemoryFootprintResult>
Reads the memory footprint every interval ms for duration ms (natively, so timing isn't affected by JS-thread load) and resolves with the collected readings.
| Option | Default | Description |
| ---------- | ----------- | -------------------------------- |
| interval | 500 (ms) | How often to read the footprint. |
| duration | 5000 (ms) | Total time to keep sampling. |
Both must be numbers >= 1; fractional values are rounded to whole ms. A < 1 or non-finite value rejects the promise with a RangeError.
Result:
| Field | Type | Description |
| --------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| samples | number[] | Footprint readings in bytes, oldest first. samples[0] is read immediately when sampling starts; the rest follow one per interval. A reading that failed is -1 (same sentinel as getMemoryFootprint()). |
To cancel early — e.g. on component unmount — pass an AbortSignal as { signal } in the second argument. The promise then rejects with an AbortError (handle it with .catch), and aborting one call never affects other concurrent calls.
const { samples } = await sampleMemoryFootprint(
{ interval: 250, duration: 10000 },
{ signal }
);How iOS memory footprint is calculated
Per WWDC 2018 Session 416, "iOS Memory Deep Dive", Apple's own mental model for phys_footprint is:
App memory = Dirty + Compressed. Clean memory is not considered being used.
Only memory your app has actually written to (dirty), plus memory the compressor has squeezed out of residency (compressed), counts. Clean, read-only, or shared memory (like code pages loaded from system libraries) doesn't, no matter how much RAM it occupies. The exact formula, from the XNU kernel source (osfmk/kern/task.c):
phys_footprint = (internal - alternate_accounting)
+ (internal_compressed - alternate_accounting_compressed)
+ iokit_mapped
+ purgeable_nonvolatile
+ purgeable_nonvolatile_compressed
+ page_tableWhy this metric: it's the same number Xcode's memory gauge shows, and the one Jetsam actually uses to decide whether to kill your app, so it's the most actionable measure of your app's real memory risk.
Further reading
- darwin-xnu
task.c: the actual ledger field definitions (internal,internal_compressed,alternate_accounting,iokit_mapped,purgeable_nonvolatile) - WWDC 2018 Session 416, "iOS Memory Deep Dive" (slides) and session notes
- Identifying high-memory use with jetsam event reports (Apple Developer Documentation): what Jetsam is and how it decides to terminate apps
- Apple Developer Forums:
phys_footprintand IOKit accounting
How Android memory footprint is calculated
Android has no single official "memory footprint" metric; the term covers three different measurements. Per the Android memory management guide:
- Resident Set Size (RSS): The number of shared and non-shared pages used by the app
- Proportional Set Size (PSS): The number of non-shared pages used by the app and an even distribution of the shared pages (for example, if three processes are sharing 3MB, each process gets 1MB in PSS)
- Unique Set Size (USS): The number of non-shared pages used by the app (shared pages are not included)
getMemoryFootprint() reports anonymous RSS + swap: resident anonymous memory (heap, native allocations, thread stacks) plus anonymous memory currently swapped out. It's read directly from RssAnon + VmSwap in /proc/self/status, a single cheap file read with no special permissions needed.
Why this metric: it's the same one behind Play Console's "excessive memory usage" vitals warning, per the Play Vitals memory usage doc:
Tracking anonymous RSS + swap ensures you see your app's true, unevictable memory footprint.
It's also a better match for actual kill risk than PSS. PSS's proportional-sharing math exists for system-wide RAM bookkeeping, not for judging whether your app individually is heavy, and Android's low-memory killer daemon doesn't use PSS either: per the AOSP lmkd source, it picks a victim by reading plain RSS from /proc/pid/statm. Anonymous RSS + swap sits close to that, while also mirroring iOS's "dirty + compressed" model.
Further reading
- Android memory management guide: RSS vs. PSS vs. USS
- Memory usage (anonymous RSS + swap), Play Vitals: official definition of the metric this library reports
proc_pid_status(5)man page:RssAnon/VmSwapfield definitions- AOSP
lmkdsource,proc_get_size(): confirms kill decisions use RSS, not PSS - Detecting Android memory leaks in production (Lyft Engineering): cost of
Debug.getMemoryInfo()/Debug.getPss()
Contributing
License
MIT
Made with create-react-native-library
