Static ObjectMapper in Java: Performance and Design Considerations
Jackson’s ObjectMapper is a versatile and flexible tool for JSON processing in Java. However, how you manage its lifecycle, whether as a static field, singleton bean or per-use instance, can significantly impact your application’s performance, maintainability and thread safety. This article will explore the implications of using ObjectMapper as a static field and outline best practices for efficient and safe usage.
1. Understanding the Cost of Creating ObjectMapper
ObjectMapper is not a lightweight object. Constructing it involves scanning for modules, building serializers and deserializers, and initialising multiple caches. Doing this repeatedly can have a noticeable performance impact, especially in high-throughput applications.
Here’s a Java program that runs both benchmarks:
- First, it measures the time to create 1,000 new
ObjectMapperinstances. - Then, it measures the time to reuse a single static
ObjectMapperfor 1,000 serializations.
public class JacksonObjectmapperUsage {
private static final ObjectMapper STATIC_MAPPER = new ObjectMapper();
public static void main(String[] args) {
// Test 1: Creating new ObjectMapper instances 1000 times
long creationStart = System.nanoTime();
for (int i = 0; i < 1000; i++) {
ObjectMapper mapper = new ObjectMapper();
try {
mapper.writeValueAsString(new Dummy(i));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
long creationEnd = System.nanoTime();
System.out.println("Time taken using new ObjectMapper 1000 times: "
+ (creationEnd - creationStart) / 1_000_000 + " ms");
// Test 2: Reusing a static ObjectMapper 1000 times
long reuseStart = System.nanoTime();
for (int i = 0; i < 1000; i++) {
try {
STATIC_MAPPER.writeValueAsString(new Dummy(i));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
long reuseEnd = System.nanoTime();
System.out.println("Time taken using static ObjectMapper 1000 times: "
+ (reuseEnd - reuseStart) / 1_000_000 + " ms");
}
// class to serialize
static class Dummy {
private final int id;
Dummy(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
}
This comparison demonstrates the performance impact of ObjectMapper instantiation. The first test highlights the overhead of creating 1,000 new instances, while the second test shows that reusing a static instance eliminates that cost and significantly improves performance. You should see a clear performance improvement in the second part.
Output
Time taken using new ObjectMapper 1000 times: 2712 ms Time taken using static ObjectMapper 1000 times: 69 ms
2. How Jackson Handles Thread Safety
Before deciding whether to make ObjectMapper a static field, we need to understand Jackson’s thread-safety contract. According to the official documentation, once fully configured, ObjectMapper is thread-safe for concurrent use. However, modifying the configuration while it is in use is not safe.
public class ThreadSafeExample {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static void main(String[] args) throws Exception {
Runnable task = () -> {
try {
String json = "{\"name\":\"John\"}";
Person p = MAPPER.readValue(json, Person.class);
System.out.println(Thread.currentThread().getName() + " -> " + p.getName());
} catch (Exception e) {
e.printStackTrace();
}
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
t1.start();
t2.start();
}
static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
Here, multiple threads share the same ObjectMapper instance without issue, because no configuration changes are being made after initialisation. Jackson allows this pattern and encourages reuse for performance reasons.
3. Declaring ObjectMapper as a Static Field
In most applications, it is safe and recommended to declare a single, static, pre-configured ObjectMapper and reuse it throughout the application.
public class StaticMapperExample {
public static final ObjectMapper MAPPER = new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT);
public static void main(String[] args) throws Exception {
Person person = new Person("Thomas", 65);
String json = MAPPER.writeValueAsString(person);
System.out.println(json);
}
static class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
}
This approach ensures configuration happens once, during application startup, and the instance is safely reused across threads without reinitialising caches or modules.
4. Alternative: Using Dependency Injection
In frameworks such as Spring Boot, Micronaut, or Jakarta EE, a single, globally available ObjectMapper can be defined as a bean managed by the dependency injection container. This eliminates the need for declaring a static field, as the framework handles the lifecycle and provides the instance wherever it is needed through injection, effectively serving the same purpose as a static instance but in a more controlled and modular way.
@Configuration
public class JsonMapperProvider {
@Bean
public ObjectMapper objectMapper() {
// You can add custom configurations here
return JsonMapper.builder()
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.build();
}
}
5. Best Practice: Configuring and Reusing Jackson’s ObjectMapper Safely
When working with Jackson’s ObjectMapper, it is essential to follow a few best practices to ensure optimal performance, maintainability, and thread-safety throughout your application. First, configure the ObjectMapper once during application startup. This includes registering any necessary modules, setting serialisation or deserialization features, or customising date formats. Doing this upfront avoids repeated configuration costs at runtime.
Once configured, reuse this instance either by declaring it as a static final field or, preferably, letting a Dependency Injection (DI) framework manage it as a singleton bean. This promotes consistency and eliminates the overhead of creating new instances repeatedly.
Finally, never modify the ObjectMapper configuration after it has been shared across threads. Changes like adjusting the date format or altering feature flags after concurrent use can lead to unpredictable behaviour, data corruption, or race conditions, especially in multi-threaded environments.
6. Conclusion
In this article, we explored the implications of using Jackson’s ObjectMapper as a static field in Java applications. We measured the performance differences between creating multiple instances versus reusing a single one, and demonstrated how to configure it effectively. Whether you choose to manage ObjectMapper statically or through a Dependency Injection framework like Spring, the key takeaway is to configure it once and reuse it consistently.
7. Download the Source Code
This article explored the use of a static field for the Java Jackson ObjectMapper.
You can download the full source code of this example here: Java Jackson ObjectMapper Static field

