Serialization Optimizations in Java applications
Table of Contents
Serialization Optimizations
The Surprising CPU Saver.
Profiling showed that 15% of CPU time was spent in Jackson serialization. Switch to a more efficient configuration.
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// Use afterburner module for faster serialization
mapper.registerModule(new AfterburnerModule());
// Only include non-null values
mapper.setSerializationInclusion(Include.NON_NULL);
// Disable features we don't need
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return mapper;
}
}
For our most performance-critical endpoints, I replaced Jackson with Protocol Buffers:
syntax = "proto3";
package com.example.proto;
message ProductResponse {
int64 id = 1;
string name = 2;
string description = 3;
double price = 4;
int32 inventory = 5;
}
@RestController
@RequestMapping("/api/products")
public class ProductController {
// Jackson-based endpoint
@GetMapping("/{id}")
public Mono<ResponseEntity<Product>> getProduct(@PathVariable Long id) {
// Original implementation
}
// Protocol buffer endpoint for high-performance needs
@GetMapping("/{id}/proto")
public Mono<ResponseEntity<byte[]>> getProductProto(@PathVariable Long id) {
return service.getProductById(id)
.map(product -> ProductResponse.newBuilder()
.setId(product.getId())
.setName(product.getName())
.setDescription(product.getDescription())
.setPrice(product.getPrice())
.setInventory(product.getInventory())
.build().toByteArray())
.map(bytes -> ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(bytes));
}
}
This change reduced serialization CPU usage by 80% and decreased response sizes by 30%