Default VM memory allocation

Hi

As you all will know the JVM allocates x MB of minimum memory and y MB of maximum memory, if no parameters are given. If the application needs more memory, a MemoryOutOfBoundsException is thrown and I have to grant the VM more max and possibly more initial memory by passing appropriate parameters.

For an applet or a web abblication this may be a good behavior. But for a desktop application noone will expect this. And it prevents me from simply doubleclicking a jar file to start my application, if it needs more memory than the default.

And if an application can perfectly run with a few MB of memory, but potentially needs way more, if large resources are loaded at runtime, I find it bad to let the JVM always take 2 GB just to make sure, that it will work in all situations, but also robbs 2 GB of my memory, even if it won’t need most of it.

A normal desktop application only takes the memory, that it needs and dynamically allocates more from the system heap when it needs more. This should also be the default behavior for a Java application, if run in desktop mode and no xMx or xMs parameters are passed.

What do you think?

Marvin

The problem is that the garbage collector will perform horribly (for GUIs) with large heaps. Expect gc-times of more than a second. In JDK7 we will have the GarbageFirst collector, which will take care of that: it will have many 1MB heaps.

What you can do however, is setting the direct-memory-heap (NIO Buffers), to something very big. You’ll be malloc-ing just like any other native application, and it won’t claim any resources, not even virtual memory, as long as you haven’t actually allocated your buffers.

You can still use an executable JAR when you need a larger heap: you simply use it to kick off a new java process with the appropriate parameters.

So in JDK7 the behavior described above could the done, couldn’t it?

Unfortunately in my own case I cannot do this because I need large byte arrays for performace reasons.

Marvin

best way would be that the JVM reduce max heap the same way it increase it (but it should still take care of Xms / Xmx), the problem is that JVM know how to grow to Xmx but it did not shrink the same way.

[quote]For an applet or a web abblication this may be a good behavior
[/quote]
sure you know but in case of, this is no more true, there is no more memory limitation for Applet with new plugin parameters

This is an inherent problem of GC systems up until now. Really, we all know that the upper memory limit should be totally unbounded, and the GC should be reclaiming memory before it attempts to expand any further. I can’t really see how this is hard, so it’s a wonder it’s taken quite as long as it has. Even now though with G1 we’ve still got arbitrary limits on memory allocation.

Cas :slight_smile:

Maybe I’m being thick here :persecutioncomplex:, but doesn’t the garbage collector already prevent unnecessarily increasing the heap as it is now? I remember in the old days that the heap would always expand to maximum size eventually, but nowadays I don’t really see that behavior anymore.
And usually I see the worst GC performance when an app is approaching -Xmx, much more so than when the heap gets big.

A trick i sometimes use, with things that would overflow the default heap, in a longer running application is to spawn another jvm process to do what needs to be done. If there is no communication needed immediately, it is tolerable, though file locks and such ARE involved. You can do it from java easily enough, but it wont be signed.

So on our cluster i often have my app set with -Xmx2000M. It does not take that memory until the app requires it. It will sit at only about 60-100M if that’s all i use.

Personally I like being able to set a max, however ideally its something the OS should be managing, not java.

yes but problem is that if the program requiere the max heap size even for few seconds, it will never free it, setting a maximum is IMO nice but it will be even better if JVM was freeing it after a moment if it is no more requiere (inded it should always stay between xms & xmx limit )

Not true anymore! :-*

ha ! nice, did not notice that yet :persecutioncomplex:

import java.util.ArrayList;
import java.util.List;

public class HeapShrink
{
   static String whoAmI()
   {
      String v = System.getProperty("java.runtime.version");
      String a = System.getProperty("os.arch");
      return "java version: " + v + " arch=" + a;
   }

   static List<long[]> history = new ArrayList<long[]>();

   static void dumpMem()
   {
      for (int i = 0; i < 4; i++)
         System.gc();

      long free = Runtime.getRuntime().freeMemory();
      long total = Runtime.getRuntime().totalMemory();
      long max = Runtime.getRuntime().maxMemory();
      long used = total - free;

      long[] snapshot = new long[3];
      snapshot[0] = used;
      snapshot[1] = total;
      snapshot[2] = max;
      history.add(snapshot);

      System.out.println("Current: " + (used / 1024) + "K / " + (total / 1024) + "K / " + (max / 1024) + "K");

      if (history.size() >= 2)
      {
         long[] prev = history.get(history.size() - 2);
         long delta = ((snapshot[1] - prev[1]) / 1024);
         System.out.println("  Heap-delta: " + (delta > 0 ? "+" : "") + delta + "K");
      }
   }

   static void sleep(long ms)
   {
      try
      {
         Thread.sleep(ms);
      }
      catch (InterruptedException exc)
      {
         // ignore
      }
   }

   public static void main(String[] args)
   {
      System.out.println(whoAmI());

      final List<byte[]> retain = new ArrayList<byte[]>();

      new Thread(new Runnable()
      {
         @Override
         public void run()
         {
            while (true)
            {
               dumpMem();
               sleep(1000);
            }
         }
      }).start();

      while (true)
      {
         if (Math.random() < 0.1)
            retain.clear();
         else
            retain.add(new byte[1024 * 1024]);
         sleep(100);
      }
   }
}

Output on JRE supporting heap shrinking:


java version: 1.6.0_20-b02 arch=x86
Current: 471K / 15936K / 253440K
Current: 2250K / 15936K / 253440K
  Heap-delta: 0K
Current: 6347K / 15936K / 253440K
  Heap-delta: 0K
Current: 4299K / 15936K / 253440K
  Heap-delta: 0K
Current: 14539K / 35180K / 253440K
  Heap-delta: +19244K
Current: 2251K / 15940K / 253440K
  Heap-delta: -19240K
Current: 1227K / 15940K / 253440K
  Heap-delta: 0K
Current: 203K / 15940K / 253440K
  Heap-delta: 0K
Current: 203K / 15940K / 253440K
  Heap-delta: 0K
Current: 203K / 15940K / 253440K
  Heap-delta: 0K
Current: 2251K / 15940K / 253440K
  Heap-delta: 0K
Current: 3275K / 15940K / 253440K
  Heap-delta: 0K
Current: 14522K / 35152K / 253440K
  Heap-delta: +19212K
Current: 24763K / 59900K / 253440K
  Heap-delta: +24748K
Current: 8378K / 40604K / 253440K
  Heap-delta: -19296K
Current: 6330K / 25756K / 253440K
  Heap-delta: -14848K
Current: 16571K / 40100K / 253440K
  Heap-delta: +14344K
Current: 186K / 15940K / 253440K
  Heap-delta: -24160K
Current: 7354K / 17828K / 253440K
  Heap-delta: +1888K
Current: 186K / 15940K / 253440K
  Heap-delta: -1888K
Current: 7355K / 17828K / 253440K
  Heap-delta: +1888K

Output on ‘old’ JRE:


java version: 1.6.0_13-b03 arch=amd64
Current: 770K / 58880K / 873856K
Current: 1182K / 58880K / 873856K
  Heap-delta: 0K
Current: 11423K / 58880K / 873856K
  Heap-delta: 0K
Current: 5279K / 58880K / 873856K
  Heap-delta: 0K
Current: 7327K / 58880K / 873856K
  Heap-delta: 0K
Current: 18591K / 58880K / 873856K
  Heap-delta: 0K
Current: 8351K / 58880K / 873856K
  Heap-delta: 0K
Current: 4255K / 58880K / 873856K
  Heap-delta: 0K
Current: 14495K / 58880K / 873856K
  Heap-delta: 0K
Current: 2207K / 58880K / 873856K
  Heap-delta: 0K
Current: 13471K / 58880K / 873856K
  Heap-delta: 0K
Current: 6303K / 58880K / 873856K
  Heap-delta: 0K
Current: 2207K / 58880K / 873856K
  Heap-delta: 0K
Current: 4255K / 58880K / 873856K
  Heap-delta: 0K
Current: 15519K / 58880K / 873856K
  Heap-delta: 0K
Current: 25760K / 58880K / 873856K
  Heap-delta: 0K
Current: 7327K / 58880K / 873856K
  Heap-delta: 0K
Current: 18592K / 58880K / 873856K
  Heap-delta: 0K
Current: 7328K / 58880K / 873856K
  Heap-delta: 0K
Current: 3232K / 58880K / 873856K
  Heap-delta: 0K
Current: 5280K / 58880K / 873856K
  Heap-delta: 0K
Current: 15520K / 58880K / 873856K
  Heap-delta: 0K
Current: 2208K / 58880K / 873856K
  Heap-delta: 0K

excellent showcase Riven thanks, so OP should be happy ?

Everybody should be.

[quote]Output on ‘old’ JRE:
[/quote]
You show 1.6.0_13, everything above that version this should shrink its memory use? If not what version.

Is there a test flag to help for the older Java VMs shrink its memory use?

I don’t know (that answers both questions)

Some info here:

Seems this is supported on Java 5. Just need to change the default values of XX:MinHeapFreeRatio/XX:MaxHeapFreeRatio.