001 /* 002 * $Id: MemoryMonitor.java,v 1.13 2012/02/19 17:35:52 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2012 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031 package edu.wisc.ssec.mcidasv.util; 032 033 import static javax.swing.GroupLayout.DEFAULT_SIZE; 034 import static javax.swing.GroupLayout.Alignment.LEADING; 035 036 import java.awt.BorderLayout; 037 import java.awt.Color; 038 import java.awt.Font; 039 import java.awt.event.MouseAdapter; 040 import java.awt.event.MouseEvent; 041 import java.awt.event.MouseListener; 042 import java.text.DecimalFormat; 043 import java.text.SimpleDateFormat; 044 import java.util.Date; 045 046 import javax.swing.GroupLayout; 047 import javax.swing.JFrame; 048 import javax.swing.JLabel; 049 import javax.swing.JPanel; 050 import javax.swing.JPopupMenu; 051 import javax.swing.SwingUtilities; 052 053 import ucar.unidata.idv.IntegratedDataViewer; 054 import ucar.unidata.util.CacheManager; 055 import ucar.unidata.util.GuiUtils; 056 import ucar.unidata.util.Msg; 057 058 // initial version taken verbatim from Unidata :( 059 public class MemoryMonitor extends JPanel implements Runnable { 060 061 /** flag for running */ 062 private boolean running = false; 063 064 /** sleep interval */ 065 private final long sleepInterval = 2000; 066 067 /** a thread */ 068 private Thread thread; 069 070 /** percent threshold */ 071 private final int percentThreshold; 072 073 /** number of times above the threshold */ 074 private int timesAboveThreshold = 0; 075 076 /** percent cancel */ 077 private final int percentCancel; 078 079 /** have we tried to cancel the load yet */ 080 private boolean triedToCancel = false; 081 082 /** format */ 083 private static DecimalFormat fmt = new DecimalFormat("#0"); 084 085 /** the label */ 086 private JLabel label = new JLabel(""); 087 088 /** Keep track of the last time we ran the gc and cleared the cache */ 089 private static long lastTimeRanGC = -1; 090 091 /** Keep track of the IDV so we can try to cancel loads if mem usage gets high */ 092 private IntegratedDataViewer idv; 093 094 private boolean showClock = false; 095 096 private static final Font clockFont = new Font("Dialog", Font.BOLD, 11); 097 098 private static SimpleDateFormat clockFormat = new SimpleDateFormat("HH:mm:ss z"); 099 100 /** 101 * Default constructor 102 */ 103 public MemoryMonitor(IntegratedDataViewer idv) { 104 this(idv, 75, 95, false); 105 } 106 107 /** 108 * Create a new MemoryMonitor 109 * 110 * @param percentThreshold the percentage of use memory before garbage 111 * collection is run 112 * 113 */ 114 public MemoryMonitor(IntegratedDataViewer idv, final int percentThreshold, final int percentCancel, boolean showClock) { 115 super(new BorderLayout()); 116 this.idv = idv; 117 this.showClock = showClock; 118 Font f = label.getFont(); 119 label.setToolTipText("Used memory/Max used memory/Max memory"); 120 label.setFont(f); 121 this.percentThreshold = percentThreshold; 122 this.percentCancel = percentCancel; 123 124 GroupLayout layout = new GroupLayout(this); 125 this.setLayout(layout); 126 layout.setHorizontalGroup( 127 layout.createParallelGroup(LEADING) 128 .addComponent(label, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 129 ); 130 layout.setVerticalGroup( 131 layout.createParallelGroup(LEADING) 132 .addComponent(label, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 133 ); 134 135 // MouseListener ml = new MouseAdapter() { 136 // @Override public void mouseClicked(MouseEvent e) { 137 // if (SwingUtilities.isRightMouseButton(e)) 138 // popupMenu(e); 139 // } 140 // }; 141 MouseListener ml = new MouseAdapter() { 142 public void mouseClicked(MouseEvent e) { 143 if (!SwingUtilities.isRightMouseButton(e)) { 144 toggleClock(); 145 showStats(); 146 } 147 handleMouseEvent(e); 148 } 149 }; 150 151 label.addMouseListener(ml); 152 label.setOpaque(true); 153 label.setBackground(doColorThing(0)); 154 start(); 155 } 156 157 /** 158 * Handle a mouse event 159 * 160 * @param event the event 161 */ 162 private void handleMouseEvent(MouseEvent event) { 163 if (SwingUtilities.isRightMouseButton(event)) { 164 popupMenu(event); 165 return; 166 } 167 } 168 169 private void toggleClock() { 170 this.showClock = !this.showClock; 171 idv.getStateManager().putPreference("idv.monitor.showclock", this.showClock); 172 } 173 174 private String getToolTip() { 175 if (showClock) { 176 return "Current time"; 177 } else { 178 return "Used memory/Max used memory/Max memory"; 179 } 180 } 181 182 /** 183 * Popup a menu on an event 184 * 185 * @param event the event 186 */ 187 private void popupMenu(final MouseEvent event) { 188 JPopupMenu popup = new JPopupMenu(); 189 // if (running) { 190 // popup.add(GuiUtils.makeMenuItem("Stop Running", 191 // MemoryMonitor.this, "toggleRunning")); 192 // } else { 193 // popup.add(GuiUtils.makeMenuItem("Resume Running", 194 // MemoryMonitor.this, "toggleRunning")); 195 // } 196 197 popup.add(GuiUtils.makeMenuItem("Clear Memory & Cache", 198 MemoryMonitor.this, "runGC")); 199 popup.show(this, event.getX(), event.getY()); 200 } 201 202 /** 203 * Toggle running 204 */ 205 public void toggleRunning() { 206 if (running) { 207 stop(); 208 } else { 209 start(); 210 } 211 } 212 213 /** 214 * Set the label font 215 * 216 * @param f the font 217 */ 218 public void setLabelFont(final Font f) { 219 label.setFont(f); 220 } 221 222 /** 223 * Stop running 224 */ 225 public synchronized void stop() { 226 running = false; 227 label.setEnabled(false); 228 } 229 230 /** 231 * Start running 232 */ 233 private synchronized void start() { 234 if (running) 235 return; 236 237 label.setEnabled(true); 238 running = true; 239 triedToCancel = false; 240 thread = new Thread(this, "Memory monitor"); 241 thread.start(); 242 } 243 244 /** 245 * Run the GC and clear the cache 246 */ 247 public void runGC() { 248 CacheManager.clearCache(); 249 Runtime.getRuntime().gc(); 250 lastTimeRanGC = System.currentTimeMillis(); 251 } 252 253 /** 254 * Show the statistics. 255 */ 256 private void showStats() throws IllegalStateException { 257 label.setToolTipText(getToolTip()); 258 if (showClock) { 259 Date d = new Date(); 260 clockFormat.setTimeZone(GuiUtils.getTimeZone()); 261 label.setText(" " + clockFormat.format(d)); 262 repaint(); 263 return; 264 } 265 266 double totalMemory = Runtime.getRuntime().maxMemory(); 267 double highWaterMark = Runtime.getRuntime().totalMemory(); 268 double freeMemory = Runtime.getRuntime().freeMemory(); 269 double usedMemory = (highWaterMark - freeMemory); 270 271 double megabyte = 1024 * 1024; 272 273 totalMemory = totalMemory / megabyte; 274 usedMemory = usedMemory / megabyte; 275 highWaterMark = highWaterMark / megabyte; 276 277 long now = System.currentTimeMillis(); 278 if (lastTimeRanGC < 0) 279 lastTimeRanGC = now; 280 281 // For the threshold use the physical memory 282 int percent = (int)(100.0f * (usedMemory / totalMemory)); 283 if (percent > percentThreshold) { 284 timesAboveThreshold++; 285 if (timesAboveThreshold > 5) { 286 // Only run every 5 seconds 287 if (now - lastTimeRanGC > 5000) { 288 // For now just clear the cache. Don't run the gc 289 CacheManager.clearCache(); 290 // runGC(); 291 lastTimeRanGC = now; 292 } 293 } 294 int stretchedPercent = Math.round(((float)percent - (float)percentThreshold) * (100.0f / (100.0f - (float)percentThreshold))); 295 label.setBackground(doColorThing(stretchedPercent)); 296 } else { 297 timesAboveThreshold = 0; 298 lastTimeRanGC = now; 299 label.setBackground(doColorThing(0)); 300 } 301 302 // TODO: evaluate this method--should we really cancel stuff for the user? 303 // Decided that no, we shouldn't. At least not until we get a more bulletproof way of doing it. 304 // action:idv.stopload is unreliable and doesnt seem to stop object creation, just data loading. 305 if (percent > this.percentCancel) { 306 if (!triedToCancel) { 307 // System.err.println("Canceled the load... not much memory available"); 308 // idv.handleAction("action:idv.stopload"); 309 triedToCancel = true; 310 } 311 } 312 else { 313 triedToCancel = false; 314 } 315 316 label.setText(" " 317 + Msg.msg("Memory:") + " " 318 + fmt.format(usedMemory) + "/" 319 + fmt.format(highWaterMark) + "/" 320 + fmt.format(totalMemory) + " " + Msg.msg("MB") 321 + " "); 322 323 repaint(); 324 } 325 326 private Color doColorThing(final int percent) { 327 Float alpha = new Float(percent).floatValue() / 100; 328 return new Color(1.0f, 0.0f, 0.0f, alpha); 329 } 330 331 /** 332 * Run this monitor 333 */ 334 public void run() { 335 while (running) { 336 try { 337 showStats(); 338 Thread.sleep(sleepInterval); 339 } catch (Exception exc) { 340 } 341 } 342 } 343 344 /** 345 * Set whether we are running 346 * 347 * @param r true if we are running 348 */ 349 public void setRunning(final boolean r) { 350 running = r; 351 } 352 353 /** 354 * Get whether we are running 355 * 356 * @return true if we are 357 */ 358 public boolean getRunning() { 359 return running; 360 } 361 362 /** 363 * Test routine 364 * 365 * @param args not used 366 */ 367 public static void main(final String[] args) { 368 JFrame f = new JFrame(); 369 MemoryMonitor mm = new MemoryMonitor(null); 370 f.getContentPane().add(mm); 371 f.pack(); 372 f.setVisible(true); 373 } 374 375 }