Search code examples
javamemorygarbage-collectionjvmheap-memory

Heap size is greater than -Xmx


I set the initial and maximum Java heap size to 256MB using -Xms256m and -Xmx256m JVM options. The GC log (using -XX:+PrintHeapAtGC) states that the heap size is 251904K (246MB), which is smaller than the minimum heap size, -Xms256m (see last line of log). However, this is because the stated heap size is the available heap size, which does not include the unavailable from space.

When I manually include the unavailable from space memory, the derived heap size is 262656K (256.5MB), which is slightly larger than the maximum heap size, -Xmx (by 512KB):

[heap size] = [eden space size] + [from space size] + [to space size] + [OldGen size]
  262656K   =       66048K      +       10752K      +     10752K      + 175104K

Why is the heap size slightly larger than the maximum heap size, -Xmx?


Solution

  • Firstly it seems strange, but as usual there is no magic. The only way to find out the answer is to go deeper: to the openjdk sources. Just checkout and try to grep: grep -r "Xmx" .

    There will be many tests, but they are not interesting to us. Two files can help us: /vm/runtime/arguments.cpp and /vm/memory/collectorPolicy.cpp

    Let's take a look at arguments.cpp: there is only simple parameters parsing, e.g. for -Xmx256M it will be something like result = 256 * 1024 * 1024 (with some inlining from me). So there is no answer to our question.

    But variable MaxHeapSize is initialized here, so we can try to find it. The real usage of MaxHeapSize can be found in our /vm/memory/collectorPolicy.cpp (where Xmx is used only in comments), where all heap generation sizes are are chosen by some policy.

    If we skip all the details, we will find that all regions in heap should be aligned and region's sizes depends on ratio parameters.

    The default JVM parameters for generation ratios are -XX:NewRatio=2 (ratio of young gen:old gen sizes) and -XX:SurvivorRatio=8 (ratio of eden:survivor gens). But heap size is not always divisible by 3, 9 or something else, so there should be some rounding. And, as I mentioned previously, there always should be some alignment.

    So, finally the answer: these sizes are the only possible sizes to satisfy the conditions of generational ratios and alignment. It's just not always possible for sum of these values being equals to Xmx parameter value.

    If you are interested in details, you can find the logic for rounding -XX:NewRatio here in method scale_by_NewRatio_aligned. Try to find the same logic about -XX:SurvivorRatio yourself :)