As I noticed crazy high RAM usage on my client application (Swing-based), I started looking into it and it seems like this is somehow related to Annotation-based configuration in Spring. As you will see in my edits below, i realized that this occurs only on 64-Bit JVM.
See the following testcode:
xml-based configuration
<beans ....>
<bean id="xmlConfigTest" class="at.test.XmlConfigTest" />
</beans>
public class XmlConfigTest extends JFrame {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("config/applicationContext.xml");
XmlConfigTest frame = (XmlConfigTest) ctx.getBean("xmlConfigTest");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Uses up about 32MB memory, which seems ok to me.
Now the same with annotation based configuration:
@Service
public class AnnotationConfigTestFrame extends JFrame {
public static void main(String[] args) throws InterruptedException {
ApplicationContext ctx = new AnnotationConfigApplicationContext("at.test");
AnnotationConfigTestFrame frame = (AnnotationConfigTestFrame) ctx
.getBean("annotationConfigTestFrame");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Not only takes it noticeable longer to open the frame, but the memory consumption sky-rockets to 160MB memory on startup and then levels off at about 152MB, which seems really high to me. And remember, this is only the most basic case, the client application i develope atm eats up over 400MB already, which is just too much for older machines.
Does anyone have an explanation for this behaviour? I don't understand..
(Using 3.1.1.RELEASE here btw.)
edit* As suggested by axtavt, i also tried to construct the AnnotationConfigApplicationContext directly with the Test-Class as Argument, so that no classpath-scan is necessary. Didn't changed anything about the memory consumption unfortunately.
edit 2 removed, see edit 3
edit 3 I now tested on the same machine (Windows 7 64-Bit) with both 32-Bit and 64-Bit JVM and the test-programms from above. This are the results:
xml based configuration:
32-Bit JVM: 16MB
64-Bit JVM: 31MB
annotation bassed configuration:
32-Bit JVM: 17MB
64-Bit JVM: 160MB
So on 32-Bit JVM both proramms are close, which is pretty much what i would expect. On 64-Bit though, this is different. Even the first programm uses twice as much memory on 64-Bit, which already seems to be too much. Still it's nothing against the second program, which uses nearly 10 times more memory on 64-Bit.
edit 4 Now tested under ubuntu too -> same effect. Still no idea why this is happening though. This is really a dealbreaker for me
You can get a simple Spring Boot app down to around 72M total by using the following JVM options. With -XX:MaxRAM=72m This will restrict the JVM's calculations for the heap and non heap managed memory to be within the limits of this value.
Spring Boot application memory soars The result is that each service startup takes up 1.2G-2G of memory, and some services can't use that much at all. So, how is the JVM memory configured in Spring Boot if the JVM memory parameter is not set?
At startup a large number of java.lang.reflect.Method
objects are created.
These objects are eligible for garbage collection, but in the case of your application it probably causes too many eden collections which results in high startup times.
Most of these java.lang.reflect.Method
objects are allocated at the following site:
These seem to be created when Spring tries to find setters on AnnotationConfigTestFrame
which inherits lots of methods from java.awt
and javax.swing
super classes. I did not read through the relevant code closely, but as a quick test to verify this hypothesis, I did the following:
@Service
public class AnnotationConfigTestFrame /* extends JFrame */
{
public static void main(String[] args) throws InterruptedException
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(AnnotationConfigTestFrame.class);
AnnotationConfigTestFrame frame = (AnnotationConfigTestFrame) ctx
.getBean("annotationConfigTestFrame");
// frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
// frame.setVisible(true);
waitABit();
printRuntimeStats();
System.exit(0);
}
}
i.e. made AnnotationConfigTestFrame
not inherit from javax.swing.JFrame
. Now the memory usage for looking up the bean is reasonably low!
This might give you hints for debugging this further.
The way you construct your AnnotationConfigApplicationContext
(providing a base package of your annotated classes) requires classpath scanning, therefore there is no surprise that it takes time and memory.
If you want to avoid classpath scanning, you can try to provide exact set of annotated classes (@Component
s and @Configuration
s) instead, using the corresponding constuctor of AnnotationConfigApplicationContext
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With