Dear Support Team, Would you please help us about the memory performance issue We have a small project using spring boot application and we wanna got an issue related to memory size while on running my service on docker desktop the memory size increase after a many hours or during the load testing from Jmeter. We don't know what is the problem if it is related to spring boot version or JDK version or dockerfile or something else cz our code is small and easy
and kindly note that the bellow info needed to understand my issue :
Spring Boot V 3.2.1 JDK 19
One API : send a request with document size around 1MB Jmeter Report : Load testing for more than 1000 requests per 3 minutes.
MAT(Memory Analyzer Tool )
Memory Usage on Docker and JVM on Visual VM:
Docker File : FROM openjdk:19-alpine WORKDIR /usr/app COPY /target/*.jar app.jar EXPOSE 8090 8091
Set JVM options
ENTRYPOINT ["java", \ "-XX:+UseG1GC","-Xms16m","-Xmx256m","-Xss256k","-XX:MaxRAM=1024m","-XX:MinHeapFreeRatio=5"\ ,"-XX:MaxHeapFreeRatio=10","-XX:+TieredCompilation","-XX:TieredStopAtLevel=1","-XX:GCTimeRatio=4"\ ,"-XX:AdaptiveSizePolicyWeight=20","-XX:MaxMetaspaceSize=320m","-XX:MetaspaceSize=42m"\ ,"-XX:InitialRAMPercentage=75.0","-XX:MaxRAMPercentage=75.0","-XX:MinRAMPercentage=75.0"\ ,"-XX:+UnlockExperimentalVMOptions","-XX:-ShrinkHeapInSteps"\ ,"-Dspring.profiles.active=local"\ ,"-Dcom.sun.management.jmxremote=true"\ ,"-Dcom.sun.management.jmxremote.port=8091"\ ,"-Dcom.sun.management.jmxremote.local.only=false"\ ,"-Dcom.sun.management.jmxremote.authenticate=false"\ ,"-Dcom.sun.management.jmxremote.ssl=false"\ ,"-Dcom.sun.management.jmxremote.rmi.port=8091"\ ,"-Djava.rmi.server.hostname=localhost"\ ,"-jar","app.jar"]
pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.16</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>service-discovery</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>interceptor</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<artifactId>jwt-verifier</artifactId>
<groupId>com.qrdn.common</groupId>
</exclusion>
<exclusion>
<artifactId>data-storage</artifactId>
<groupId>com.qrdn.common</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>jwt-verifier</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>mnp-pdf</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>data-storage</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>db-connect</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>kafka-producer-log</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.qrdn.common</groupId>
<artifactId>qr-code</artifactId>
<version>1.0.0</version>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency> -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Comment From: bclozel
As discussed in https://github.com/spring-projects/spring-framework/issues/25619, there is no indication that there is a memory leak in Spring. Your screen captures show that the memory usage is not growing infinitely so I don't understand how you can infer a memory leak.
Please investigate with your application. We can reopen this issue if it turns out you can shows that there is a leak in Spring Framework.
Comment From: hassibnkhoury
on this photo you can see the memory usage is growing infinitely and increase after load testing 1 and load testing 2. Noting that between the both testing 1 and 2 we doesn't send any request.
Comment From: hassibnkhoury
and take MOT report in consideration while you can see they have leak issue related to 2 classes : org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.boot.loader.launch.LaunchedClassLoader
and as you see on the screenshot the memory increse from 32MB to 36MB
Comment From: bclozel
This points to a bean in your application leaking memory. Can you see which bean in the context is increasing the memory?
Comment From: hassibnkhoury
How can i see which bean in the context is increasing the memory if I am using spring boot? only I wanna create these annotation : @Service for each service needed to put data on DB using hibernate @Autowired to create singleton class from service @Configuration to configure main info
Comment From: bclozel
Sorry but we can't justify spending time helping you to investigate an application problem. You can drill down in your memory profiling tool to better understand where the problem comes from.
Comment From: hassibnkhoury
thanks a lot you can close this isue
Comment From: SimoneGiusso
Hello @bclozel,
I'm encountering problems with DefaultSingletonBeanRegistry
as well.
I found that our request scoped bean (name by spring as scopedTarget.myName
). created under @Configuraiton
annotation has 800k dependencies in DefaultSingletonBeanRegistry#dependenciesForBeanMap
. On the other side in DefaultSingletonBeanRegistry#dependentBeanMap
there are 800k entries. These 800k are jdk.proxy3.$Proxy as this is their name.
I'm not really sure how it is possible that this bean as so many bean dependencies. It seems that this data are not freed after GC runs. I found it odds because this bean has only 5 properties:
@Bean
@Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
Consumer consumer(HttpServletRequest request) {
return new RequestConsumer(request, uuid);
}
static class RequestConsumer implements Consumer {
private static final String
private static final String
private final HttpServletRequest;
private final MyCustomObject;
private final UUID;
}
Where myCustom contains some strings and UUID.
Are there any suggestions?
Comment From: bclozel
@SimoneGiusso not on the top of my head. If you manage to reproduce the problem with a minimal sample, please create a new issue and we'll have a look.
Comment From: SimoneGiusso
@SimoneGiusso not on the top of my head. If you manage to reproduce the problem with a minimal sample, please create a new issue and we'll have a look.
Just as a note (see also my message above) for others reading the conversation. We have encounter a leak which is really similar to what is explained here. The problem is present with Spring Boot 2.7.18
but not with Spring 3
apparently.
Enabling this logging:
logging:
level:
org.springframework.beans: DEBUG
Will confirm that, every time a request is made, the following is logged:
Autowiring by type from bean name 'scopedTarget. consumer' via factory method to bean named 'jdk.proxy2.$Proxy439@36c72b3c'
The cause of the leak seems apparently odd. The usage of ApplicationEventPublisher
at some point of the code (not in consumer) triggers the leak and the leak is anyway happening before the usage of ApplicationEventPublisher
. We noticed that commenting the line that uses ApplicationEventPublisher
doesn't trigger the leak. I know it sounds odd.
Our workaround was modifying the code above:
@Bean
@Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
Consumer consumer() { // Here the change
return new RequestConsumer(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), uuid);
}
static class RequestConsumer implements Consumer {
private static final String
private static final String
private final HttpServletRequest;
private final MyCustomObject;
private final UUID;
}
So that the HttpServletRequest
is not autowired everytime a request is made with a different name. Causing:
/** Map between dependent bean names: bean name to Set of dependent bean names. */
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/** Map between depending bean names: bean name to Set of bean names for the bean's dependencies. */
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
in DefaultSingletonBeanRegistry
to grow indefinitely.