Library / SDK
ben-manes/caffeine avatar
ben-manes/caffeine

Caffeine: The Java Cache Library That Puts TinyLFU Into Production

A high performance caching library for Java. Cache Caffeine provides an in-memory cache using a Google Guava inspired API.

17,866 stars1,714 forksJavaApache-2.0

At a glance

What is it?
Caffeine is an in-memory caching library for Java with a Guava-style API and a TinyLFU-based eviction policy. This review covers its mechanism, setup, and the trade-offs you should check before adopting it.
Who is it for?
Adopt Caffeine if you need a high-throughput in-memory cache in a Java 11+ application and you value an eviction policy that adapts to shifting workloads. It is a poor fit if you require a distributed cache, need to cache values larger than your heap, or cannot afford the complexity of its asynchronous refresh semantics.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What Caffeine Solves and Who Needs It

Caffeine addresses a specific problem: providing an in-memory cache for Java applications that must serve hot data with minimal latency while keeping memory bounded. It is aimed at engineers who previously reached for Guava's cache or built their own ConcurrentHashMap-based cache and hit limits on concurrency, eviction quality, or maintenance. The library is not a distributed cache, nor a persistent store. It lives entirely inside one JVM process. The README positions it as "a high performance caching library," and its origin is telling: the author also designed Guava's cache and ConcurrentLinkedHashMap. So this is a second-generation attempt at the same problem. You would use it when your application needs to memoize expensive computations, database queries, or remote calls, and you want a cache that handles concurrent access without turning into a bottleneck.

The Eviction Mechanism: TinyLFU and the Adaptive Window

Caffeine's core differentiator is its eviction policy. The README links to research papers on TinyLFU and its adaptive variant. TinyLFU stands for Tiny Least Frequently Used, and it is an admission policy: before an entry is admitted to the cache, the library estimates its future frequency using a compact frequency sketch. This sketch uses a probabilistic data structure to count accesses with a small memory footprint. The policy keeps frequently used entries and rejects one-hit wonders. The adaptive part, described in the README's link to "The Adaptive Window, From the Ground Up," adjusts the balance between a small recency-protected window and the main frequency-based segment. This matters because pure LFU can fail when a workload shifts to a new set of hot keys; the adaptive window gives recent entries a chance to prove themselves before being admitted. The README does not detail the exact parameters, but the mechanism is documented in the linked papers. In practice, this means Caffeine can outperform a simple LRU cache on skewed workloads, but the cost is a more complex algorithm that is harder to reason about.

Getting It Running: Dependencies and a Minimal Cache

You add Caffeine to a Gradle project with one line: implementation("com.github.ben-manes.caffeine:caffeine:3.2.4"). There are optional artifacts for Guava adapters and JCache, but the core library is a single JAR. The README shows a minimal construction: a LoadingCache that loads entries via a function. The builder accepts maximumSize, expireAfterWrite, and refreshAfterWrite, all with Duration objects. For example, the README's snippet sets a maximum of 10,000 entries, expiration after five minutes, and refresh after one minute. The build method takes a key-to-value function. This is the same pattern as Guava's CacheBuilder, so anyone familiar with Guava will feel at home. The library requires Java 11 or above for version 3.x; if you are on Java 8, you must use the 2.x line. That is a hard constraint to check before upgrading.

Beyond Basics: Async Loading, References, and Statistics

The README lists features that go beyond simple get-and-put. You can load entries asynchronously, which is useful when the load function performs I/O. You can set expiration based on last access or last write, and you can refresh entries asynchronously when the first stale request arrives. Keys can be wrapped in weak references, and values in weak or soft references, which lets the garbage collector reclaim memory under pressure. There is a removal listener for eviction notifications, a way to propagate writes to an external resource, and a statistics collector for hit rates and miss counts. These are not gimmicks; they address real operational needs. The statistics feature is particularly useful for tuning the cache size, because you can observe the hit ratio and adjust maximumSize accordingly. The async refresh is a double-edged sword: it returns stale data while the refresh happens, which is fine for some use cases but dangerous for others.

Where Caffeine Is the Wrong Tool

Caffeine is an in-memory cache, so it is the wrong tool for any scenario that requires sharing cache state across multiple JVMs or processes. If your application runs on several nodes, each node will have its own cache, and you will get inconsistent data unless you add an external cache like Redis or Memcached. Even within a single JVM, Caffeine's size-based eviction is based on entry count, not on the actual memory footprint of the values. If your values are large objects, a maximumSize of 10,000 could consume gigabytes of heap. The README does not mention any memory-based eviction. Another limitation is the complexity of the eviction algorithm. TinyLFU is a research-grade policy, and while it performs well on typical web workloads, it is not a silver bullet. If your access pattern is a sequential scan that touches every key once, TinyLFU's admission policy may reject entries that would be useful later, because their frequency is too low. The README's own benchmark page is linked, but this review did not run those benchmarks, so treat performance claims with caution.

Alternatives: Guava Cache and Caffeine's Own Adapters

The most direct alternative is Guava's cache, which Caffeine's API mimics. The difference is in the eviction policy: Guava uses a ConcurrentLinkedHashMap with an LRU-like policy, while Caffeine uses TinyLFU. For workloads with skewed access, Caffeine should have a higher hit rate, but Guava is simpler and has a smaller codebase. The README even provides a Guava adapter artifact, so you can keep your existing Guava cache API and switch the backend to Caffeine. Another alternative is to use Caffeine's JCache implementation if you need a standard JSR-107 interface. If you need a distributed cache, you would look at Infinispan or Redisson, both listed as projects that use Caffeine internally, but they are not replacements for Caffeine itself. The key difference is scope: Caffeine is a single-node cache, while those are multi-node systems. For a single-node cache, the choice is often between Caffeine and a hand-rolled ConcurrentHashMap, and Caffeine's advantage is the eviction policy and the built-in statistics.

Maintenance and Licensing: What the Repository Tells You

The repository is active, with the latest release v3.2.4 pushed on 2026-05-03. The project is not archived. The license is Apache-2.0, which is permissive and allows commercial use without copyleft obligations. The README lists a long set of integrations, including Spring, Micronaut, and Quarkus, which suggests a healthy ecosystem. However, the README does not describe an upgrade path or migration guide. The release notes are linked but not included in the material, so you would need to check those for breaking changes. The dependency is a single JAR, which keeps upgrade cost low, but you must verify that your Java version matches the 3.x requirement. The project also maintains a simulator tool, which is a notable feature: you can simulate your access log against Caffeine's policy before deploying. That is a concrete way to validate the eviction policy for your specific workload, and it is something you should do before committing to Caffeine in production.

Editorial conclusion

Adopt Caffeine if you need a high-throughput in-memory cache in a Java 11+ application and you value an eviction policy that adapts to shifting workloads. It is a poor fit if you require a distributed cache, need to cache values larger than your heap, or cannot afford the complexity of its asynchronous refresh semantics. Before adopting, verify your workload's access pattern against the TinyLFU assumptions: if your cache is dominated by one-time scans, Caffeine's admission policy may evict useful entries. Also confirm that your project can tolerate the Apache-2.0 license and that you are on a supported Java version, since 3.x requires Java 11 or above.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Community notes

Community notes