JDK-4686462 : Runtime.getRuntime().maxMemory() does not correspond to -Xmx
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2002-05-16
  • Updated: 2007-05-08
Description
Name: nt126004			Date: 05/16/2002


FULL PRODUCT VERSION :
[c:\java\work]java -version
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

FULL OPERATING SYSTEM VERSION :  Windows 2000
4NT  3.01A   Windows NT 5.00

A DESCRIPTION OF THE PROBLEM :
The current documentation says:

public long maxMemory()
Returns the maximum amount of memory that the Java virtual machine will
attempt to use.

The behaviour described in bug 4391499 shows numbers that appear to be close
to what was requested in the -Xmx flag.  In that report, the maxMemory
numbers seem to increase in 4MB increments.  I would have no problem with
that.  But what I'm seeing is that the maxMemory values are consistently a
full 64MB larger than what was requested in -Xmx, but the effective maximum
heap size is actually very close the -Xmx value.  So I'm left with the
conclusion that the maxMemory() method is worthless in practice.

Our client/server package needs to determine at runtime if there's enough
memory to accept a new connection.  I was hoping to use the logical
calculation:  available = maxMemory - totalMemory - freeMemory.  Instead,
we're doing this:

    boolean refuse = false;
    try
    {
        byte[] memoryCheck = new byte[MIN_STARTUP_MEMORY];
    }
    catch (OutOfMemoryError e)
    {
        refuse = true;
    }

That seems to be pretty reliable, but it's causing extra GC thrashing that
shouldn't be necessary if we had a reliable way to determine the maximum
memory.

Also see:
http://forum.java.sun.com/thread.jsp?forum=31&thread=253517

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the sample program below.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Given this code:

long max = Runtime.getRuntime().maxMemory();

Expected: I would expect max to correspond to the number of
bytes specified in the -Xmx command-line parameter, or the
default of 67,108,864 bytes if no -Xmx parameter was
specified.

Actual: Instead, the value returned is consistently 64MB
(67,108,864 bytes) more than the -Mmx parameter.

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class max
{
    public static void main(String[] args)
    {
        long max = Runtime.getRuntime().maxMemory();
        System.out.println("Runtime.maxMemory(): "+max+" bytes");
        System.out.println("Runtime.maxMemory(): "+max/1024/1024+" MB");
        long adjusted = max - (64*1024*1024);
        System.out.println("Adjusted value is "+adjusted+" bytes");
        System.out.println("Adjusted value is "+adjusted/1024/1024+" MB");
    }
}

[c:\java\work]java max
Runtime.maxMemory(): 134217728 bytes
Runtime.maxMemory(): 128 MB
Adjusted value is 67108864 bytes
Adjusted value is 64 MB

[c:\java\work]java -Xmx128m max
Runtime.maxMemory(): 201326592 bytes
Runtime.maxMemory(): 192 MB
Adjusted value is 134217728 bytes
Adjusted value is 128 MB

[c:\java\work]java -Xmx134217728 max
Runtime.maxMemory(): 201326592 bytes
Runtime.maxMemory(): 192 MB
Adjusted value is 134217728 bytes
Adjusted value is 128 MB

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
long max = Runtime.getRuntime().maxMemory() - 64*1024*1024;

...but that's obviously not a good long term solution
because I have no idea where the 64MB comes from in the
first place.
(Review ID: 146629) 
======================================================================

Comments
EVALUATION ###@###.### 2003-07-22 the specific problem reported here is caused by a bug (fixed in 1.4.2) that caused PermGen space to be included in the value returned by maxMemeory(). However, there are larger questions as to whether or not this whole approach is correct. I will ask a GC expert to comment further.
11-06-2004