Facebook Android
The Memory Devourer
Facebook was the most-installed app and the most-hated app simultaneously. Users called it a memory monster.
The Incident
Between 2012 and 2015, Facebook's Android app was notorious for excessive memory usage and out-of-memory crashes. On devices with less than 1GB RAM — the majority of Android phones globally at the time — the app crashed regularly mid-scroll through the News Feed. The engineering team eventually built an entirely new image loading library called Fresco to solve the problem. What made the original approach so catastrophic?
Evidence from the Scene
- Out-of-memory crashes occurred consistently when scrolling through image-heavy feeds
- Heap dumps revealed hundreds of large Bitmap objects alive in memory simultaneously
- Memory usage grew with each scroll session and never returned to baseline
- The same image loaded multiple times created multiple independent Bitmap objects
- Devices with 512MB RAM crashed within 2 minutes of opening the app
The Suspects
3 of these are the real root causes. The others are plausible-sounding distractors.
Loading full-resolution Bitmaps without downsampling to the display size
In-memory Bitmap cache with no eviction policy — growing without bound
Bitmap references held in static fields, preventing garbage collection
Bitmap decoding performed on the main thread causing ANR dialogs
Not using an image loading library like Glide or Picasso
RecyclerView not using the ViewHolder pattern — inflating layouts on every bind
The Verdict
Real Root Causes
Loading full-resolution Bitmaps without downsampling to the display size
A 4MP camera photo decoded at full resolution uses ~16MB of heap on a device with a 48MB app heap limit. Using BitmapFactory.Options.inSampleSize to decode at display resolution reduces this to under 1MB — a 16x improvement that fits dozens of images in the same memory.
In-memory Bitmap cache with no eviction policy — growing without bound
Caching Bitmaps without an LRU eviction policy means the cache size grows with every image viewed in a session. After scrolling through 50 posts with multiple images each, the cache could hold hundreds of megabytes — more than the entire device RAM on low-end phones.
Bitmap references held in static fields, preventing garbage collection
Static fields have application-lifetime scope. A Bitmap referenced statically is never eligible for garbage collection regardless of memory pressure. Static image caches were common before WeakReference-based and LRU-based caching became standard practice.
Plausible But Wrong
Bitmap decoding performed on the main thread causing ANR dialogs
Main-thread Bitmap decoding causes ANRs and UI freezing — not out-of-memory crashes. The symptom here is OOM from heap exhaustion, not a blocked main thread.
Not using an image loading library like Glide or Picasso
The absence of a library is a consequence of the era, not the cause of OOM. The root failures were specific memory management mistakes — any of which would cause OOM even with a loading library if its configuration was wrong.
RecyclerView not using the ViewHolder pattern — inflating layouts on every bind
Missing ViewHolder causes jank and CPU overhead from layout inflation — not the OOM crashes described here. Heap dumps showing hundreds of Bitmap objects point directly to memory mismanagement.
Summary
Facebook's News Feed was a Bitmap graveyard. Every image was decoded at full resolution, stored in a statically-referenced cache with no eviction policy, and never released. On a device with a 48MB app heap limit, scrolling through 20 posts could easily allocate 320MB of Bitmap data — the app crashed instantly. This engineering pain directly motivated Facebook to build Fresco, which (in its original 2015 design) stored Bitmap pixel data in Android's ashmem (anonymous shared memory) region outside the Java heap on API < 21, bypassing the Java heap limit. Note: since Android 8.0 (Oreo), the Android framework itself stores Bitmap pixel data in native memory by default — this heap-bypass benefit is now built into the platform and not unique to Fresco. Fresco is now one of the most widely used Android libraries in existence.
The Real Decision That Caused This
“Managing Bitmap memory manually without downsampling, LRU eviction, or scope-aware lifetimes — before the ecosystem had standardized image loading libraries that handle this automatically.”
Lesson Hint
Chapter 7 (Platform & Performance) covers Bitmap memory management and why Fresco/Glide/Coil exist. Chapter 5 (Data & Persistence) covers LRU eviction policies.
Want to test yourself before reading the verdict?
Open Interactive Case in Autopsy Lab