How to reduce Spring memory footprint

Kousalik picture Kousalik · Mar 5, 2014 · Viewed 17.2k times · Source

I would like to ask you HOW (or IF) is possible to reduce Spring framework's RAM footprint.

I created a simple helloworld app for demonstrating the issue. There are only two classes and context.xml file:

  • Main - class with main method
  • Test - class used for simulating some "work" (printig Hello in endless loop)

context.xml contains only this:

<context:component-scan base-package="mypackage" />

Test class contains only metod called init, called after construction:

@Component
public class Test{

    @PostConstruct
    public void init() {
        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    while (true) {
                        System.out.println("Hello " + Thread.currentThread().getName());
                        Thread.sleep(500);
                    }
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        });
        t.start();
    } 
}

I prepared two scenarios and in both of them main method contains only one line.

In first scenario main method does this: (new Test()).init(); App works without Spring and consumes only aprox. 8MB of RAM.

In second scenario main method contains following: new ClassPathXmlApplicationContext(new String[]{"spring/context.xml"}); So the app is initialized throug Spring container and consumes aprox. 45MB of RAM !

Is there any way how to reduce (in best case completely get rid of) this extra memory ? So far I wasn't able to find any fitting solution.

I don't mind if there is the extra memory consumption on startup - this is perfectly fine, but after that, I need our app to reduce it.

(The story behind this question is a bit more complicated, but this is for me now the core problem.)

Thank you

Answer

ARRG picture ARRG · Mar 5, 2014

First a few things which you may already know but are important to understand the situation: most of the memory your program uses is the JVM heap. The heap has an initial size when your program starts executing. Sometimes the JVM will ask the OS for more memory and increase the size of the heap. The JVM will also perform garbage collection, thus freeing space in the heap.

When the used heap size diminishes, memory is not necessarily released by the JVM. The oracle JVM is reluctant to do so. For example if your program uses 500 MB of ram on startup, then after garbage collection only uses 100 MB for most of its execution, you cannot assume the additional 400 MB will be given back to your OS.

Furthermore, tools like the windows task manager (and I assume unix's ps) will display the size of all the memory that is allocated to the JVM, regardless of whether this memory is actually used or not. Tools like jvisualvm will let you see exactly how the memory is used in your java program, and in particular what amount of heap you're actually using vs. how much is allocated.


With this in mind I tested your program in the following scenarios. Note that this depends on many factors including which JVM you use, its version, and probably your OS too.

  • Standard java (SE) vs Spring.
  • Garbage collection (GC) vs none (NOGC) (I called the garbage collector from jvisualvm).
  • Minimum heap size defined for the JVM (using -Xmx8M). This tells the JVM to only allocate 8MB on startup. The default on my system is 256MB.

For each case I report the allocated heap size and used heap size. Here are my results:

  • SE, NOGC, 256M : 270 MB allocated, 30 MB used
  • Spring, NOGC, 256M : 270 MB allocated, 30 MB used

These results are identical, so from your question I assume you already had a different environment than I did.

  • SE, GC, 256M : 270 MB allocated, 9 MB used

Using the GC reduces heap usage, but we still have the same allocated memory

  • SE, NOGC, 8M : 9 MB allocated, <5 MB used
  • Spring, NOGC, 8M : 20 MB allocated, <5 MB used

This is the most important result: more memory is allocated because Spring probably needs more at some point during startup.

Conclusions:

  • If you're trying to reduce heap usage, using spring should not be much of a problem. The overhead is not gigantic.
  • If you're trying to reduce allocated memory, the price of using Spring, in this experiment, is steeper. But you can still configure your JVM so that it will free memory more often than the default. I don't know much about this, but jvm options such as -XX:MaxHeapFreeRatio=70 might be a start (more here http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html#PerformanceTuning )