package edu.stanford.nlp.util; import edu.stanford.nlp.util.logging.Redwood; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.util.ArrayList; /** * Utilities for monitoring memory use, including peak memory use. * */ public class MemoryMonitor { /** A logger for this class */ private static Redwood.RedwoodChannels log = Redwood.channels(MemoryMonitor.class); public static final int MAX_SWAPS = 50; protected long lastPoll; protected long pollEvery; protected int freeMem; protected int usedSwap; protected int swaps; protected Runtime r; public MemoryMonitor() { this(60000); // 1 min default } public MemoryMonitor(long millis) { lastPoll = 0; pollEvery = millis; freeMem = 0; usedSwap = 0; swaps = 0; r = Runtime.getRuntime(); pollVMstat(true); } // TODO I don't think anyone uses this public void pollAtMostEvery(long millis) { pollEvery = millis; } public int getMaxMemory() { return (int) (r.maxMemory() / 1024); } public int getMaxAvailableMemory() { return getMaxAvailableMemory(false); } // kilobytes public int getMaxAvailableMemory(boolean accurate) { if (accurate) { System.gc(); } return (int) ((r.maxMemory() - r.totalMemory() + r.freeMemory()) / 1024); } public int getUsedMemory() { return getUsedMemory(false); } public int getUsedMemory(boolean accurate) { if (accurate) { System.gc(); } return (int) ((r.totalMemory() - r.freeMemory()) / 1024); } public int getSystemFreeMemory(boolean accurate) { if (accurate) { System.gc(); } pollVMstat(false); return freeMem; } public int getSystemUsedSwap() { pollVMstat(false); return usedSwap; } public double getSystemSwapsPerSec() { pollVMstat(false); return swaps; } protected static ArrayList<String> parseFields(BufferedReader br, String splitStr, int[] lineNums, int[] positions) throws IOException { int currLine = 0; int processed = 0; ArrayList<String> found = new ArrayList<>(); while (br.ready()) { String[] fields = br.readLine().split(splitStr); currLine++; if (currLine == lineNums[processed]) { int currPosition = 0; for (String f : fields) { if (f.length() > 0) { currPosition++; if (currPosition == positions[processed]) { found.add(f); processed++; if (processed == positions.length) { break; } } } } } } return found; } public void pollFree(boolean force) { if (!force) { long time = System.currentTimeMillis(); if (time - lastPoll < pollEvery) { return; } } Process p = null; int[] freeLines = { 2, 4 }; int[] freePositions = { 4, 3 }; lastPoll = System.currentTimeMillis(); try { p = r.exec("free"); p.waitFor(); BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream())); ArrayList<String> l = parseFields(bri, " ", freeLines, freePositions); freeMem = Integer.parseInt(l.get(1)); usedSwap = Integer.parseInt(l.get(2)); } catch (Exception e) { log.info(e); } finally { if (p != null) { p.destroy(); } } } public void pollVMstat(boolean force) { if (!force) { long time = System.currentTimeMillis(); if (time - lastPoll < pollEvery) { return; } } Process p = null; int[] lines = { 4, 4, 4, 4 }; int[] positions = { 3, 4, 7, 8 }; try { p = r.exec("vmstat 1 2"); p.waitFor(); long time = System.currentTimeMillis(); BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream())); ArrayList<String> l = parseFields(bri, " ", lines, positions); usedSwap = Integer.parseInt(l.get(0)); freeMem = Integer.parseInt(l.get(1)); swaps = Integer.parseInt(l.get(2)) + Integer.parseInt(l.get(3)); lastPoll = time; } catch (Exception e) { e.printStackTrace(); } finally { if (p != null) { p.destroy(); } } } public boolean systemIsSwapping() { return (getSystemSwapsPerSec() > MAX_SWAPS); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("lastPoll:").append(lastPoll); sb.append(" pollEvery:").append(pollEvery); sb.append(" freeMem:").append(freeMem); sb.append(" usedSwap:").append(usedSwap); sb.append(" swaps:").append(swaps); sb.append(" maxAvailable:").append(getMaxAvailableMemory(false)); sb.append(" used:").append(getUsedMemory(false)); return sb.toString(); } /** * This class offers a simple way to track the peak memory used by a program. * Simply launch a <code>PeakMemoryMonitor</code> as * * <blockquote><code> * Thread monitor = new Thread(new PeakMemoryMonitor());<br /> * monitor.start() * </code></blockquote> * * and then when you want to stop monitoring, call * * <blockquote><code> * monitor.interrupt(); * monitor.join(); * </code></blockquote> * * You only need the last line if you want to be sure the monitor stops before * you move on in the code; and strictly speaking, you should surround the * <code>monitor.join()</code> call with a <code>try/catch</code> block, as * the <code>Thread</code> you are running could itself be interrupted, so you * should actually have something like * * <blockquote><code> * monitor.interrupt(); * try { * monitor.join(); * } catch (InterruptedException ex) { * // handle the exception * } * </code></blockquote> * * or else throw the exception. * * @author ilya */ public static class PeakMemoryMonitor implements Runnable { private static final float GIGABYTE = 1 << 30; private static final int DEFAULT_POLL_FREQUENCY = 1000; /* 1 second */ private static final int DEFAULT_LOG_FREQUENCY = 60000; /* 1 minute */ private int pollFrequency; private int logFrequency; private Timing timer; private PrintStream outstream; private long peak = 0; public PeakMemoryMonitor() { this(DEFAULT_POLL_FREQUENCY, DEFAULT_LOG_FREQUENCY); } /** * @param pollFrequency frequency, in milliseconds, with which to poll * @param logFrequency frequency, in milliseconds, with which to log maximum memory * used so far */ public PeakMemoryMonitor(int pollFrequency, int logFrequency) { this(pollFrequency, logFrequency, System.err); } public PeakMemoryMonitor(int pollFrequency, int logFrequency, PrintStream out) { this.pollFrequency = pollFrequency; this.logFrequency = logFrequency; this.outstream = out; this.timer = new Timing(); } public void run() { Runtime runtime = Runtime.getRuntime(); timer.start(); while (true) { peak = Math.max(peak, runtime.totalMemory() - runtime.freeMemory()); if (timer.report() > logFrequency) { log(); timer.restart(); } try { Thread.sleep(pollFrequency); } catch (InterruptedException e) { log(); throw new RuntimeInterruptedException(e); } } } public void log() { outstream.println(String.format("Maximum memory used: %.1f GB", peak / GIGABYTE)); } } public static void main(String[] args) throws InterruptedException { Thread pmm = new Thread(new PeakMemoryMonitor()); pmm.start(); long time = System.currentTimeMillis(); MemoryMonitor mm = new MemoryMonitor(); long time2 = System.currentTimeMillis(); System.out.println("Created MemoryMonitor. Took " + (time2 - time) + " milliseconds."); System.out.println(mm); time = System.currentTimeMillis(); mm.pollVMstat(true); time2 = System.currentTimeMillis(); System.out.println("Second Poll. Took " + (time2 - time) + " milliseconds."); System.out.println(mm); pmm.interrupt(); pmm.join(); } }