JavaBlog.fr / Java.lu DEVELOPMENT,Java Java: JVM Memory Parameters, OutOfMemoryError and Memory checker

Java: JVM Memory Parameters, OutOfMemoryError and Memory checker

Hello,

I would update my previous article concerning a solution in order to avoid the famous OutOfMemoryError: Java heap space or OutOfMemoryError: PermGen space errors.

This error java.lang.OutOfMemoryError in Java is a subclass of java.lang.VirtualMachineError which is thrown by the Java Virtual Machine when it ran out of memory in heap or it cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector, for example:

  • when not enough memory can be allocated for a new array;
  • when not enough memory is available for your class to load;
  • when too many threads / not enough stack space available for a new thread.

However, it is possible to avoid this error by verifying the memory status of JVM. So, we have created a simple technical component Memory Checker whose the role is to check the available memory space in VM for the execution of Java code in order to avoid these “out of memory” errors. If there is enough memory, the current thread will be put on hold for the specified delay (between 2 retries).
 


 
As a reminder:

  • PermGen is the memory dedicated to the permanent objects in the VM (Class names, internalized strings, objects that will never get garbage-collected). So the -XX:PermSize=128M parameter corresponds to the memory reserved at the application server starting time. The -XX:MaxPermSize parameter corresponds to the maximum memory usable by the server before swaping.
  • -Xms1024m corresponds to the memory reserved at the application server starting time for the non permanent (volatile) objects in the VM.
  • -Xmx4096m corresponds to the maximum memory usable by the server for the non permanent (volatile) objects in the VM, if this limit is reached, the server persists the data on disk via swaping.

…so, at the application server starting time, the memory reserved will be the total of -XX:PermSize and -Xms parameters.
 


 
I. Memory Checker
So, we will create a class named MemoryChecker:

01public class MemoryChecker {
02    // Number of tests of memory allocation
03    // for treatment before returning an OutOfMemory error
04    private static int NB_OF_RETRY = 5;
05     
06    // If there is enough memory,
07    // the thread will be put on hold
08    // for the specified delay (between 2 retries).
09    private static int WAITING_DELAY_MS = 500;
10 
11//...
12}

…this class will contain a private static method logMemoryStatusInConsole which will print the memory status in console, eg the total amount of memory in the JVM, the maximum amount of memory that the JVM will attempt to use and the amount of free memory in the JVM:

01/**
02 * Print the memory status in console
03 * @param threadName
04 */
05private static void logMemoryStatusInConsole(String threadName){
06    /*
07        Used_now = Total_now - Free_now
08        Free_real = Max - Used_now
09        Max => parameter JVM '-Xmx' (ex: -Xmx1G)
10        Total => parameter JVM '-Xms' (ex: -Xms1G)
11        The total memory is reserved at the application server starting time.
12        Blocking condition to execute the java main:  maxMemory >= totalMemory
13     */
14 
15    Runtime rt = Runtime.getRuntime();
16         
17    // Returns the total amount of memory in the Java virtual machine.
18    // parameter Xms ; (ex: -Xms1G)
19    long totalNow = rt.totalMemory();
20 
21    // Returns the maximum amount of memory that the Java virtual machine will attempt to use
22    // parameter Xmx ; (ex: -Xmx1G)
23    long maxPossible = rt.maxMemory();
24 
25    // Returns the amount of free memory in the Java Virtual Machine.
26    long freeNow = rt.freeMemory();
27         
28    long usedNow = totalNow - freeNow;
29    long freeRealPossible = maxPossible - usedNow;
30         
31    //
32    System.out.println(threadName + " : " + "memorybroker : free "+ freeRealPossible/1024 + "ko : (total/max) " + totalNow/1024 + "ko/" + maxPossible/1024+"ko");
33}

…this class will contain a public static method checkMemoryAndIfNecessaryPutThreadOnHold which will check if there is enough memory to process the given number of bytes. If there is enough memory, the current thread will be put on hold for the specified delay.

01{
02    int nbLoop = NB_OF_RETRY;
03             
04    while(nbLoop>0){
05        long freeNow = Runtime.getRuntime().freeMemory(); //
06        long totalNow = Runtime.getRuntime().totalMemory(); // parameter Xms
07        long maxPossible = Runtime.getRuntime().maxMemory(); // parameter Xmx
08        long usedNow = totalNow - freeNow;
09        long freeRealPossible = maxPossible - usedNow;
10                     
11        if(freeRealPossible >= memorySize){
12            // At this stage, the amount of memory required is available
13            break;
14                         
15        }else{
16            System.out.println(threadName + " : " + memorySize/1024 + "ko : Not enough memory for the current thread, try again in " + WAITING_DELAY_MS + " ms");
17            try{
18                // Sleep the current thread
19                Thread.sleep(WAITING_DELAY_MS);
20            }catch(Throwable e1){
21            }//end-try
22            nbLoop--;
23        }
24    }//end-while
25 
26 
27    if(0 == nbLoop){
28        System.out.println(threadName + " : " + memorySize/1024 + "ko : Not enough memory for the current thread despite testing, so the propagation of OutOfMemoryError will be done.");
29        // Print the memory status in console
30        logMemoryStatusInConsole(threadName);
31        throw new OutOfMemoryError();
32    }//end-if
33 
34}//end-block

memorycheckerJVM
 


 
II. Test Memory Checker
We will create a main method in order to simulate multiple threads which call load a file (size of bytes) into memory Java (JVM):

01// Number of threads
02int nbThreads = 10;
03 
04// Memory needed for each thread
05final int memorySizeNeeded = 300000*1024;
06 
07// Creation of threads
08ExecutorService exec = Executors.newFixedThreadPool(nbThreads);
09for (int i = 0; i < nbThreads; i++) {
10    final int valueI = i;
11             
12    Runnable runnable = new Runnable(){
13        private String threadName = null;
14        public void run(){
15            this.threadName = "thread_"+valueI;
16                     
17            try{
18                System.out.println(threadName + ":-- START --");
19 
20                // Memory status before the memory checking.
21                System.out.println(threadName + " : " + "Memory status before the memory checking");
22                logMemoryStatusInConsole(threadName);
23 
24                // Memory checking AND bo the business
25                {
26                    // Memory checking
27                    checkMemoryAndIfNecessaryPutThreadOnHold(memorySizeNeeded, threadName);
28                    // Business
29                    byte[] tab = new byte[memorySizeNeeded];
30                }
31 
32                logMemoryStatusInConsole(threadName);
33                System.out.println(threadName + ":-- END --");
34                         
35            }catch(Throwable th){
36                System.out.println(threadName + ":-- ERROR --" + th);
37            }//end-try
38        }//end-run
39    };
40    //end-Runnable
41    exec.submit(runnable);//end-submit
42}//end-for

Test n°1
With 2 threads and a 300Mo for the memory needed fo each thread:

1// Number of threads
2int nbThreads = 2;
3 
4// Memory needed for each thread
5final int memorySizeNeeded = 300000*1024;

… results: there is not enough of memory for all 2 threads, so, an error OutOfMemoryError is thrown:

01thread_0:-- START --
02thread_1:-- START --
03thread_1 : Memory status before the memory checking
04thread_1 : memorybroker : free 252893ko : (total/max) 15872ko/253440ko
05thread_1 : 300000ko : Not enough memory for the current thread, try again in 500 ms
06thread_0 : Memory status before the memory checking
07thread_0 : memorybroker : free 252893ko : (total/max) 15872ko/253440ko
08thread_0 : 300000ko : Not enough memory for the current thread, try again in 500 ms
09thread_1 : 300000ko : Not enough memory for the current thread, try again in 500 ms
10thread_0 : 300000ko : Not enough memory for the current thread, try again in 500 ms
11thread_1 : 300000ko : Not enough memory for the current thread, try again in 500 ms
12thread_0 : 300000ko : Not enough memory for the current thread, try again in 500 ms
13thread_1 : 300000ko : Not enough memory for the current thread, try again in 500 ms
14thread_0 : 300000ko : Not enough memory for the current thread, try again in 500 ms
15thread_1 : 300000ko : Not enough memory for the current thread, try again in 500 ms
16thread_0 : 300000ko : Not enough memory for the current thread, try again in 500 ms
17thread_1 : 300000ko : Not enough memory for the current thread despite testing, so the propagation of OutOfMemoryError will be done.
18thread_1 : memorybroker : free 252893ko : (total/max) 15872ko/253440ko
19thread_0 : 300000ko : Not enough memory for the current thread despite testing, so the propagation of OutOfMemoryError will be done.
20thread_0 : memorybroker : free 252893ko : (total/max) 15872ko/253440ko
21thread_0:-- ERROR --java.lang.OutOfMemoryError
22thread_1:-- ERROR --java.lang.OutOfMemoryError

Test n°2
With 2 threads and a 300ko for the memory needed fo each thread:

1// Number of threads
2int nbThreads = 2;
3 
4// Memory needed for each thread
5final int memorySizeNeeded = 300*1024;

… results: there is enough of memory for all 2 threads, so, no error OutOfMemoryError thrown:

01thread_0:-- START --
02thread_1:-- START --
03thread_1 : Memory status before the memory checking
04thread_1 : memorybroker : free 252893ko : (total/max) 15872ko/253440ko
05thread_1 : memorybroker : free 252593ko : (total/max) 15872ko/253440ko
06thread_1:-- END --
07thread_0 : Memory status before the memory checking
08thread_0 : memorybroker : free 252593ko : (total/max) 15872ko/253440ko
09thread_0 : memorybroker : free 252293ko : (total/max) 15872ko/253440ko
10thread_0:-- END --

III. Conclusion
This MemoryChecker class must be used for sensitive areas of an application (created thread pool, webservices, servlets, file handling…). These “sensitive” points can be targeted via tools like jconsole and visualvm that monitor the activity (eg total number of thread) of JVM on the real-time, which can be a big help.

But, the number of threads in a JVM is not infinitely expandable, so, if the application needs a very large number of thread to meet demand, several options should be considered:

  1. First solution: the least expensive change, modify (if possible) the Xmx (and/or Xss) parameters of your server in order to may create more threads. Then, it is well advised to re-test your application.
  2. Second solution: modify the physical and/or logical architecture of your application:
    • If you are on 32bit machines, switch to 64 bits, so as to extend the size limit of the process;
    • Modify your application so that you can add more instances of the JVM. Make the application more scaleable horizontally;
    • Turn (if possible) your synchronous processes into asynchronous processes in order to better manage your resources and minimize your wait time threads.

Source: MemoryChecker.java

That’s all!!!

Huseyin OZVEREN

Leave a Reply

Your email address will not be published.

Time limit is exhausted. Please reload CAPTCHA.

Related Post