001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028package edu.wisc.ssec.mcidasv.monitors.memory;
029
030import java.awt.Color;
031import java.text.DecimalFormat;
032import java.util.List;
033import java.util.concurrent.CopyOnWriteArrayList;
034
035import javax.swing.SwingUtilities;
036
037import ucar.unidata.util.Msg;
038
039import edu.wisc.ssec.mcidasv.monitors.MonitorManager;
040import edu.wisc.ssec.mcidasv.monitors.Monitorable;
041import edu.wisc.ssec.mcidasv.monitors.Monitoring;
042
043public class MemoryMonitor implements Monitorable {
044
045    private final DecimalFormat fmt = new DecimalFormat("#0");
046
047    private final MonitorManager manager;
048
049    private final int percentThreshold;
050
051    private final int percentCancel;
052
053    private int timesAboveThreshold;
054
055    private long lastTimeRanGC = -1;
056
057    private final List<Monitoring> listeners = new CopyOnWriteArrayList<>();
058
059    public MemoryMonitor(final MonitorManager manager, final int threshold, final int cancel) {
060        this.manager = manager;
061        percentThreshold = threshold;
062        percentCancel = cancel;
063    }
064
065    public void addMonitor(final Monitoring listener) {
066        listeners.add(listener);
067    }
068
069    public void removeMonitor(final Monitoring listener) {
070        if (!listeners.isEmpty()) {
071            listeners.remove(listener);
072        }
073    }
074
075    public boolean hasMonitors() {
076        return !listeners.isEmpty();
077    }
078
079    public void run() {
080        double totalMem = Runtime.getRuntime().maxMemory();
081        double highMem = Runtime.getRuntime().totalMemory();
082        double freeMem = Runtime.getRuntime().freeMemory();
083        double usedMem = (highMem - freeMem);
084
085        totalMem = totalMem / 1048576;
086        usedMem = usedMem / 1048576;
087        highMem = highMem / 1048576;
088
089        int percent = (int)(100.0f * (usedMem / totalMem));
090        int stretchedPercent = 0;
091
092        long now = System.currentTimeMillis();
093        if (lastTimeRanGC < 0) {
094            lastTimeRanGC = now;
095        }
096
097        if (percent > percentThreshold) {
098            timesAboveThreshold++;
099            if (timesAboveThreshold > 5) {
100                if (now - lastTimeRanGC > 5000) {
101                    manager.scheduleClearCache();
102                    lastTimeRanGC = now;
103                }
104            }
105            stretchedPercent = Math.round((percent - percentThreshold) * (100.0f / (100.0f - percentThreshold)));
106        } else {
107            timesAboveThreshold = 0;
108            lastTimeRanGC = now;
109        }
110
111        String output = " " + Msg.msg("Memory:") + " " +
112                        fmt.format(usedMem) + "/" +
113                        fmt.format(highMem) + "/" +
114                        fmt.format(totalMem) + " " + Msg.msg("MB");
115        final MemoryMonitorEvent event = new MemoryMonitorEvent(this, doColorThing(stretchedPercent), output);
116        for (final Monitoring listener : listeners) {
117            SwingUtilities.invokeLater(new Runnable() {
118                public void run() {
119                    listener.monitorUpdated(event);
120                }
121            });
122        }
123    }
124
125    public static Color doColorThing(final int percent) {
126        Float alpha = new Float(percent).floatValue() / 100;
127        return new Color(1.0f, 0.0f, 0.0f, alpha);
128    }
129}