Home > Software design >  How can I fix memory leak in Spring Scheduled function?
How can I fix memory leak in Spring Scheduled function?

Time:12-31

I have a strange memory leak in the java application that uses Spring 3.0 the java class has this structure

@EnableAsync
@Component
public class ScheduleFixedRate {

SellerRepository sellerRepository;
SellerProfileRepository sellerProfileRepository;
StatsRepository statsRepository;

public ScheduleFixedRate(SellerRepository sellerRepository, SellerProfileRepository sellerProfileRepository, StatsRepository statsRepository) {
        this.sellerRepository = sellerRepository;
        this.sellerProfileRepository = sellerProfileRepository;
        this.statsRepository = statsRepository;
    }


    public Builder webClientBuilder(String baseUrl) {
        boolean secure = baseUrl.contains("https");

        HttpClient httpClient = HttpClient.create().followRedirect(true);
        if (secure) {
            httpClient = httpClient.secure(sslContextSpec -> sslContextSpec.sslContext(createInsecureSslContext()));
        }
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .baseUrl(baseUrl);
    }

    private SslContext createInsecureSslContext() {
        try {
            return SslContextBuilder
                    .forClient()
                    .trustManager(InsecureTrustManagerFactory.INSTANCE)
                    .build();
        } catch (SSLException e) {
            throw new RuntimeException(e);
        }
    }

@Scheduled(fixedRate = 60000)
public void updateOrdersList() {
       WebClient client = webClientBuilder("https://web.com:443").build();
                HttpMethod method = HttpMethod.valueOf("GET");
                RequestBodySpec requestBodySpec = client.method(method).uri("/api***");
                requestBodySpec.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36");
                StringBuilder response = new StringBuilder();

                try {
                    response.append(requestBodySpec.exchangeToMono(clientResponse -> {
                        if (clientResponse.statusCode().is4xxClientError()) {
                            return Mono.error(new Throwable());//Mono.empty();
                        }
                        return clientResponse.bodyToMono(String.class);
                    }).block(Duration.ofSeconds(5)));
                } catch (Exception ex) {
                    System.out.println(ex.getMessage());
                }
       // doing some manipulation with response
sellerRepository.save(data);
 }
}

Within one day, I saw that my app will start using an additional 1.5GB of memory. I saw that the spring is creating many scheduler threads in the memory. Please see the images below (the first image is after 10 minutes of starting the application, and the second image is after one day)

10 minutes

1 day Could you help me with what's wrong?

CodePudding user response:

Hard to know what is going on without seeing more code, but I would look into EntityManager.clear() if you have long living threads that handle many entities.

CodePudding user response:

You can try something below.

WebClientConfig

@Configuration
class WebClientConfig {

    @Value("{$base.url}")
    private String baseUrl;

    @Bean
    WebClient webClientBuilder(String baseUrl) {
        boolean secure = baseUrl.contains("https");

        HttpClient httpClient = HttpClient.create().followRedirect(true);
        if (secure) {
            httpClient = httpClient.secure(sslContextSpec -> sslContextSpec.sslContext(createInsecureSslContext()));
        }
        return WebClient.builder()
                        .clientConnector(new ReactorClientHttpConnector(httpClient))
                        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .baseUrl(baseUrl)
                        .build();
    }

    private SslContext createInsecureSslContext() {
        try {
            return SslContextBuilder
                    .forClient()
                    .trustManager(InsecureTrustManagerFactory.INSTANCE)
                    .build();
        }
        catch (SSLException e) {
            throw new RuntimeException(e);
        }
    }
}

Scheduler Code:

@EnableAsync
@Component
public class ScheduleFixedRate {

    private final SellerRepository sellerRepository;
    private final SellerProfileRepository sellerProfileRepository;
    private final StatsRepository statsRepository;
    private WebClient webClient;

    public ScheduleFixedRate(SellerRepository sellerRepository,
            SellerProfileRepository sellerProfileRepository,
            StatsRepository statsRepository,
            WebClient webClient) {
        this.sellerRepository = sellerRepository;
        this.sellerProfileRepository = sellerProfileRepository;
        this.statsRepository = statsRepository;
        this.webClient = webClient;
    }

    @Scheduled(fixedRate = 60000)
    public void updateOrdersList() {
        String response = webClient
                .get()
                .uri("/api***")
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36")
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError, res -> Mono.error(new Throwable()))
                .bodyToMono(String.class)
                .block();

        // doing some manipulation with response
        sellerRepository.save(response);
    }
}
  • Related