Give Java Applications More Memory

Here you’ll find a neat trick how to allow your Java applications to use more memory without any user interaction.

The Problem

Java applications have maximum amount of memory which by default is often not enough. So users often see your application become slower and slower because all it is doing is garbage collection, until it finally throws an OutOfMemoryException.

There are ways to allow for more memory like starting with an explicit -mx switch setting, fiddling with the defaults in the Java Control Panel, or using a dedicated installer. The first two are not easy to explain to non-tech users, the last one introduces a new dependency.

But there is a simple solution that works with a an executable jar file.

The Solution

Just make your application restart itself again with improved memory settings.

This burns down to the following steps:

  1. When started, check if this is already a restart.
  2. If not, check the memory settings and see whether it is enough.
    • Use Runtime.getRuntime().maxMemory() for the currently allowed maximum.

    • The amount of physical memory is a bit harder to get and requires basically evil access to sun packages:

        /**
         * Get the amount of physical memory on this machine.
         * @return the amount of physical memory in bytes, or {@code 0L} if the amount is unknown
         */
        public static long getPhysicalMemorySize()
        {
          try {
            // this may result in a class cast exception, but currently there is no other simple way to get the physical memory size
            final OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
            return operatingSystemMXBean.getTotalPhysicalMemorySize();
          } catch (ClassCastException x) {
            Debug.error(x);
          }
          return 0;
        }
      
  3. If it is not enough, collect all necessary data (JRE, Java settings, command line parameters), and start a new process with improved memory settings.

Modern Java implementations don’t start the whole JVM again when it is already running, and as the restart should happen early in your main method, only a few classes are actually loaded and the time penalty for restarting is usually not noticeable.

The Restarter Class

One of the pearls hidden in the Caff Commons is the Restarter in the commons module.

It fulfills the above steps with some bells and whistles, and you are entitled to download it and adapt it to your needs. It comes with a simple test code in form of a main method:

 /**
   * Test code.
   * @param args arguments (unused, but should be passed through)
   */
  public static void main(String[] args)
  {
    Debug.installCookedOutput();
    Debug.setMask(Debug.DEBUG_ALL_MASK - Debug.TRACE_FLAG);

    System.out.println("args: "+ Arrays.toString(args));

    Runtime runtime = Runtime.getRuntime();
    long maxMem = runtime.maxMemory();
    System.out.println("max mem: "+maxMem);
    Restarter.possiblyRestart(Restarter.class, 0.95, args);
    System.out.println("Running on ... and exit");
    System.exit(0);
  }

What it prints with a 64bit Java 7 JVM with 32 GiB of memory installed on a Linux machine is:

args: []
max mem: 7488405504
07.06.2016 12:27:57	MESSAGE:
	Trying to require 32008925593 bytes...
[de.caff.util.startup.Restarter.possiblyRestart(Restarter.java:175)]
07.06.2016 12:27:57	MESSAGE:
	Command line: /home/rammi/jdk/jdk1.7.0_79/jre/bin/java -mx32008925593 -classpath [...] -Dawt.toolkit=sun.awt.X11.XToolkit -Dde.caff.util.startup.Restarted=true de.caff.util.startup.Restarter
[de.caff.util.startup.Restarter.possiblyRestart(Restarter.java:192)]
args: []
max mem: 28454158336
07.06.2016 12:27:57	MESSAGE:
	Property de.caff.util.startup.Restarted was set, application was already restarted.
[de.caff.util.startup.Restarter.possiblyRestart(Restarter.java:161)]
Running on ... and exit

Be Polite

Users might have a reason to restrict the memory size of your app, so you should follow the example of my de·caff Viewer and add a preference setting whether to restart or not; or at least ask before restarting.

Caveats

This only works for Java applications, not for Java Applets. But Applets are basically dead, anyway.