Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring annotation-based configuration - memory consumption too high?

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

like image 558
Mario B Avatar asked Jul 19 '12 06:07

Mario B


People also ask

How do I reduce spring boot memory usage?

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.

How much memory does a spring boot application use?

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?


2 Answers

At startup a large number of java.lang.reflect.Method objects are created.

heap dump

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:

allocation sites for java.lang.reflect.Method objects

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.

like image 88
Binil Thomas Avatar answered Oct 28 '22 23:10

Binil Thomas


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 (@Components and @Configurations) instead, using the corresponding constuctor of AnnotationConfigApplicationContext.

like image 43
axtavt Avatar answered Oct 29 '22 01:10

axtavt