Software Development

Avoiding memory leaks with Spring Boot WebClient | bol.com


Using the dominator tree we were able to easily browse through the hashmap’s contents. In the above picture we opened two hashmap nodes. Here we see a lot of micrometer timers tagged with “v2/products/…” and a product id. Hmm, where have we seen that before?

What does WebClient have to do with this?

So, it’s Spring Boot’s metrics that are responsible for this memory leak, but what does WebClienthave to do with this? To find that out you really have to understand what causes Spring’s metrics to store all these timers.

Inspecting the implementation of AutoConfiguredCompositeMeterRegistrywe see that it stores the metrics in a hashmap named meterMap. So, let’s put a well-placed breakpoint on the spot where new entries are added and trigger our suspicious call our WebClientperforms to the “v2/product/{productId}” endpoint.

We run the application again and … Gotcha! For each call the WebClientmakes to the “v2/product/{productId}” endpoint, we saw Spring creating a new Timerfor each unique instance of product identifier. Each such timer is then stored in the AutoConfiguredCompositeMeterRegistry bean. That explains why we see so many timers with tags like these:

/v2/products/9200000109074941 /v2/products/9200000099621587

How can you fix this memory leak?

Before we identify when this memory leak might affect you, let’s first explain how one would fix it. We’ve mentioned in the introduction, that by simply not using a URI builder to construct WebClient URLs, you can avoid this memory leak. Now we will explain why it works.

After a little online research we came across this post (https://rieckpil.de/expose-metrics-of-spring-webclient-using-spring-boot-actuator/) of Philip Riecks, in which he explains:

“As we usually want the templated URI string like “/todos/{id}” for reporting and not multiple metrics e.g. “/todos/1337” or “/todos/42″ . The WebClient offers several ways to construct the URI […], which you can all use, except one.”

And that method is using the URI builder, coincidentally the one we’re using: