Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

JMH-Based Performance Benchmarking of Java JSON Libraries: Gson, Fastjson, Jackson, and json-lib

Tech 1

Performance-sensitive systems often move JSON back and forth at high volume. When latency or throughput matters, the choice of JSON library can influence CPU time and allocation rates. Using JMH, the runtime characteristics of four popular Java libraries—Gson, Fastjson, Jackson, and json-lib—are contrasted for both serialization and deserialization workloads.

Selection considerations

  • Converting JSON strings to tree models or POJOs
  • Converting POJOs to compact JSON strings
  • Handling nested structures and collections
  • API ergonomics and configurability
  • Dependency footprint and compatibility

Libraries overview

  • Gson (https://github.com/google/gson)

    • Mature, zero external dependencies, straightforward API via Gson#toJson/fromJson.
    • Works well for typical POJO bindings; advanced configuraton available (TypeAdapters, ExclusionStrategies, etc.).
  • Fastjson (https://github.com/alibaba/fastjson)

    • Performance-focused implementation with minimal dependencies.
    • Very fast for many scenarios; care is advised for complex generic types and configuration nuances.
  • Jackson (https://github.com/FasterXML/jackson)

    • Widely adopted; provides streaming (jackson-core), data-binding (jackson-databind), and annotations (jackson-annotations).
    • Strong extensibility, good performance, and robust feature set (modules for Java 8 types, Afterburner, etc.).
  • json-lib (http://json-lib.sourceforge.net/index.html)

    • Older library with multiple transitive dependencies.
    • Limited in handling of nested generics and modern Java types; not performance-oriented.

Maven coordinates

Use versions appropriate for the target runtime; the following block illustrates the four libraries as standalone dependencies.

<!-- JSON libraries -->
<dependencies>
  <dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.4</version>
    <classifier>jdk15</classifier>
  </dependency>

  <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.2</version>
  </dependency>

  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.46</version>
  </dependency>

  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.4</version>
  </dependency>

  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.4</version>
  </dependency>
</dependencies>

Utility wrappers

FastjsonHelper.java

import com.alibaba.fastjson.JSON;

public final class FastjsonHelper {
    private FastjsonHelper() {}

    public static String toJson(Object value) {
        return JSON.toJSONString(value);
    }

    public static <T> T fromJson(String payload, Class<T> type) {
        return JSON.parseObject(payload, type);
    }
}

GsonHelper.java

import com.google.gson.*;

public final class GsonHelper {
    private static final Gson G = new GsonBuilder().create();

    private GsonHelper() {}

    public static String toJson(Object value) {
        return G.toJson(value);
    }

    public static <T> T fromJson(String payload, Class<T> type) {
        return G.fromJson(payload, type);
    }

    public static String pretty(String compactJson) {
        JsonElement tree = JsonParser.parseString(compactJson);
        return new GsonBuilder().setPrettyPrinting().create().toJson(tree);
    }
}

JacksonHelper.java

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public final class JacksonHelper {
    private static final ObjectMapper MAPPER = new ObjectMapper();

    private JacksonHelper() {}

    public static String toJson(Object value) {
        try {
            return MAPPER.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> T fromJson(String payload, Class<T> type) {
        try {
            return MAPPER.readValue(payload, type);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

JsonLibHelper.java

import net.sf.json.JSONObject;

public final class JsonLibHelper {
    private JsonLibHelper() {}

    public static String toJson(Object value) {
        return JSONObject.fromObject(value).toString();
    }

    @SuppressWarnings("unchecked")
    public static <T> T fromJson(String payload, Class<T> type) {
        return (T) JSONObject.toBean(JSONObject.fromObject(payload), type);
    }
}

Model types

import java.util.*;
import java.util.Date;

public class Person {
    private String name;
    private FullName fullName;
    private int age;
    private Date birthday;
    private List<String> hobbies;
    private Map<String, String> clothes;
    private List<Person> friends;

    // getters and setters omitted for brevity

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder();
        b.append("Person{name=").append(name)
         .append(", fullName=").append(fullName)
         .append(", age=").append(age)
         .append(", birthday=").append(birthday)
         .append(", hobbies=").append(hobbies)
         .append(", clothes=").append(clothes)
         .append('\n');
        if (friends != null && !friends.isEmpty()) {
            b.append("Friends:\n");
            for (Person f : friends) {
                b.append('\t').append(f).append('\n');
            }
        }
        return b.toString();
    }
}
public class FullName {
    private String firstName;
    private String middleName;
    private String lastName;

    public FullName() {}

    public FullName(String first, String middle, String last) {
        this.firstName = first;
        this.middleName = middle;
        this.lastName = last;
    }

    // getters and setters omitted

    @Override
    public String toString() {
        return "[firstName=" + firstName + ", middleName=" + middleName + ", lastName=" + lastName + ']';
    }
}

Serialization microbenchmark

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.*;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class JsonEncodeBench {

    @Param({"1000", "10000", "100000"})
    private int iterations;

    private Person subject;

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
                .include(JsonEncodeBench.class.getSimpleName())
                .forks(1)
                .warmupIterations(0)
                .build();
        Collection<RunResult> results = new Runner(opt).run();
        // Persist or visualize results via a custom sink if desired
        // ResultsSink.emit("Encode", results, "iterations", "seconds");
    }

    @Benchmark
    public void encodeWithJsonLib() {
        for (int i = 0; i < iterations; i++) {
            JsonLibHelper.toJson(subject);
        }
    }

    @Benchmark
    public void encodeWithGson() {
        for (int i = 0; i < iterations; i++) {
            GsonHelper.toJson(subject);
        }
    }

    @Benchmark
    public void encodeWithFastjson() {
        for (int i = 0; i < iterations; i++) {
            FastjsonHelper.toJson(subject);
        }
    }

    @Benchmark
    public void encodeWithJackson() {
        for (int i = 0; i < iterations; i++) {
            JacksonHelper.toJson(subject);
        }
    }

    @Setup
    public void setUp() {
        List<Person> peers = new ArrayList<>();
        peers.add(buildPerson("Xiao Ming", null));
        peers.add(buildPerson("Tony", null));
        peers.add(buildPerson("Chen Xiao er", null));
        subject = buildPerson("Xiao Shu", peers);
    }

    private static Person buildPerson(String n, List<Person> peers) {
        Person p = new Person();
        p.setName(n);
        p.setFullName(new FullName("zjj_first", "zjj_middle", "zjj_last"));
        p.setAge(24);
        List<String> likes = new ArrayList<>();
        likes.add("Basketball");
        likes.add("Swimming");
        likes.add("coding");
        p.setHobbies(likes);
        Map<String, String> wear = new HashMap<>();
        wear.put("coat", "Nike");
        wear.put("trousers", "adidas");
        wear.put("shoes", "Anta");
        p.setClothes(wear);
        p.setFriends(peers);
        return p;
    }
}

Observed behavior in typical runs shows:

  • For smaller iteration counts, Gson often leads by a small margin.
  • As the workolad scales (e.g., 100k serializations), Fastjson tends to overtake, with Jackson close behind.
  • Jackson maintains consistently strong results acrosss scales.
  • json-lib trails significantly in all cases.

Deserialization microbenchmark

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.Collection;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class JsonDecodeBench {

    @Param({"1000", "10000", "100000"})
    private int iterations;

    private String json;

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
                .include(JsonDecodeBench.class.getSimpleName())
                .forks(1)
                .warmupIterations(0)
                .build();
        Collection<RunResult> results = new Runner(opt).run();
        // ResultsSink.emit("Decode", results, "iterations", "seconds");
    }

    @Benchmark
    public void decodeWithJsonLib() {
        for (int i = 0; i < iterations; i++) {
            JsonLibHelper.fromJson(json, Person.class);
        }
    }

    @Benchmark
    public void decodeWithGson() {
        for (int i = 0; i < iterations; i++) {
            GsonHelper.fromJson(json, Person.class);
        }
    }

    @Benchmark
    public void decodeWithFastjson() {
        for (int i = 0; i < iterations; i++) {
            FastjsonHelper.fromJson(json, Person.class);
        }
    }

    @Benchmark
    public void decodeWithJackson() {
        for (int i = 0; i < iterations; i++) {
            JacksonHelper.fromJson(json, Person.class);
        }
    }

    @Setup
    public void setUp() {
        json = "{" +
                "\"name\":\"Xiao Shu\"," +
                "\"fullName\":{\"firstName\":\"zjj_first\",\"middleName\":\"zjj_middle\",\"lastName\":\"zjj_last\"}," +
                "\"age\":24," +
                "\"birthday\":null," +
                "\"hobbies\":[\"Basketball\",\"Swimming\",\"coding\"]," +
                "\"clothes\":{\"shoes\":\"Anta\",\"trousers\":\"adidas\",\"coat\":\"Nike\"}," +
                "\"friends\":[" +
                "{\"name\":\"Xiao Ming\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"Basketball\",\"Swimming\",\"coding\"],\"clothes\":{\"shoes\":\"Anta\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null}," +
                "{\"name\":\"Tony\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"Basketball\",\"Swimming\",\"coding\"],\"clothes\":{\"shoes\":\"Anta\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null}," +
                "{\"name\":\"Chen Xiao er\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"Basketball\",\"Swimming\",\"coding\"],\"clothes\":{\"shoes\":\"Anta\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null}]" +
                "}";
    }
}

Typical outcomes for the decode benchmark indicate that Gson, Jackson, and Fastjson perform similarly and handle the workload efficiently, while json-lib remains notably slower. As always with microbenchmarks, absolute numbers depend on hardware, JVM, JIT optimizations, and library versions; relative trends above are stable under repeated runs on common setups.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.