Mastering Ehcache: A Deep Dive into Java Local Caching Strategies
This article introduces Ehcache, an open‑source Java local‑cache framework, explains its layered storage options, flexible expiration policies, eviction strategies, and provides comprehensive code examples for configuring in‑memory, off‑heap, and disk‑persistent caches, along with practical usage tips and performance considerations.
Java local caching includes frameworks like Caffeine, Guava Cache, and Ehcache. While Caffeine is popular, Ehcache offers fast, flexible caching with memory and disk options and rich configuration.
1. What is Ehcache
Ehcache is an open‑source Java local‑cache framework that provides fast, flexible caching solutions. It supports in‑memory and disk storage, integrates with various Java applications including Spring, offers rich configuration options, and can work with distributed caches such as Terracotta.
2. Features of Ehcache
Layered storage:
On‑heap storage: Uses JVM heap RAM; fast but limited and subject to GC pauses.
Off‑heap storage: Uses RAM outside the JVM heap; not affected by GC, slightly slower.
Disk storage: Persists data to the file system; abundant space but slower than RAM; SSD recommended.
Clustered (distributed) storage: Remote server cache; incurs network latency and consistency overhead.
Flexible expiration policies:
No expiration – entries live as long as the application runs.
Time‑to‑live – entries expire after a fixed duration since creation.
Idle time – entries expire after a period of inactivity.
Custom expiration – implement ExpiryPolicy to define bespoke rules.
Expiration return values: Duration.ZERO – immediate expiration. Duration.INFINITE – never expires. Duration.ofSeconds(...) – expires after the specified time.
Null duration – keeps the previous expiration setting.
Eviction strategies:
LFU – evicts entries with low access frequency.
LRU – evicts least recently used entries.
FIFO – evicts entries that entered the cache earliest.
3. Cache architecture
Typical three‑tier architecture: on‑heap, off‑heap, and disk storage.
The cache manager, cache, and element form the core of Ehcache. Transactions ensure consistency across tiers, and listeners clean up or persist data according to eviction policies.
4. Practical usage
Dependency (Maven):
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.0</version>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>Creating caches:
/*************************** 1. Pure in‑memory cache *****************************/
// 1.1 Create a pre‑configured in‑memory cache
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("preConfigured",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.heap(10)))
.build(true);
// 1.2 Retrieve the cache instance
Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class);
/*************************** 2. Add a new cache *****************************/
// 2.1 Create and obtain a new cache
Cache<Long, String> myCache = cacheManager.createCache("myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.heap(10)));
/*************************** 3. Three‑tier persistent cache *****************************/
// 3.1 Create a cache with heap → off‑heap → disk persistence
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "myData")))
.withCache("threeTieredCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.disk(20, MemoryUnit.MB, true)))
.build(true);
// 3.2 Obtain the three‑tier cache
Cache<Long, String> threeTieredCache = persistentCacheManager.getCache("threeTieredCache",
Long.class, String.class);
/*************************** 4. One manager, multiple persistent caches *****************************/
// 4.1 Manage multiple caches, each persisted to its own directory
PersistentCacheManager persistentCacheManager1 = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(new File("/path/to/persistent/directory1").getAbsoluteFile()))
.withCache("cache1",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.disk(20, MemoryUnit.MB, true)))
.with(CacheManagerBuilder.persistence(new File("/path/to/persistent/directory2").getAbsoluteFile()))
.withCache("cache2",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Integer.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(20, EntryUnit.ENTRIES)
.offheap(2, MemoryUnit.MB)
.disk(30, MemoryUnit.MB, true)))
.build(true);Typical usage patterns combine tiers, e.g., heap + off‑heap + disk, or just heap + off‑heap, etc. When using disk persistence, remember to close the manager before application exit to flush data; on restart, persisted data is reloaded, reducing database load.
5. Test results
Cache with disk persistence works as expected; entries expire according to configured TTL, and data is correctly reloaded after a restart.
6. Full test code
package com.jx.jxreserve.groupbuy.manager;
import com.jd.flash.commons.exception.BaseBusinessException;
import com.jx.jxreserve.groupbuy.common.enums.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.PersistentCacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;
import org.springframework.stereotype.Component;
import java.io.File;
import java.time.Duration;
import static org.ehcache.Status.AVAILABLE;
/**
* Ehcache test manager
*/
@Slf4j
@Component
public class EhcacheTestManager {
/*************************** 1. Pure in‑memory operation *****************************/
// 1.1 Create cache preConfigured based on heap memory
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("preConfigured",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.heap(10)))
.build(true);
// 1.2 Get cache instance
Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class);
/*************************** 2. Add new instance *****************************/
// 2.1 Create a new cache and obtain it
Cache<Long, String> myCache = cacheManager.createCache("myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.heap(10)));
/*************************** 3. Three‑tier storage – disk persistence *****************************/
// 3.1 Create cache myData with heap → off‑heap → local disk
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "myData")))
.withCache("threeTieredCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.disk(20, MemoryUnit.MB, true))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(600)))) // set TTL
.build(true);
/*************************** 4. One manager, multiple caches – disk persistence *****************************/
// 4.1 Manage multiple caches, each persisted to its own directory
PersistentCacheManager persistentCacheManager1 = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/").getAbsoluteFile()))
.withCache("cache1",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.disk(20, MemoryUnit.MB, true)))
.with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/").getAbsoluteFile()))
.withCache("cache2",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Integer.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(20, EntryUnit.ENTRIES)
.offheap(2, MemoryUnit.MB)
.disk(30, MemoryUnit.MB, true)))
.build(true);
/** Set cache value */
public void setEhCache(Long key, String values) throws BaseBusinessException {
try {
log.info("setEhCache.value:{},{}", values, key);
Cache<Long, String> testDiskCache = getManagerCache("testDiskCache");
testDiskCache.put(key, values);
} catch (Exception e) {
log.error("setEhCache failure! Exception:", e);
throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(),
ErrorCode.SYSTEM_DB_ERROR.getMessage());
}
}
/** Get cache value */
public String getEhCache(Long key) throws BaseBusinessException {
try {
log.info("getEhCache.key:{}", key);
Cache<Long, String> testDiskCache = getManagerCache("testDiskCache");
return testDiskCache.get(key);
} catch (Exception e) {
log.error("getEhCache failure! Exception:", e);
throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(),
ErrorCode.SYSTEM_DB_ERROR.getMessage());
}
}
/** Close cache manager */
public void closeEhCache() throws BaseBusinessException {
try {
log.info("closeEhCache.persistentCacheManager.close1:{}", persistentCacheManager.getStatus());
persistentCacheManager.close();
log.info("closeEhCache.persistentCacheManager.close2:{}", persistentCacheManager.getStatus());
} catch (Exception e) {
log.error("closeEhCache failure! Exception:", e);
throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(),
ErrorCode.SYSTEM_DB_ERROR.getMessage());
}
}
private Cache<Long, String> getManagerCache(String cache) {
// Ensure manager is available
if (AVAILABLE != persistentCacheManager.getStatus()) {
persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "groupData")))
.withCache("testDiskCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.disk(20, MemoryUnit.MB, true)))
.build(true);
}
return persistentCacheManager.getCache(cache, Long.class, String.class);
}
}Note: For production systems requiring distributed caching, Redis is generally recommended over Ehcache’s Terracotta‑based clustering due to better reliability and performance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
