Question

Measure Android App and Device Memory Usage Programmatically

javaandroidmemorymemory-management

Question

How can an Android application programmatically determine how much memory it is using? Additionally, how can it retrieve the amount of memory currently available on the device?

Short Answer

You will learn the difference between your app's memory usage and device-wide available memory, how to query both with Android APIs, and why “free memory” is not the best health metric on Android.

Concept

Android memory measurements answer different questions:

  • App memory usage: memory currently associated with your process. This is useful when investigating leaks, large images, caches, or out-of-memory crashes.
  • Device available memory: memory Android can make available for work without being under memory pressure. This helps an app decide whether it should reduce optional work, such as preloading media.
  • Per-app memory limit: the approximate heap budget assigned to your app class. This is useful context, but it is not a target to fill.

Android runs on Linux, whose memory manager deliberately uses unused RAM for file and system caches. Therefore, a device having little literally “free” RAM is normal. Prefer ActivityManager.MemoryInfo.availMem and lowMemory over treating free RAM as a problem.

For your own process, Debug.MemoryInfo provides detailed memory statistics, including PSS (Proportional Set Size). PSS shares the cost of shared pages between processes, so it is commonly useful for comparing a process's practical memory footprint over time.

Mental Model

Think of RAM as a shared workshop:

  • Your app's memory is the amount of workspace and tools your team is currently using.
  • Device available memory is the amount of workspace the workshop manager can provide before needing to clear stored materials.
  • Cached files are materials placed nearby because they may be useful again. They occupy space, but the manager can clear them when another team needs room.

That is why “free memory” alone is misleading: memory used for cache is still reclaimable when needed.

Take Quiz

Syntax and Examples

Use ActivityManager for device-wide memory information and Debug.getMemoryInfo() for the current process.

import android.app.ActivityManager;
import android.content.Context;
import android.os.Debug;

public final class MemoryStats {
    private MemoryStats() { }

    public static void logMemory(Context context) {
        ActivityManager activityManager =
                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

        // Memory associated with this app process.
        Debug.MemoryInfo appMemory = new Debug.MemoryInfo();
        Debug.getMemoryInfo(appMemory);
        int appPssKb = appMemory.getTotalPss();

        // Device-wide memory state.
        ActivityManager.MemoryInfo deviceMemory = new ActivityManager.MemoryInfo();
        activityManager.getMemoryInfo(deviceMemory);
        long availableBytes = deviceMemory.availMem;
        boolean lowMemory = deviceMemory.lowMemory;

        // Approximate heap limit for this application class, in MB.
        int heapLimitMb = activityManager.getMemoryClass();

        System.out.println("App PSS: " + appPssKb + " KB");
        System.out.println("Available device memory: " + availableBytes + " bytes");
        System.out.println("Device low on memory: " + lowMemory);
        System.out.println("App heap limit: " + heapLimitMb + " MB");
    }
}

getTotalPss() returns kilobytes. availMem returns bytes. Convert values before displaying them to users or logs:

long availableMb = deviceMemory.availMem / (1024L * 1024L);

Call these APIs occasionally for diagnostics or adaptive behavior. Do not repeatedly poll them in a tight loop.

Step by Step Execution

Consider this diagnostic method:

ActivityManager manager =
        (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

ActivityManager.MemoryInfo deviceInfo = new ActivityManager.MemoryInfo();
manager.getMemoryInfo(deviceInfo);

Debug.MemoryInfo appInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(appInfo);

long availableMb = deviceInfo.availMem / (1024L * 1024L);
int appPssKb = appInfo.getTotalPss();
  1. getSystemService(...) obtains Android's ActivityManager service.
  2. new ActivityManager.MemoryInfo() creates an object that will receive device memory data.
  3. manager.getMemoryInfo(deviceInfo) fills that object. deviceInfo.availMem is the available memory in bytes, and deviceInfo.lowMemory indicates memory pressure.
  4. new Debug.MemoryInfo() creates an object for details about the current app process.
  5. Debug.getMemoryInfo(appInfo) asks the runtime to populate those process statistics.
  6. appInfo.getTotalPss() returns the app's total PSS in kilobytes.
  7. The division converts available bytes to megabytes using 1024L so the calculation remains a long.

The two results should not be subtracted from each other. They describe different scopes: one process versus the whole device.

Real World Use Cases

  • Image-heavy screens: Record app PSS before and after loading a photo feed to detect unexpectedly retained bitmaps.
  • Media apps: When lowMemory is true, reduce artwork preloading or clear nonessential in-memory caches.
  • Diagnostic reports: Add app PSS, available memory, and heap limit to an internal bug report.
  • Large imports: Avoid starting optional previews or concurrent import jobs when the device reports memory pressure.
  • Performance testing: Compare memory readings across repeated navigation cycles to spot growth that may indicate a leak.
Take Quiz

Real Codebase Usage

In production code, memory readings are usually used as signals, not as exact resource accounting.

Check for pressure before optional work

boolean isDeviceUnderMemoryPressure(Context context) {
    ActivityManager manager =
            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
    manager.getMemoryInfo(info);
    return info.lowMemory;
}

if (isDeviceUnderMemoryPressure(context)) {
    imageCache.clearMemory();
    return;
}

preloadNextImages();

Respond to Android's trim-memory callbacks

For cache management, lifecycle callbacks are usually more useful than polling memory values:

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);

    if (level >= TRIM_MEMORY_RUNNING_LOW) {
        imageCache.clearMemory();
    }
}

Apps commonly combine these approaches:

  • Use onTrimMemory() to release caches when Android asks.
  • Log Debug.MemoryInfo during development, testing, and diagnostics.
  • Use memoryClass as planning context, not permission to allocate up to that amount.
  • Fix the object, bitmap, or cache lifetime causing growth instead of relying on garbage collection.

Common Mistakes

Treating available RAM as an error

// Not a reliable conclusion:
if (deviceInfo.availMem < 500 * 1024 * 1024) {
    // The phone is broken or cannot run the app.
}

Android intentionally uses RAM for caching. Check deviceInfo.lowMemory and make optional work smaller rather than assuming a fixed number works on every device.

Mixing units

int availableMb = (int) deviceInfo.availMem / 1024 / 1024;

The cast can overflow before division on devices with large amounts of memory. Use long arithmetic:

long availableMb = deviceInfo.availMem / (1024L * 1024L);

Calling getMemoryInfo() as a leak detector

A single PSS reading cannot prove a memory leak. Take comparable readings after repeating the same flow, and use Android Studio Memory Profiler to inspect retained objects.

Assuming PSS equals Java heap size

PSS includes more than Java/Kotlin heap allocations, such as native allocations and shared memory portions. It is a process footprint metric, not a direct count of Java objects.

Polling continuously

Do not run memory queries many times per second. Query for diagnostics or at meaningful decision points, and rely on onTrimMemory() for cache pressure notifications.

Comparisons

Measurement or APIWhat it tells youUnitBest use
Debug.MemoryInfo.getTotalPss()Approximate practical memory footprint of this process, with shared pages apportionedKBMonitoring process growth and diagnostics
ActivityManager.MemoryInfo.availMemMemory currently available system-widebytesAdaptive optional work
ActivityManager.MemoryInfo.lowMemoryWhether the system is under low-memory conditionsbooleanReducing or clearing caches
ActivityManager.getMemoryClass()Approximate per-app heap class limitMBCapacity planning and testing
Android Studio Memory ProfilerAllocations, heap dumps, retained objects, timelinesUI/tool dataFinding leaks and allocation sources

Runtime.getRuntime() can report Java runtime heap values, but it does not represent the entire Android process. Use it only when you specifically need Java heap information, not total app memory.

Take Quiz

Cheat Sheet

// Get ActivityManager
ActivityManager manager =
        (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

// Device-wide state
ActivityManager.MemoryInfo device = new ActivityManager.MemoryInfo();
manager.getMemoryInfo(device);
long availableBytes = device.availMem;
boolean underPressure = device.lowMemory;
long thresholdBytes = device.threshold;

// Current process memory
Debug.MemoryInfo app = new Debug.MemoryInfo();
Debug.getMemoryInfo(app);
int pssKb = app.getTotalPss();

// App heap class limit
int heapLimitMb = manager.getMemoryClass();

// Bytes to MB
long mb = availableBytes / (1024L * 1024L);
  • availMem is bytes.
  • getTotalPss() is KB.
  • Low available RAM can be normal because caches are reclaimable.
  • Prefer lowMemory and onTrimMemory() for cache decisions.
  • Use Memory Profiler to investigate suspected leaks.

FAQ

How do I get my Android app's current memory usage?

Create Debug.MemoryInfo, call Debug.getMemoryInfo(memoryInfo), then read memoryInfo.getTotalPss(). The result is in kilobytes.

How do I get available RAM on Android?

Use ActivityManager.getMemoryInfo(), then read MemoryInfo.availMem. The result is in bytes.

Does Android require a permission to read memory information?

No special permission is needed for the current process information and the device memory state shown here.

Is low free RAM bad on Android?

Not necessarily. Android uses spare RAM for caches and can reclaim cache memory. lowMemory is more meaningful than a literal free-memory number.

What is PSS in Android memory usage?

PSS means Proportional Set Size. It counts private process memory plus a proportional share of memory pages shared with other processes.

Can I use getMemoryClass() as my app's exact memory limit?

No. It is an approximate heap class limit and should not be treated as a target allocation size. Actual behavior varies with the device and the type of memory being allocated.

What should an app do when memory is low?

Release recreatable caches, stop optional preloading, and respond to onTrimMemory(). Do not kill your own process as a normal memory-management strategy.

Take Quiz

Mini Project

Description

Build a small memory diagnostics helper for a debug screen or internal log. It gathers the current process PSS, device available memory, low-memory state, and the app heap class limit in a readable report.

Goal

Create a method that returns a formatted memory report and identifies whether the device is under memory pressure.

Requirements

Requirement 1

Take Quiz

Keep learning

Related questions

View more