CLI tool
jacoco/jacoco avatar
jacoco/jacoco

JaCoCo: coverage measured on the bytecode rather than the source

:microscope: Java Code Coverage Library

4,603 stars1,199 forksJavaNOASSERTION

At a glance

What is it?
The Java code coverage library that has been chasing new Java releases for over a decade, and spends most of its release notes filtering out bytecode the compiler generated on your behalf.
Who is it for?
JaCoCo's real subject is not the coverage percentage but the gap between your source and the bytecode the compiler produced from it. Every module in the repository is organised around that gap, which is why the report package needs filters and why the release notes read as a running list of compiler artifacts to exclude.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 18 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 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Coverage computed on class files, not on source text

JaCoCo is a Java code coverage library, and the interesting decision is one it made long ago: it instruments bytecode rather than trying to observe source lines. The repository topics state the mechanism outright, listing bytecode, instrumentation, java-agent, java-virtual-machine and coverage alongside the language versions the project tracks.

That choice has consequences in both directions, and the release notes make them concrete. Because the unit of measurement is the class file, JaCoCo is not tied to a particular build tool or a particular compiler plugin, and it sees everything the JVM will actually execute, including code a language-level tool might miss. The cost is that the compiler emits instructions for things you did not write. Lambdas, default arguments, inline classes, switch statements and annotation-driven helpers all become branches in the bytecode, and a naive reading of those instructions produces coverage numbers that look wrong.

So a large fraction of what this project does is subtraction. Version 0.8.15 filters compatibility methods generated for interface functions in Kotlin, compatibility methods for exposed boxed inline value classes, methods generated for functions carrying the JvmStatic annotation, and bytecode that javac versions 24 through 26 generate for switch statements and exception handling. Version 0.8.14 filters branches for default arguments numbered 33 or higher, elvis operators following a safe call, longer chains of safe calls, and the intrinsic used to implement suspending lambdas.

That is not padding in a changelog. Each entry is a construct where the compiler's output would otherwise be attributed to your code, and each fix moves a number in someone's report.

A README that is a directory rather than a manual

The JaCoCo README is short, and it is short on purpose. It names the project, states that it is a free Java code coverage library distributed under the Eclipse Public License, and then offers a list of starting points keyed to intent: I want to use JaCoCo, I want to know how JaCoCo works, I have a question, I found a bug, I have an idea.

The use path fans out to a download page and four build-specific pages for Maven, Ant, the command line interface, and other integrations. The understand path goes to the documentation. The question path offers an FAQ, the documentation, and a Google Groups user forum. Bugs and ideas both go to GitHub issue templates, with the feature request link pointing at the forum instead.

Nothing else is in the file. There is no install snippet, no sample report and no configuration example, which means an article about JaCoCo cannot quote a command from its README, because its README contains none. What you can say is where the project expects you to go for each kind of question, and that division is deliberate. A library with fifteen years of compatibility history has a documentation site, not a README.

Two badges are worth reading for the operational picture. The build badge points at Azure Pipelines on the master branch, and the second badge points at Maven Central for the org.jacoco namespace, which is where the artifacts actually live for anyone not building from source.

Six modules and a validation suite per language

The repository tree is laid out as one directory per Maven module, each with a matching test module. Reading the names tells you the architecture.

`org.jacoco.agent` is the part that runs in your JVM, and it has a sibling runtime module, `org.jacoco.agent.rt`, for the code the agent needs at runtime. `org.jacoco.core` is the analysis engine: it reads class files and works out what to probe. `org.jacoco.report` consumes that analysis and produces output. `org.jacoco.cli` is the command line front end, and `org.jacoco.ant` and `jacoco-maven-plugin` are the two build tool integrations the project ships itself. There is also `org.jacoco.doc` for the documentation site and `jacoco.build` for build tooling.

The `org.jacoco.core.test.validation.*` directories are the part worth slowing down on. There is one per language or version the project claims to handle correctly: java5, java7, java8, java14, java16, java21, java22, java25, java27, java28, plus kotlin, scala and groovy. Each is a separate validation suite.

That structure is the project's answer to the hard question. Coverage correctness is not a single claim but a claim per construct, and a new validation module for a new Java version is the artifact that makes the support claim testable. Note the range the directories already span: java27 and java28 have validation directories even though the latest release only declares official support for Java 26 with experimental handling of Java 27 class files. The suites lead the releases.

There is also `jacoco.benchmarks`, which is a reminder that instrumentation has a runtime cost someone is actively measuring, and `jacoco.tests`, a shared module for the tests used across the suites.

Three releases a year, each one chasing a Java version

The release cadence explains the shape of the project better than any feature list would. Version 0.8.13 was published on 2025-04-02 and added official support for Java 23 and Java 24, with experimental support for Java 25 class files. Version 0.8.14 followed on 2025-10-11, adding official support for Java 25 and experimental support for Java 26. Version 0.8.15 arrived on 2026-06-05 with official support for Java 26 and experimental support for Java 27.

Each of those releases also carried Kotlin work. 0.8.13 calculated line coverage for inline functions, for inline functions with a reified type parameter, and for functions marked JvmSynthetic, and it filtered bytecode from the Kotlin Compose compiler plugin and from inline value classes. The version and the language support move together, which reflects that Kotlin on the JVM emits bytecode the coverage tool has to recognise.

The repository topics track the span explicitly, listing java5, java8, java11, java17, java21, java25 and java26 alongside groovy, kotlin, coverage and instrumentation. With 4,603 stars, 1,199 forks and 253 open issues, this is a dependency that many teams would notice breaking before they noticed it working.

The last push was on 2026-09-18, so the master branch is active. Whether a given Java version is officially supported is answered by the release notes for the version you are on, not by the branch you are reading, which is the practical reason to check the release page rather than the repository.

What the project leaves to its own documentation

Because the README delegates, the honest gaps are worth naming. Nothing on the repository page tells you how to configure exclusion filters, how to merge coverage from several execution units, or how the report formats differ from each other. All of that lives in the documentation the README links, and the FAQ is the page to read first because filters are where most coverage arguments are settled.

Nothing tells you how JaCoCo behaves under a particular test runner or in a particular container either, since the integrations page exists precisely because those combinations vary. And nothing states a license in the repository metadata; the README's own sentence is the claim, and it names the Eclipse Public License, with a LICENSE.md at the repository root.

For an evaluation, the practical sequence is short. Read the download page to pick an artifact, read the page for your build tool, and then read the filters documentation before trusting any number your build produces. If you are interested in how a specific language construct is measured, the validation modules named after that language version are the part of the repository that answers it directly.

Editorial conclusion

JaCoCo's real subject is not the coverage percentage but the gap between your source and the bytecode the compiler produced from it. Every module in the repository is organised around that gap, which is why the report package needs filters and why the release notes read as a running list of compiler artifacts to exclude. If you are choosing a coverage tool, the deciding question is whether you can accept numbers computed on class files, because that is what makes JaCoCo independent of your build system and of your language's compiler. The repository itself is thin documentation, so the starting point is the download page and then the plugin page for your build, the FAQ for the report filters, and the validation modules if you want to see how a given construct is treated. License is worth confirming for your organisation: the README states JaCoCo is distributed under the Eclipse Public License and the repository carries a LICENSE.md, but the repository metadata itself reports no asserted license identifier.

Frequently asked questions

What is JaCoCo used for?

JaCoCo measures test coverage for Java code. It instruments bytecode at runtime through a Java agent, records which parts executed while your tests ran, and turns that into line, branch, instruction and class coverage that can be reported as HTML, XML or CSV.

How does JaCoCo calculate coverage?

It works on class files rather than source lines. The agent module runs inside the JVM and inserts probes into the bytecode, the core module reads class files and works out what to probe, and the report module turns the collected execution data into a report. Because the unit is the class file, bytecode your compiler generated for its own purposes has to be filtered out, which is what the project's validation modules and release notes are largely about.

Which module produces the HTML report?

The report module. The tree separates the runtime pieces, the analysis engine in `org.jacoco.core`, and output generation in `org.jacoco.report`, with the command line and the Ant and Maven plugins sitting on top as front ends.

How do I know whether my Java version is supported?

Check the release notes for the JaCoCo version you are actually running. The latest release declares official support for Java 26 and experimental support for Java 27 class files, and earlier releases followed the same pattern of adding a new Java version and marking the next one experimental. The repository topics list the versions the project tracks, but the release notes are what settle support for a specific artifact.

Why do my coverage numbers include code I never wrote?

Because JaCoCo measures bytecode, and compilers emit instructions for language constructs rather than for lines you typed. Lambdas, default arguments, inline value classes and switch lowering all show up. The project filters these out and fixes the remaining cases with each release, so an odd number often means either a filter you need to configure or a construct a newer release has not covered yet.

Official sources

  1. Issues
  2. jacoco/jacoco on GitHub
  3. Project website
  4. README
  5. Releases
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/jacoco-jacoco.svg)](https://hysenlabs.com/projects/jacoco-jacoco)