java-json-benchmark, twenty-six JSON libraries measured the same way
Performance testing of serialization and deserialization of Java JSON libraries
At a glance
- What is it?
- java-json-benchmark is a JMH based harness that measures the throughput of more than two dozen Java JSON libraries, covering both databinding and streaming styles across two model shapes and four payload sizes. Its value is less in the numbers than in the discipline: the same seed, the same warmup, the same machine, published raw results.
- Who is it for?
- java-json-benchmark suits teams choosing a JSON library for the JVM and wanting an apples to apples comparison rather than a blog post, and it suits anyone who needs to justify a dependency choice with reproducible numbers.
- Can I use it commercially?
- Yes. MIT 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 4 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 November 7, 2026, and from our analysis. They are not legal advice.
Star history
- fabienrenaud/java-json-benchmark1,045 ★ · +4
| Repository | 2026-10-21 | 2026-11-07 |
|---|---|---|
| fabienrenaud/java-json-benchmark | 1k | 1k |
4 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
A comparison harness, not a library
This project benchmarks the throughput performance of a variety of Java JSON libraries using JMH, the OpenJDK benchmarking harness. It is not a JSON library and it does not compete with any of the libraries it measures. It is the measuring instrument.
The list of covered libraries is long. The README names avaje-jsonb, boon, djomo, dsl-json, fastjson, flexjson, fory-json, genson, gson, jackson, jakarta-json, johnzon, json-io, json-simple, json-smart, logansquare, minimal-json, mjson, moshi, nanojson, org.json, purejson, qson, tapestry, `underscore-java` and wast, with jodd json and yasson also appearing in the results table. A library that stopped being maintained can stay in the list, which is useful in itself, since json-simple and flexjson both point at old project pages.
Where a library offers both approaches, both are tested. The README says that when available, both databinding and stream implementations are tested, and the distinction shows up in the run commands as the --apis flag, which takes values like stream, databind, or both.
Two model shapes are evaluated, and the difference between them is what makes the benchmark interesting. The Users model uses primitive types, String, List and simple POJOs. The Clients model adds arrays, an enum, a UUID and a LocalDate. The README notes that fewer libraries are tested with the second model, due to lack of support for some of the evaluated types, and that fact is itself a finding: a library that cannot round trip an enum or a LocalDate is not a candidate for a domain model that contains one.
Payloads come in four sizes, 1, 10, 100 and 1000 KB, so you can see how each library behaves as the document grows.
The design choices that make the numbers comparable
A benchmark that does not control for its environment is a set of anecdotes. The README lists what this one controls, and it is worth reading that list carefully before believing any ranking.
Payloads are randomly generated upon static loading of the JVM or benchmark, and the seed is also shared across runs. That second clause matters: a shared seed means the same bytes are produced in every run and on every machine, so a difference in the numbers is a difference in the library rather than a difference in the data.
The data is read from RAM, so the measurement is of the JSON library and not of the disk. Output goes to reusable output streams when possible, which reduces allocation pressure, and all output streams are consumed to avoid dead code elimination, which is the standard JMH concern where a JIT removes work whose result nobody reads.
The library also states what it does not measure. RAM utilisation is not evaluated, compression is not evaluated, and payloads larger than 1 MB are not evaluated. Those exclusions are reasonable for a throughput comparison, and they are also the questions a memory-constrained service would ask, so a reader should treat the results as one axis of a decision rather than the decision itself.
The results themselves are published as graphs per model and per direction, with all graphs and sheets in a linked Google document and the raw JMH results linked separately. Publishing the raw output matters, because the graphs are the summary and the raw file is the evidence.
The configuration behind the published numbers
The results in the README were computed on February the 28th, 2026, and the version table gives the exact library versions used, which is the detail that makes the numbers reproducible rather than merely reported. Among them: avaje-jsonb 1.11, boon 0.34, djomo 0.9.4, dsl-json 2.0.2, fastjson 2.0.57, flexjson 3.3, genson 1.6, gson 2.11.1, jackson 2.17.1, jodd json 6.0.3, johnzon 2.0.1, jakarta 2.1.3, json-io 4.88.0, simplejson 1.1.1, json-smart 2.5.1, logansquare 1.3.7, minimal-json 0.9.5, mjson 1.4.1, moshi 1.15.1, nanojson 1.9, org.json 20240303, purejson 1.0.1, qson 1.1.1, tapestry 5.8.6, `underscore` 1.101, yasson 3.0.3 and wast 0.0.29.1.
The JMH configuration is published too, and it is specific enough to argue about. The harness version is 1.35, the VM is JDK 25.0.2 on OpenJDK 64-Bit Server VM 25.0.2+10-LTS, running from an Amazon Corretto 25 aarch64 path, which tells you the tests were run on an ARM instance. The VM options are -Xms2g -Xmx2g with java.time opened to unnamed and the jdk.incubator.vector module added. Warmup is 5 iterations of 10 seconds each, measurement is 10 iterations of 3 seconds each, the timeout is 10 minutes per iteration, and there are 3 threads which will synchronise iterations. The benchmark mode is Throughput in ops per time.
The machine is an Amazon EC2 c8g.xlarge with 4 vCPU and 8 GiB of RAM, and the benchmarks are written for Java 25.
That last combination deserves a caution. A 4 vCPU ARM instance with a fixed 2 GB heap and three synchronised threads is a specific environment. A library that wins there is winning on that machine, and a service running on a 64 core x86 host with a much larger heap is a different question. The README is not making a universal claim, it is publishing one reproducible run, which is the right thing to do and also easy to over-read.
Running it yourself, and narrowing the scope
The run interface is a shell script, and the granularity is good. By default, running ./run ser, or ./run deser respectively, runs all stream and databind serialization or deserialization benchmarks with 1 KB payloads of the Users model.
You can narrow the scope by library, API style, payload size and model:
./run deser --apis stream --libs genson,jackson
./run ser --apis databind,stream --libs jackson
./run deser --apis stream --libs dsljson,jackson --size 10 --datatype usersAnd ./run help ser or ./run help deser prints the help for those commands. To reproduce everything behind the published reports, the README points at ./run-everything, and warns that it will take several hours to complete.
The prerequisites for a local run are JDK 25 with JAVA_HOME set, and make.
There are two containerised and cloud paths as well. The Docker route is for reproducing the consistent environment or for running the benchmarks in a separate safe environment, and it needs Docker:
docker build -t java-json-benchmark:latest .
docker run --rm -it java-json-benchmark:latest ./run deserThe image is built on amazoncorretto:25-al2023-jdk pinned by digest, and it deliberately does the work as a non-root user called appuser, updates the package manager and clears its cache, and disables the shadow JAR build with an environment variable because the JAR is already built. Gradle is copied in and primed with a dependencies run so the build does not need the network later.
The AWS path uses packer and the awscli, configured through aws configure, and the Makefile is small enough to read in full. The packer target depends on a tar target that packages the Gradle files, the source and the two run scripts into app.tar.gz, and then packer build ami.json produces the AMI. The ami.json file in the repository root is the Packer template.
Reading the results without over-reading them
The useful thing about this benchmark is what it lets you conclude, and the trap is what it invites you to conclude.
The graph structure tells you that the ranking is not one-dimensional. There is a separate graph per model and per direction, and the Clients model covers a different set of libraries than the Users model. A library that is fast on Users but absent from Clients has a gap in its type support, and that gap is more consequential for most applications than the throughput difference on the model it does support. Reading the Clients results first tells you which libraries are even candidates.
The other trap is the payload size axis. Four sizes from 1 KB to 1000 KB means a library that wins at 1 KB may lose at 1000 KB, because streaming libraries amortise differently from databinding ones, and because a document that fits in cache behaves differently from one that does not. The README does not draw a single conclusion, and you should not either.
The API style axis matters just as much in practice. A stream implementation lets you control exactly what is written, which matters when you need a field to be absent rather than null, or when you are writing to a specific schema. A databinding implementation saves code. The benchmark measures both for the libraries that offer both, which is a genuine service to somebody choosing between them.
The project is MIT licensed with LICENSE.txt at the repository root, the default branch is master, and the repository has no tagged releases, so the results in the README are the record. The last push was on 2026-08-11, and the README explicitly invites help to improve the existing benchmarks or write ones for other libraries, noting that adding a library requires little work and pointing at the commit history for examples. The CI badge at the top of the page shows a Gradle workflow running on every change, so a broken benchmark is caught before it reaches the results.
Editorial conclusion
java-json-benchmark suits teams choosing a JSON library for the JVM and wanting an apples to apples comparison rather than a blog post, and it suits anyone who needs to justify a dependency choice with reproducible numbers. It is the wrong tool if you are looking for a single winner, because the rankings shift with payload size, model shape and API style, and it is the wrong tool for memory or footprint questions, because RAM utilisation and compression are explicitly not evaluated. Before running it, read the benchmark design notes in the README, since the random payload generation with a shared seed and the reusable output streams are what make the numbers comparable, and reproduce the environment with the Docker image or the packer build rather than trusting a laptop run. The results in the README were computed on 2026-02-28 and the last push was on 2026-08-11.
Frequently asked questions
Which Java JSON libraries does the benchmark cover?
The README lists avaje-jsonb, boon, djomo, dsl-json, fastjson, flexjson, fory-json, genson, gson, jackson, jakarta-json, johnzon, json-io, json-simple, json-smart, logansquare, minimal-json, mjson, moshi, nanojson, org.json, purejson, qson, tapestry, `underscore-java` and wast, with jodd json and yasson also appearing in the results table.
How do I run a subset of the benchmarks?
Use the run script with flags, for example ./run deser --apis stream --libs genson,jackson or ./run deser --apis stream --libs dsljson,jackson --size 10 --datatype users. Running ./run help deser prints the available options, and ./run-everything reproduces all the reports, which takes several hours.
What does the benchmark not measure?
The README states that RAM utilisation, compression and payloads over 1 MB are not evaluated. The results are also specific to the published environment, an Amazon EC2 c8g.xlarge with JMH 1.35 on JDK 25 with a 2 GB heap and three synchronised threads.
Why are some libraries missing from the Clients model results?
The README notes that fewer libraries are tested with the Clients model, which adds arrays, an enum, a UUID and a LocalDate, due to lack of support for some of the evaluated types.
Can I reproduce the benchmark environment with Docker?
Yes. Build with docker build -t java-json-benchmark:latest and run with docker run --rm -it java-json-benchmark:latest ./run deser. The image is based on amazoncorretto:25-al2023-jdk pinned by digest and runs as a non-root user.
Official sources
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.
[](https://hysenlabs.com/projects/fabienrenaud-java-json-benchmark)Star history chart for your README
[](https://hysenlabs.com/projects/fabienrenaud-java-json-benchmark)Was this useful?