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

Caffeine: the Java in-memory cache behind Cassandra, Kafka and Solr

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

17,877 stars1,717 forksJavaApache-2.0

At a glance

What is it?
Caffeine is a Java caching library with a Guava-inspired API and a TinyLFU admission policy. It is a good fit for read-heavy JVM services; it is the wrong tool for anything that needs to survive a restart.
Who is it for?
Adopt Caffeine if you run a JVM service that repeatedly computes the same values and you can afford to lose them on restart. Do not adopt it if entries must outlive the process, or if you need a shared cache across nodes: it is per-process, and the README points at JCache only as an extension, not as a distributed store.
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 last received commits 6 days ago.
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 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Caffeine replaces in a JVM service

The problem is repeated computation of the same value inside one process. A database row, a parsed configuration, a resolved permission set: each is cheap once and expensive at a few thousand requests per second. The usual first attempt is a ConcurrentHashMap, which grows without bound and never expires anything. The usual second attempt is a hand-rolled map with a timestamp check, which is where the subtle bugs live.

Caffeine targets Java services that need bounded memory and a sensible eviction choice without writing that bookkeeping themselves. The README describes it as "a high performance, near optimal caching library" and credits the design to experience building Guava's cache and ConcurrentLinkedHashMap, so the intended audience is people already comfortable with Guava's CacheBuilder and looking for something newer rather than a different model. The library is per-process. It is not a distributed cache, and nothing in the README suggests otherwise.

TinyLFU admission, the window, and what actually gets evicted

The mechanism worth understanding is admission. A plain LRU cache evicts the least recently used entry when it is full, which means a single scan over a large key space can push out the hot working set. Caffeine instead keeps a frequency sketch and decides whether a newly requested entry deserves to displace an existing one. The README links three papers on the policy: TinyLFU, Adaptive Software Cache Management, and Lightweight Robust Size Aware Cache Management, and the wiki page on efficiency is where the argument is made in prose.

The README does not spell out the internal data structures. What it does state is the observable behaviour: size-based eviction "based on frequency and recency", plus a separate admission step. The adaptive part is the part most people miss. A recency-biased workload and a frequency-biased workload want different policies, and the design shifts between them rather than fixing one. If your access pattern is genuinely uniform random, admission buys you little and a simpler map would do.

Expiration is separate from eviction. expireAfterWrite and expireAfterAccess measure from the write or the last access respectively, and refreshAfterWrite marks an entry stale without removing it, so the first request after the interval triggers a reload while the stale value is still returned. That distinction matters in production: an expired entry is a miss and a latency spike, a refreshed entry is a background reload.

Adding Caffeine to a Gradle build and building a first cache

Caffeine is published to Maven Central. The README gives the Gradle coordinate directly, with the version it documents for the current release. The README also states that Java 11 or above should use the 3.x line and older runtimes should use 2.x, so check your target before picking a version.

gradle
implementation("com.github.ben-manes.caffeine:caffeine:3.2.4")

// Optional extensions
implementation("com.github.ben-manes.caffeine:guava:3.2.4")
implementation("com.github.ben-manes.caffeine:jcache:3.2.4")

The first line is the core artifact. The other two are optional: the guava artifact provides adapters to Guava's cache types, and the jcache artifact implements JSR-107. Leave them out unless you actually need the bridge.

The README's own example builds a LoadingCache with three policies at once. This is the shape most services end up with.

java
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(Duration.ofMinutes(5))
    .refreshAfterWrite(Duration.ofMinutes(1))
    .build(key -> createExpensiveGraph(key));

maximumSize caps the entry count, expireAfterWrite forces a reload five minutes after a write, and refreshAfterWrite reloads in the background one minute after a write. The loader passed to build is invoked on a miss, so the first call for a given key pays the cost of createExpensiveGraph. If you do not want automatic loading, build a plain Cache and call get with a mapping function instead. The README documents statistics accumulation as a feature, which is how you find out whether the cache is actually helping rather than guessing.

Where Caffeine is the wrong choice

The first limitation is stated by omission. Caffeine is an in-memory cache, so a restart empties it. There is no persistence layer in the README, no write-ahead log, no snapshot. If your service restarts under load and the warm-up cost is unacceptable, you need a store with durability in front of it, and Caffeine becomes a second-level optimization rather than the answer.

The second is scope. Two instances of a service have two independent caches with two independent eviction histories. If an upstream write must invalidate entries everywhere, Caffeine gives you no mechanism for that. The README lists JCache as an extension, but JCache is an API, not a distributed implementation, and nothing in the README claims otherwise.

The third is the loader. Automatic loading means a miss runs your function inside the cache's own machinery. If that function calls back into the same cache, or blocks for a long time, you have moved the failure into a harder place to debug. The repository does contain an examples directory with a coalescing bulk loader built on Reactor, which suggests the maintainers consider concurrent loading a real enough problem to demonstrate. The README itself does not document a timeout on the loader, so a hanging loader is something you have to guard against yourself.

Finally, the eviction policy is probabilistic in spirit. TinyLFU makes a good bet about what to keep, not a guarantee. If you need deterministic retention of specific entries, no frequency-based policy will give it to you.

Caffeine against Guava's CacheBuilder

The obvious alternative is Guava's cache, and the README is explicit that Caffeine's API is Guava-inspired and that its authors worked on Guava's cache. Migration is therefore mostly mechanical: Caffeine.newBuilder() replaces CacheBuilder.newBuilder(), and the policy methods have the same names. The guava extension artifact exists precisely so the two can coexist while you move.

The difference is in what happens when the cache is full. Guava's cache uses size-based eviction with a recency-oriented policy; Caffeine adds the admission step and the adaptive window described in its papers. In practice that means Caffeine is less likely to be polluted by a burst of one-off keys. The README does not publish a head-to-head number, and the linked benchmarks page is where any such comparison lives, so treat the claim as a design argument rather than a measured guarantee.

If you are already on Guava and the cache is not a bottleneck, moving buys you little. If you have watched a scan or a batch job evict your hot set, the admission policy is the reason to switch.

Versioning, licence and the cost of staying current

Caffeine ships under Apache-2.0, which is a permissive licence. The practical implication for most teams is that you can bundle it into a closed-source product and ship it, provided you preserve the licence notice and are aware of the patent grant. That is a description of the licence text, not legal advice; your counsel decides what your distribution requires.

The versioning rule is the one thing to get right before anything else. The README states that Java 11 or above should use 3.x and everything older should use 2.x. That is a hard split, not a preference. A service stuck on Java 8 stays on the 2.x line and will not receive the 3.x changes.

On maintenance: the repository is not archived, and the last push was on 2026-05-03, which is the same date as the v3.2.4 release. The release before that, v3.2.3, was published on 2025-10-28, and v3.2.2 on 2025-07-13. The cadence is roughly a few releases a year, so plan for a dependency bump every few months rather than a continuous stream. Upgrades within 3.x are the normal case; the release notes are where the breaking details would appear, and the README does not summarize them.

Editorial conclusion

Adopt Caffeine if you run a JVM service that repeatedly computes the same values and you can afford to lose them on restart. Do not adopt it if entries must outlive the process, or if you need a shared cache across nodes: it is per-process, and the README points at JCache only as an extension, not as a distributed store. Before rolling it out, verify two things yourself: that the removal listener fires where you expect on eviction, and that refreshAfterWrite does not block the caller the way expireAfterWrite does.

Frequently asked questions

Is Caffeine good or bad for you?

That question is about the stimulant, not this library. Caffeine here is a Java caching library for in-memory caches, published under Apache-2.0.

How do you install Caffeine?

Add the Maven Central coordinate to your build. The README shows the Gradle form as implementation("com.github.ben-manes.caffeine:caffeine:3.2.4"), with optional guava and jcache artifacts alongside it.

How do you use Caffeine in Java code?

Build a cache with Caffeine.newBuilder(), set policies such as maximumSize and expireAfterWrite, and call build with a loader function if you want automatic population. The README's example returns a LoadingCache that loads missing keys through the supplied function.

How do you install Caffeine on a Mac?

The install path is the same as on any platform: add the Maven Central artifact to your build, for example implementation("com.github.ben-manes.caffeine:caffeine:3.2.4") in Gradle. The README gives no OS-specific instructions.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ben-manes-caffeine.svg)](https://hysenlabs.com/projects/ben-manes-caffeine)