Core Java

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 ObjectMapper instances.
  • Then, it measures the time to reuse a single static ObjectMapper for 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.

Download
You can download the full source code of this example here: Java Jackson ObjectMapper Static field

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button