What an OutOfMemoryError Means
OutOfMemoryError (often abbreviated OOM) is a critical runtime error in Java and Android that occurs when the virtual machine (ART on Android) cannot allocate an object in the heap due to insufficient memory. The error typically looks like this:
java.lang.OutOfMemoryError: Failed to allocate a 123456-byte allocation with 12345 free bytes and 12MB until OOM
It causes the application to crash immediately. On Android, this error is especially common on devices with limited memory (such as budget models) or when working with large resources (images, JSON responses, caches).
Common Causes
OutOfMemoryError doesn't happen randomly. Here are the most frequent causes:
- Memory leaks — objects that are no longer needed but continue to be held in memory (e.g., via static references, unregistered listeners, activity context in background tasks).
- Loading oversized images — without proper scaling or compression, especially when using
BitmapFactory.decode*without specifyinginSampleSize. - Excessively large heap size — the app attempts to create an object that physically doesn't fit in available memory (e.g., a huge array or list).
- Inefficient data structures — using
HashMaporArrayListwithout growth limits, storing duplicate data. - Unreleased resources — unclosed
Cursor,InputStream,OutputStream,Socket, which hold memory and file descriptors. - Excessive caching — storing too many objects or large objects in a cache (e.g.,
LruCache). - Processing large JSON/XML — parsing huge strings into objects without a streaming approach.
Solutions
Method 1: Memory Analysis and Leak Detection
First, you need to understand what exactly is consuming memory. Android Studio provides a powerful tool — Memory Profiler.
- Open your project in Android Studio.
- Run the app on a device or emulator.
- In the menu, select View → Tool Windows → Profiler (or press
Alt+Shift+F10). - In the Profiler window, select your device and app, then switch to the Memory tab.
- Click the Record button (circle) and perform actions that typically cause a crash (e.g., open a screen with images, scroll a list).
- After memory growth appears, click Pause, then Dump Java heap (camera icon). A heap analysis window will open.
- In the left panel, find suspicious classes (e.g., your Activity, Fragment, Bitmap) and check the instance count. If objects aren't released after leaving the screen — that's a leak.
- For detailed analysis, use Analyzer Tasks → Find Leaks. The profiler will show reference chains holding objects.
💡 Tip: Pay attention to
ActivityandContext— the most common leak culprits. If a static field or long-lived object (e.g., a singleton) holds a reference to anActivity, it won't be garbage collected.
Method 2: Optimizing Image Handling
Images are the most frequent cause of OOM. Even a small Bitmap in memory occupies 4-8 times more space than on disk (due to ARGB_8888).
Use image loading libraries (Glide, Picasso, Coil). They automatically:
- Scale images to fit the
ImageView. - Cache in memory and on disk.
- Manage lifecycle (cancel loading when Activity is destroyed).
Example with Glide:
// build.gradle (Module)
dependencies {
implementation 'com.github.bumptech.glide:glide:4.15.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
}
// Loading with automatic scaling
Glide.with(context)
.load(imageUrl)
.override(targetWidth, targetHeight) // optional: explicitly specify size
.into(imageView)
If you're not using libraries:
- Always specify
inSampleSizewhen decoding:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
// Calculate inSampleSize based on required dimensions
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
- Use
Bitmap.Config.RGB_565(2 bytes per pixel instead of 4) if alpha channel isn't needed. - Explicitly call
bitmap.recycle()(for API < 28) or assignnulland callSystem.gc()(not guaranteed, but sometimes helps).
Method 3: Proper Resource and Lifecycle Management
Many leaks occur because resources aren't closed or background tasks hold references to Activity.
- Close all resources in
finallyblocks or use try-with-resources (Java 7+):
try (Cursor cursor = db.rawQuery(...);
InputStream is = ...) {
// work
} // automatically closed
- Cancel background tasks in
onDestroy()oronStop():
override fun onDestroy() {
super.onDestroy()
job.cancel() // for coroutines
call.cancel() // for Retrofit
handler.removeCallbacksAndMessages(null)
}
- Avoid implicit references to Context:
- Don't store
Contextin static fields. - Use
applicationContextwhere UI context isn't needed. - In fragments and activities, use
viewLifecycleOwnerfor lifecycle awareness.
- Use weak references (
WeakReference) for caches that might contain context.
Method 4: Increasing Heap Size (Temporary Workaround)
If optimizations don't help and the app is critical, you can temporarily increase the memory limit.
In AndroidManifest.xml within the <application> tag, add:
<application
android:largeHeap="true"
... >
⚠️ Important: This isn't a cure-all.
largeHeaponly increases the limit on some devices (usually 2-3x), but doesn't fix leak issues. Use only as a temporary measure while fixing the root cause. On newer Android versions (especially with background restrictions), this attribute may be ignored.
Prevention
To avoid OutOfMemoryError in the future:
- Profile memory regularly — integrate Memory Profiler into your development process, especially before releases.
- Use static analysis — tools like Android Lint and SonarQube can identify potential leaks.
- Implement LeakCanary — a library that automatically detects leaks in debug builds:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
- Limit collection sizes — use
LruCachefor caching, set limits for lists. - Process large data with streaming — don't load entire JSON/files into memory; use
JsonReaderorXmlPullParser. - Test on low-RAM devices — emulator with 512MB or real budget phones.
Method N: Using Static Analyzers and CI
For complex projects, automate leak detection:
- Integrate LeakCanary into CI (e.g., with Firebase Test Lab) — run tests on devices and check reports.
- Use Perfetto — Android's system tracer that can identify memory issues at the system level.
- Conduct regular code audits for:
- Static references to
Context/View. - Unclosed resources in
finally. - Registered listeners without corresponding
unregister.
- Static references to
Additional Prevention
Also watch out for:
- APK size — large resources (uncompressed images, audio) can cause OOM on first launch.
- Number of processes — on Android 8+ there's a limit on background processes. Excessive services can lead to memory shortage.
- Adaptation for different screen densities — load only necessary resources (
drawable-hdpi,xhdpi, etc.) viaResources.getIdentifier()or CDN.
Final tip: Start with profiling — 90% of OOM issues are found in the first 10 minutes of Memory Profiler analysis. Don't ignore Lint warnings about leaks; they're often spot-on.