Monitoring Gross Memory Usage

- 45 - System.out.printFollowing stack contructed ; System.out.printHash.gets; System.out.println times:; System.out.printlns; System.out.println ; } } }

2.5 Monitoring Gross Memory Usage

The JDK provides two methods for monitoring the amount of memory used by the runtime system. The methods are freeMemory and totalMemory in the java.lang.Runtime class. totalMemory returns a long , which is the number of bytes currently allocated to the runtime system for this particular Java VM process. Within this memory allocation, the VM manages its objects and data. Some of this allocated memory is held in reserve for creating new objects. When the currently allocated memory gets filled and the garbage collector cannot allocate sufficiently more memory, the VM requests more memory to be allocated to it from the underlying system. If the underlying system cannot allocate any further memory, an OutOfMemoryError error is thrown. Total memory can go up and down; some Java runtimes can return sections of unused memory to the underlying system while still running. freeMemory returns a long , which is the number of bytes available to the VM to create objects from the section of memory it controls i.e., memory already allocated to the runtime by the underlying system. The free memory increases when a garbage collection successfully reclaims space used by dead objects, and also increases when the Java runtime requests more memory from the underlying operating system. The free memory reduces each time an object is created, and also when the runtime returns memory to the underlying system. It can be useful to monitor memory usage while an application runs: you can get a good feel for the hotspots of your application . You may be surprised to see steady decrements in the free memory available to your application when you were not expecting any change. This can occur when you continuously generate temporary objects from some routine; manipulating graphical elements frequently shows this behavior. Monitoring memory with freeMemory and totalMemory is straightforward, and I include here a simple class that does this graphically. It creates three threads: one to periodically sample the memory, one to maintain a display of the memory usage graph, and one to run the program you are monitoring. Figure 2-1 shows a screen shot of the memory monitor after monitoring a run of the ProfileTest class defined earlier in the section Section 2.3.1 . The total memory allocation is flat because the class did not hold on to much memory at any one time. The free memory shows the typical sawtooth pattern of an application cycling through temporary objects: each upstroke is where the garbage collector kicked in and freed up the space being taken by the discarded dead objects. Figure 2-1. Memory monitoring the ProfileTest class - 46 - The monitor was run using the command: java tuning.profile.MemoryMonitor tuning.profile.ProfileTest Here are the classes for the memory monitor, together with comments: package tuning.profile; import java.awt.; import java.awt.event.; import java.lang.reflect.; Internal class to periodically sample memory usage class MemorySampler implements Runnable { long[] freeMemory = new long[1000]; long[] totalMemory = new long[1000]; int sampleSize = 0; long max = 0; boolean keepGoing = true; MemorySampler { Start the object running in a separate maximum priority thread Thread t = new Threadthis; t.setDaemontrue; t.setPriorityThread.MAX_PRIORITY; t.start ; } public void stop { set to stop the thread when someone tells us keepGoing = false; } public void run { Just a loop that continues sampling memory values every 30 milliseconds until the stop method is called. Runtime runtime = Runtime.getRuntime ; whilekeepGoing { try{Thread.sleep30;}catchInterruptedException e{}; addSampleruntime; } } public void addSampleRuntime runtime - 47 - { Takes the actual samples, recording them in the two arrays. We expand the arrays when they get full up. if sampleSize = freeMemory.length { just expand the arrays if they are now too small long[] tmp = new long[2 freeMemory.length]; System.arraycopyfreeMemory, 0, tmp, 0, freeMemory.length; freeMemory = tmp; tmp = new long[2 totalMemory.length]; System.arraycopytotalMemory, 0, tmp, 0, totalMemory.length; totalMemory = tmp; } freeMemory[sampleSize] = runtime.freeMemory ; totalMemory[sampleSize] = runtime.totalMemory ; Keep the maximum value of the total memory for convenience. if max totalMemory[sampleSize] max = totalMemory[sampleSize]; sampleSize++; } } public class MemoryMonitor extends Frame implements WindowListener,Runnable { The sampler object MemorySampler sampler; interval is the delay between calls to repaint the window long interval; static Color freeColor = Color.red; static Color totalColor = Color.blue; int[] xpoints = new int[2000]; int[] yfrees = new int[2000]; int[] ytotals = new int[2000]; Start a monitor and the graph, then start up the real class with any arguments. This is given by the rest of the commmand line arguments. public static void mainString args[] { try { Start the grapher with update interval of half a second MemoryMonitor m = new MemoryMonitor500; Remaining arguments are the class with the main method, and its arguments String classname = args[0]; String[] argz = new String[args.length-1]; System.arraycopyargs, 1, argz, 0, argz.length; Class clazz = Class.forNameclassname; main has one parameter, a String array. Class[] mainParamType = {args.getClass }; Method main = clazz.getMethodmain, mainParamType; Object[] mainParams = {argz}; - 48 - start real class main.invokenull, mainParams; Tell the monitor the application finished m.testStopped ; } catchException e { e.printStackTrace ; } } public MemoryMonitorlong updateInterval { Create a graph window and start it in a separate thread superMemory Monitor; interval = updateInterval; this.addWindowListenerthis; this.setSize600,200; this.show ; Start the sampler it runs itself in a separate thread sampler = new MemorySampler ; and put myself into a separate thread new Threadthis.start ; } public void run { Simple loop, just repaints the screen every interval milliseconds int sampleSize = sampler.sampleSize; for ;; { try{Thread.sleepinterval;}catchInterruptedException e{}; if sampleSize = sampler.sampleSize { Should just call repaint here this.repaint ; but it doesnt always work, so Ill repaint in this thread. Im not doing anything else anyway in this thread. try{ this.updatethis.getGraphics ; } catchException e{e.printStackTrace ;} sampleSize = sampler.sampleSize; } } } public void testStopped { just tell the sampler to stop sampling. We wont exit ourselves until the window is explicitly closed so that our user can examine the graph at leisure. sampler.stop ; } public void paintGraphics g { Straightforward - draw a graph for the latest N points of total and free memory where N is the width of the window. - 49 - try { java.awt.Dimension d = getSize ; int width = d.width-20; int height = d.height - 40; long max = sampler.max; int sampleSize = sampler.sampleSize; if sampleSize 20 return; int free, total, free2, total2; int highIdx = width sampleSize-1 ? width : sampleSize-1; int idx = sampleSize - highIdx - 1; for int x = 0 ; x highIdx ; x++, idx++ { xpoints[x] = x+10; yfrees[x] = height - int sampler.freeMemory[idx] height max + 40; ytotals[x] = height - int sampler.totalMemory[idx] height max + 40; } g.setColorfreeColor; g.drawPolylinexpoints, yfrees, highIdx; g.setColortotalColor; g.drawPolylinexpoints, ytotals, highIdx; g.setColorColor.black; g.drawStringmaximum: + max + bytes total memory - blue line | free memory - red line, 10, 35; } catch Exception e { System.out.printlnMemoryMonitor: + e.getMessage ;} } public void windowActivatedWindowEvent e{} public void windowClosedWindowEvent e{} public void windowClosingWindowEvent e {System.exit0;} public void windowDeactivatedWindowEvent e{} public void windowDeiconifiedWindowEvent e{} public void windowIconifiedWindowEvent e{} public void windowOpenedWindowEvent e {} }

2.6 ClientServer Communications