TL;DR
- Scenario: Using Guava Cache for local caching in Java projects, with production issues like OOM, abnormal hit rate, thread blocking and performance regression
- Conclusion: Core issues concentrate on expiration and capacity policy configuration errors, maximumSize/maximumWeight misuse, CacheLoader blocking chain and recordStats abuse
- Output: A complete troubleshooting path from configuration to monitoring
Version Matrix
| Component | Version Range | Verified |
|---|---|---|
| Guava Cache | 23.0 – 33.0-jre | Yes |
| JDK | 8, 11, 17 | Yes |
| Deployment | Spring Boot 2.x / 3.x | Yes |
Troubleshooting Issues
1. Can it cause OOM?
Situations where Guava Cache may cause OOM:
Scenario 1: Cache never expires or expiration time too long
Cache<String, Object> cache = CacheBuilder.newBuilder()
.expireAfterWrite(Long.MAX_VALUE, TimeUnit.DAYS) // Almost never expires
.build();
Scenario 2: No cache capacity limit or capacity set too large
Cache<String, LargeObject> cache = CacheBuilder.newBuilder()
.maximumSize(Integer.MAX_VALUE) // Essentially unlimited
.build();
2. Will it be cleared immediately upon expiration?
Guava Cache uses lazy cleanup mechanism:
Cleanup Trigger Timing:
- When executing
get()operation to read cache - When executing
put()operation to write cache - When executing
size()operation to count cache size
3. maximumSize / maximumWeight misconfiguration causing abnormally low cache hit rate
Root Cause Analysis:
maximumSizeset too small- weight configuration missing
Correct Configuration Suggestions:
Cache<String, Object> cache = CacheBuilder.newBuilder()
.maximumWeight(102400)
.weigher((k,v) -> ((String)v).length())
.recordStats()
.build();
4. Improper CacheLoader / get usage causing blocking or cascading exceptions
Thread Blocking Issue:
- When multiple threads concurrently call
cache.get(key)for the same uncached key - All threads will block waiting for the first thread to complete
5. recordStats abuse + frequent statistics reading
Problem Root Cause Analysis:
- recordStats mechanism overhead: Every cache operation requires atomic updates to multiple counters
- Problem with frequent statistics reading: Business code frequently calls
cache.stats()in critical path
Error Quick Reference
| Symptom | Root Cause | Fix |
|---|---|---|
| Heap memory continuously rises until OOM | Cache never expires or expiration extremely long | Configure reasonable expireAfterWrite and maximumSize for all caches |
| Configured expiration policy but expired data still occupies memory for long | Lazy deletion not triggered | Call cache.cleanUp() in scheduled tasks |
| Cache hit rate consistently below 30% | maximumSize set too small | Recalculate maximumSize using “hot key count × safety factor” |
| Many threads blocked under high concurrency | CacheLoader.load contains slow SQL | Extract heavy I/O from load and add rate limiting |
| QPS drops 5-15% after enabling recordStats | recordStats maintains atomic counters for every operation | Enable recordStats only in pressure testing or troubleshooting environments |