Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do -Xmx and Runtime.maxMemory not agree

When you add

 -Xmx????m 

to the command line, the JVM gives you a heap which is close to this value but can be out by up to 14%. The JVM can give you a figure much closer to what you want, but only through trial and error.

 System.out.println(Runtime.getRuntime().maxMemory()); 

prints

-Xmx1000m ->  932184064 -Xmx1024m -Xmx1g ->  954728448 -Xmx1072m ->  999292928 -Xmx1073m -> 1001390080 

I am running HotSpot Java 8 update 5.

Clearly, the heap can be something just above 1000000000 but why is this -Xmx1073m instead of say -Xmx1000m?

BTW 1g == 1024m which suggests that 1g should be 1024^3 which is 7% higher than 1000^3 but you get something 7% lower than 1000^3.


Being off by so much suggests that I am missing something fundamental about how the heap works. If I asked for -Xmx1000m and it was 1001390080 I wouldn't care, I would assume there is some allocation multiple it needs to adhere to, but to give you 932184064 suggests to me the heap is more complicated than I can imagine.


EDIT I have found that

-Xmx1152m gives 1073741824 which is exactly 1024^3 

so it appears it is giving me exactly 128 MB less than I asked for in this case cf the maxMemory().


BTW 128 is my favourite number. I was in a conference today at street number 128 and the speaker quoted a book from page 128 ;)

like image 692
Peter Lawrey Avatar asked May 16 '14 17:05

Peter Lawrey


People also ask

Why does Java use so much memory?

Java is also a very high-level Object-Oriented programming language (OOP) which means that while the application code itself is much easier to maintain, the objects that are instantiated will use that much more memory.

How to get Runtime object in Java?

getRuntime() method returns the runtime object associated with the current Java application. Most of the methods of class Runtime are instance methods and must be invoked with respect to the current runtime object.

What is Runtime instance?

Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method. An application cannot create its own instance of this class.

What are Runtime classes in Java?

Runtime class is a subclass of Object class, can provide access to various information about the environment in which a program is running. The Java run-time environment creates a single instance of this class that is associated with a program.


1 Answers

The difference appears to be accounted for by the size of the garbage collector's survivor space.

The -Xmx flag, as described in the docs, controls maximum size of the memory allocation pool. The heap portion of the memory allocation pool is divided into Eden, Survivor, and Tenured spaces. As described in this answer, there are two survivor regions, only one of which is available to hold live objects at any given point in time. So the total apparent space available for allocating objects, as reported by Runtime.maxMemory(), must subtract the size of one of the survivor spaces from the total heap memory pool.

You can use the MemoryMXBean and MemoryPoolMXBean classes to get a little more information about your memory allocation. Here's a simple program I wrote:

import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean;  public class MemTest {   static String mb (long s) {     return String.format("%d (%.2f M)", s, (double)s / (1024 * 1024));   }    public static void main(String[] args) {     System.out.println("Runtime max: " + mb(Runtime.getRuntime().maxMemory()));     MemoryMXBean m = ManagementFactory.getMemoryMXBean();      System.out.println("Non-heap: " + mb(m.getNonHeapMemoryUsage().getMax()));     System.out.println("Heap: " + mb(m.getHeapMemoryUsage().getMax()));      for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) {       System.out.println("Pool: " + mp.getName() +                           " (type " + mp.getType() + ")" +                          " = " + mb(mp.getUsage().getMax()));     }   } } 

The output of this on OpenJDK 7 for java -Xmx1024m MemTest is:

Runtime max: 1037959168 (989.88 M) Non-heap: 224395264 (214.00 M) Heap: 1037959168 (989.88 M) Pool: Code Cache (type Non-heap memory) = 50331648 (48.00 M) Pool: Eden Space (type Heap memory) = 286326784 (273.06 M) Pool: Survivor Space (type Heap memory) = 35782656 (34.13 M) Pool: Tenured Gen (type Heap memory) = 715849728 (682.69 M) Pool: Perm Gen (type Non-heap memory) = 174063616 (166.00 M) 

Note that Eden + 2*Survivor + Tenured = 1024M, which is exactly the amount of heap space requested on the command line. Much thanks to @Absurd-Mind for pointing this out.

The differences you observe between different JVMs are likely due to differing heuristics for selecting the default relative sizes of the various generations. As described in this article (applies to Java 6, wasn't able to find a more recent one), you can use the -XX:NewRatio and -XX:SurvivorRatio flags to explicitly control these settings. So, running the command:

java -Xmx1024m -XX:NewRatio=3 -XX:SurvivorRatio=6 

You're telling the JVM that:

Young:Tenured = (Eden + 2*Survivor):Tenured = 1:3 = 256m:768m Survivor:Eden = 1:6 = 32m:192m 

So, with these parameters, the difference between the requested -Xmx value and the available memory reported by Runtime.maxMemory() should be 32m, which is verified using the above program. And now you should be able to accurately predict the available memory reported by Runtime for a given set of command-line arguments, which is all you ever really wanted, right?

like image 159
Alex Avatar answered Oct 18 '22 20:10

Alex