Abstract: Since Java 5, we have a JVM system thread constantly monitoring our memory usage. If we exceed our specified threshold, it will send us a notification. We can use this to get an early warning of possible trouble.
Welcome to the 92nd edition of The Java(tm) Specialists' Newsletter. Down here at the end of Africa, tech-toys are rather expensive, so whilst I was in Germany, I purchased a 802.11g wireless router. It is linked to my ADSL line (which costs about US$ 160 per month), allowing me to surf the internet at 512kb/s whilst sitting next to my pool. Naturally, I was rather proud with my purchase. On the last day of my visit to Germany, I popped in at my aunt & uncle to chase some golf balls across the meadows (I lost about 12 balls in 18 holes!) Imagine my surprise when my uncle told me matter-of-factly, "Oh, by the way, we have a wireless network at home linked to DSL". Ahem, but that is not all. My grandmother (at the age of 91 years) now also has a wireless network at home. My Oma uses the wireless network so that she can sit in her garden and write emails on her notebook to her grandchildren. How's that?!
On another topic, there is a cool utility from Sun called jvmstat that will show you the various generational memory pools and how much time is being spent by the garbage collectors. I linked jvmstat's VisualGC to my IntelliJ IDEA and was surprised that in 2.5 days, it had only used about 3 minutes for collecting garbage! It shows you the Eden space, the Survivor spaces, the Old Generation and the Permanent Generation.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
In Issue 061 of my newsletter, I asked readers whether their applications had ever caused an OutOfMemoryError. I then asked them to email me if they would like to know how to receive a warning, shortly before it was about to happen. Wow, the response! The requests kept on pouring in, and so far, I have had over 200 enquiries. At the time, my ideas for a warning system were sketchy at best, and would have been hopelessly inaccurate, in comparison to what JDK 5 offers us.
JDK 5 has added some wonderful new management beans that make writing an OutOfMemoryError Warning System possible. The most difficult part was probably finding resources on the topic. Google turned up two resources: The JDK documentation and a website written in Japanese ;-)
An OutOfMemoryError (OOME) is bad. It can happen at any time, with any thread. There is little that you can do about it, except to exit the program, change the -Xmx value, and restart the JVM. If you then make the -Xmx value too large, you slow down your application. The secret is to make the maximum heap value the right size, neither too small, nor too big. OOME can happen with any thread, and when it does, that thread typically dies. Often, there is not enough memory to build up a stack trace for the OOME, so you cannot even determine where it occurred, or why. You can use the exception catching mechanism of Issue 089, but that is then an after-the-fact measure, rather than preventative.
In January this year, I was migrating a program from MySQL to MS SQL Server. The author of the original program had used some JDBC commands that caused memory leaks under the SQL Server JDBC driver. This meant that periodically, one of the application's threads would simply vanish, leaving parts of the system paralyzed. Eliminating the OOME was a major task, and only happened when I rewrote all the database access code!
   Back to the issue at hand - how can we know when OOME's are
   about to occur?  The answer lies in the java.lang.management
   package of JDK 5.  The
   ManagementFactory class returns all sorts of
   useful JMX beans that we can use to manage the JVM.  One of
   these beans is the MemoryMXBean.
   Sun's implementation of
   the MemoryMXBean interface also implements the interface
   javax.management.NotificationEmitter.  The
   recommended way of listening to notifications by the memory
   bean is by downcasting the MemoryMXBean to the NotificationEmitter
   interface.  I can hardly believe it myself, you can verify this by
   looking at the
   documentation
      of the MemoryMXBean.
   
   Once you have downcast the MemoryMXBean to a
   NotificationEmitter, you can add a NotificationListener to
   the MemoryMXBean.  You should verify that the notification is
   of type MEMORY_THRESHOLD_EXCEEDED.  In my MemoryWarningSystem
   you add listeners that implement the MemoryWarningSystem.Listener
   interface, with one method
   memoryUsageLow(long usedMemory, long maxMemory)
   that will be called when the threshold is reached.  In my
   experiments, the memory bean notifies us quite soon after the
   usage threshold has been exceeded, but I could not determine
   the granularity.  Something to note is that the listener is
   being called by a special thread, called the Low Memory
   Detector thread, that is now part of the standard JVM.
   
   What is the threshold?  And which of the many pools should
   we monitor?  The only sensible pool to monitor is the Tenured
   Generation (Old Space).  When you set the size of the memory
   with -Xmx256m, you are setting the maximum memory to be used in the
   Tenured Generation.  I could not find a neat way of
   finding the tenured generation, except by looking through all
   the pools in my findTenuredGenPool() method, and
   returning the first one that was of type HEAP and where I was
   permitted to specify a usage threshold.  I do not know whether
   a better approach would not have been to search for the name
   "Tenured Gen"?
   
   In my setPercentageUsageThreshold(double percentage)
   method, I specify when I would like to be notified.  Note
   that this is a global setting since you can only have one
   usage threshold per Java Virtual Machine.  The percentage is
   used to calculate the usage threshold, based on the maximum
   memory size of the Tenured Generation pool (not the
   Runtime.getRuntime().maxMemory() value!).
   
import javax.management.*;
import java.lang.management.*;
import java.util.*;
/**
 * This memory warning system will call the listener when we
 * exceed the percentage of available memory specified.  There
 * should only be one instance of this object created, since the
 * usage threshold can only be set to one number.
 */
public class MemoryWarningSystem {
  private final Collection<Listener> listeners =
      new ArrayList<Listener>();
  public interface Listener {
    public void memoryUsageLow(long usedMemory, long maxMemory);
  }
  public MemoryWarningSystem() {
    MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
    NotificationEmitter emitter = (NotificationEmitter) mbean;
    emitter.addNotificationListener(new NotificationListener() {
      public void handleNotification(Notification n, Object hb) {
        if (n.getType().equals(
            MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
          long maxMemory = tenuredGenPool.getUsage().getMax();
          long usedMemory = tenuredGenPool.getUsage().getUsed();
          for (Listener listener : listeners) {
            listener.memoryUsageLow(usedMemory, maxMemory);
          }
        }
      }
    }, null, null);
  }
  public boolean addListener(Listener listener) {
    return listeners.add(listener);
  }
  public boolean removeListener(Listener listener) {
    return listeners.remove(listener);
  }
  private static final MemoryPoolMXBean tenuredGenPool =
      findTenuredGenPool();
  public static void setPercentageUsageThreshold(double percentage) {
    if (percentage <= 0.0 || percentage > 1.0) {
      throw new IllegalArgumentException("Percentage not in range");
    }
    long maxMemory = tenuredGenPool.getUsage().getMax();
    long warningThreshold = (long) (maxMemory * percentage);
    tenuredGenPool.setUsageThreshold(warningThreshold);
  }
  /**
   * Tenured Space Pool can be determined by it being of type
   * HEAP and by it being possible to set the usage threshold.
   */
  private static MemoryPoolMXBean findTenuredGenPool() {
    for (MemoryPoolMXBean pool :
        ManagementFactory.getMemoryPoolMXBeans()) {
      // I don't know whether this approach is better, or whether
      // we should rather check for the pool name "Tenured Gen"?
      if (pool.getType() == MemoryType.HEAP &&
          pool.isUsageThresholdSupported()) {
        return pool;
      }
    }
    throw new AssertionError("Could not find tenured space");
  }
}
I have tested this with a small program called MemTest. It sets the threshold to 60%, then prints out a message when that is reached, and changes the threshold to 80%. The main thread of the program puts random Double objects into a LinkedList and just keeps on going until it runs out of memory.
import java.util.*;
public class MemTest {
  public static void main(String... args) {
    MemoryWarningSystem.setPercentageUsageThreshold(0.6);
    MemoryWarningSystem mws = new MemoryWarningSystem();
    mws.addListener(new MemoryWarningSystem.Listener() {
      public void memoryUsageLow(long usedMemory, long maxMemory) {
        System.out.println("Memory usage low!!!");
        double percentageUsed = ((double) usedMemory) / maxMemory;
        System.out.println("percentageUsed = " + percentageUsed);
        MemoryWarningSystem.setPercentageUsageThreshold(0.8);
      }
    });
    Collection<Double> numbers = new LinkedList<Double>();
    while (true) {
      numbers.add(Math.random());
    }
  }
}
The output on my machine is, for example:
Memory usage low!!! percentageUsed = 0.6281726667795322 Memory usage low!!! percentageUsed = 0.8504659318016649 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Since constructing new objects is a rather fast operation in Java, it may happen that the JVM runs out of memory before getting a chance to notify us.
   Inside your listener, you can then do some preventative
   measures.  For example, you could print out what each of the
   threads in your system was doing, using
   Thread.getAllStackTraces().  Or, you could keep
   a nice big chunk of memory available as reserve, and then
   release that when you are running low on memory, and send a
   warning message to the system administrator.
   The ability to at least get all the stack traces will make
   it much easier to find & destroy memory leaks in Java.
   
Kind regards
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.