Instantiating ObjectMapper on every call is one of the most common Java performance mistakes. Learn why it's expensive, how to detect it, and the correct singleton pattern.
JOptimize Team
Creating a new ObjectMapper() inside a method is one of the most widespread - and most costly - performance anti-patterns in Java. ObjectMapper initialization is expensive: it scans classpath modules, registers serializers, and builds internal caches. Do it on every request and you're wasting CPU on setup work that should happen exactly once.
// ANTI-PATTERN - seen constantly in production codebases @Service public class UserService { public String serializeUser(User user) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); // Created on EVERY call return mapper.writeValueAsString(user); } public User deserializeUser(String json) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); // Again! return mapper.readValue(json, User.class); } }
At 100 requests/second, this creates 100+ ObjectMapper instances per second. Each one rebuilds the entire Jackson module registry, registers default serializers, and allocates significant heap space. This:
ObjectMapper initialization does non-trivial work:
jackson-modules via ServiceLoaderTypeFactory and registers all default (de)serializersSerializationFeature and DeserializationFeature settingsA quick benchmark shows ObjectMapper instantiation takes ~2-5ms on a warm JVM. At 500 RPS with two JSON operations per request, that's 2-5 seconds of pure overhead per second - just from mapper creation.
ObjectMapper is thread-safe for read operations after configuration. The correct pattern is one instance per application:
Option 1: Inject Spring Boot's auto-configured ObjectMapper
Spring Boot auto-configures an ObjectMapper bean with all the right settings (JavaTimeModule, etc.). Just inject it:
@Service @RequiredArgsConstructor public class UserService { private final ObjectMapper objectMapper; // Spring-managed singleton ? public String serializeUser(User user) throws JsonProcessingException { return objectMapper.writeValueAsString(user); } public User deserializeUser(String json) throws JsonProcessingException { return objectMapper.readValue(json, User.class); } }
This is the simplest and recommended approach for Spring Boot applications.
Option 2: Declare your own @Bean
If you need custom configuration:
@Configuration public class JacksonConfig { @Bean @Primary public ObjectMapper objectMapper() { return JsonMapper.builder() .addModule(new JavaTimeModule()) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) .enable(MapperFeature.DEFAULT_VIEW_INCLUSION) .build(); } }
Option 3: Static singleton for non-Spring code
public final class JsonUtils { private static final ObjectMapper MAPPER = JsonMapper.builder() .addModule(new JavaTimeModule()) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .build(); private JsonUtils() {} public static String toJson(Object obj) throws JsonProcessingException { return MAPPER.writeValueAsString(obj); } public static <T> T fromJson(String json, Class<T> type) throws JsonProcessingException { return MAPPER.readValue(json, type); } }
ObjectMapper is safe to share across threads as long as you don't reconfigure it after initialization. The rule:
// SAFE - reading/writing JSON concurrently String json1 = mapper.writeValueAsString(obj1); // thread 1 String json2 = mapper.writeValueAsString(obj2); // thread 2 (concurrent) ? // UNSAFE - modifying configuration after construction mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // in a request handler ?
If you need per-request configuration, use ObjectWriter or ObjectReader (which are immutable and thread-safe):
// Thread-safe per-view serialization ObjectWriter writer = objectMapper.writerWithView(UserViews.Public.class); String json = writer.writeValueAsString(user); // ?
new ObjectMapper() in a @Bean method without @Bean scope control - if the method is called multiple times, you get multiple instances; always annotate with @Beanmapper.configure() after the bean is shared - this modifies global state and causes race conditions; configure in the constructor or builder onlyObjectMapper for String ? Map conversions repeatedly - consider TypeReference caching: private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {}JsonProcessingException - wrapping it in a generic RuntimeException loses context; use a proper error handler or @ControllerAdviceObjectMapper is expensive to create and safe to share - always use it as a singleton. In Spring Boot, simply inject the auto-configured ObjectMapper bean. If you need custom config, declare a @Bean. Never instantiate it inside a method or per-request. This one change can eliminate significant CPU and GC overhead in JSON-heavy services.
JOptimize scans your codebase for new ObjectMapper() inside methods and service classes, flagging every instance with a suggested refactoring to inject the singleton.
new ObjectMapper() in methods and generates an injection-based fix with one click: Install JOptimize for IntelliJEliminate unnecessary ObjectMapper instantiation and reduce CPU overhead - free scan, no configuration required.
Master Spring Boot, security, and Java performance with hands-on courses.
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.