How Grab Optimizes Image Caching on Android with Time-Aware LRU
How Grab Optimizes Image Caching on Android with Time-Aware LRU
https://www.infoq.com/news/2026/03/grab-tlru-image-cache/
Publish Date: 2026-03-14 16:01:00
Source Domain: www.infoq.com
To improve image cache management in their Android app, Grab engineers transitioned from a Least Recently Used (LRU) cache to a Time-Aware Least Recently Used (TLRU) cache, enabling them to reclaim storage more effectively without degrading user experience or increasing server costs.
The Grab Android app used Glide as its primary image loading framework, including an LRU cache to store images locally in order to reduce network calls, improve load times, and lower server costs. However, analytics showed that using a 100 MB LRU cache had significant shortcomings: it often filled up quickly for many users, leading to performance degradation, while in other cases images remained cached for months if the cache never exceeded the size limit, thus wasting storage.
To circumvent these limitations, they decided to extend LRU with time-based expiration. TLRU uses three parameters, Time To Live (TTL), which determines when a cache entry is considered expired; a minimum cache size threshold, which ensures essential images remain cached even when they expire if the cache is underpopulated; and maximum cache size, which enforces the upper storage limit.
Instead of implementing TLRU from scratch, Grab engineers chose to fork Glide and extend its DiskLruCache implementation, leveraging its “mature, battle-tested foundation”
This implementation is widely adopted across the Android ecosystem and handles complex edge cases like crash recovery, thread safety, and performance optimization that would require substantial effort to replicate.
DiskLruCache needed to be extended along three dimensions by adding support for tracking last-access time, implementing time-based eviction logic, and including a migration mechanism for existing user caches.
Last-access times were required to sort cache entries by their most recent access and had to be persisted across app restarts. The time-based eviction logic ran on each cache access to check if the least recently accessed entry…