Why Fastjson2 Slows Down: Memory, GC, and Performance Secrets Revealed
This article examines Fastjson2's performance compared to Fastjson, detailing environment setup, benchmark results, GC analysis, and a deep dive into the library's serialization and deserialization source code to explain why memory settings dramatically affect speed.
Overview
Fastjson2 is the major upgrade of the Fastjson project, aiming to provide a high‑performance JSON library for the next decade. The article evaluates the concrete improvements that lead to its performance boost.
Environment Preparation
A Spring Boot project with two modules— fastjson (Fastjson 1.2.79) and fastjson2 (Fastjson2 2.0.8)—is set up for side‑by‑side testing.
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.8</version>
</dependency>The test class creates a Student POJO with Lombok @Data and @Builder, populates a list of 100 000 students, and repeatedly serializes/deserializes the list while measuring elapsed time.
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class Student {
private String name;
private Integer age;
private String address;
public Student(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
private static final Integer NUM = 100;
public static void main(String[] args) {
long totalTime = 0L;
List<Student> studentList = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
studentList.add(Student.builder()
.name("我犟不过你")
.age(10)
.address("黑龙江省哈尔滨市南方区哈尔滨大街267号")
.build());
}
for (int i = 0; i < NUM; i++) {
long startTime = System.currentTimeMillis();
studentList.forEach(student -> {
String s = JSONObject.toJSONString(student);
JSONObject.parseObject(s, Student.class);
});
JSONArray jsonArray = JSONArray.parseArray(JSONObject.toJSONString(studentList));
jsonArray.toJavaList(Student.class);
long endTime = System.currentTimeMillis();
totalTime += (endTime - startTime);
System.out.println("单次耗费时间:" + (endTime - startTime) + "ms");
}
System.out.println("平均耗费时间:" + totalTime / NUM + "ms");
}Performance Tests
Four experiments with different JVM heap sizes ( 128M, 64M, 256M, 1G) show that Fastjson2 can be slower than Fastjson when the heap is small, but surpasses it when ample memory is provided. The key observations are:
With -Xms128m -Xmx128m, Fastjson averages 366 ms while Fastjson2 averages 478 ms.
Reducing the heap to 64M triggers an OutOfMemoryError in Fastjson2 during toJSONString.
Increasing the heap to 256M improves Fastjson2 to ~335 ms, still behind Fastjson's ~198 ms.
At 1G heap, Fastjson2 becomes faster (average 119 ms) than Fastjson (average 133 ms).
GC Analysis
Using VisualVM, the author records GC counts and pause times. Fastjson2 performs more full GCs (316) and spends ~34 s in GC, whereas Fastjson incurs fewer GCs (1675) and ~22 s total pause time. The conclusion is that insufficient heap leads to frequent GC, inflating Fastjson2's latency.
Source Code Analysis – Serialization (Writer)
The core method JSON.toJSONString(Object, JSONWriter.Feature...) creates a JSONWriter.Context, selects a JSONWriter implementation, obtains an ObjectWriter from JSONFactory.defaultObjectWriterProvider, and delegates the actual writing to the ObjectWriter.
static String toJSONString(Object object, JSONWriter.Feature... features) {
JSONWriter.Context writeContext = new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider, features);
boolean pretty = (writeContext.features & JSONWriter.Feature.PrettyFormat.mask) != 0;
JSONWriterUTF16 jsonWriter = JDKUtils.JVM_VERSION == 8 ? new JSONWriterUTF16JDK8(writeContext) : new JSONWriterUTF16(writeContext);
try (JSONWriter writer = pretty ? new JSONWriterPretty(jsonWriter) : jsonWriter) {
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);
Class<?> valueClass = object.getClass();
boolean fieldBased = (writeContext.features & JSONWriter.Feature.FieldBased.mask) != 0;
ObjectWriter<?> objectWriter = writeContext.provider.getObjectWriter(valueClass, valueClass, fieldBased);
objectWriter.write(writer, object, null, null, 0);
}
return writer.toString();
}
}The provider selects an ObjectWriterCreator (ASM‑based by default) that generates a dynamic class ObjectWriter_1. This class contains a list of FieldWriter instances, each specialized for a field type (e.g., FieldWriterInt32 for Integer, direct writeString calls for String). The generated writer is cached for reuse, which contributes to the library's speed.
Source Code Analysis – Deserialization (Reader)
The counterpart JSON.parseObject(String, Class<T>) builds a JSONReader (choosing an ASCII, UTF‑16, or unsafe implementation based on JDK version), obtains an ObjectReader from the same provider, and invokes readObject to fill a new Java instance.
static <T> T parseObject(String text, Class<T> clazz) {
if (text == null || text.isEmpty()) return null;
try (JSONReader reader = JSONReader.of(text)) {
JSONReader.Context context = reader.context;
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader<T> objectReader = context.provider.getObjectReader(clazz, fieldBased);
T object = objectReader.readObject(reader, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}The ObjectReaderCreator (ASM‑based) scans the target class for setter methods, creates FieldReader objects for each property, and assembles a dynamic ObjectReader_1. During parsing, the reader iterates over JSON fields, stores values in a temporary map, and finally constructs the target object via a constructor that matches the collected arguments.
Conclusions
Fastjson2 offers measurable speed gains, but its performance is highly sensitive to JVM heap size; insufficient memory leads to excessive GC and even OOM errors.
The library relies on ASM‑generated bytecode for both serialization and deserialization, which explains the caching benefits and the steep memory footprint.
For projects that require strict stability, Fastjson (v1) may still be preferable under constrained memory, while Fastjson2 shines when ample heap is allocated and GC pauses are tuned.
Fastjson2 is largely compatible with Fastjson, but full compatibility is not guaranteed, and the current codebase lacks extensive documentation, making source‑code exploration challenging.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
