001    /*
002     * $Id: MemoryGuard.java,v 1.8 2011/07/09 21:43:22 oboehm Exp $
003     *
004     * Copyright (c) 2008 by Oliver Boehm
005     *
006     * Licensed under the Apache License, Version 2.0 (the "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     *
018     * (c)reated 19.01.2009 by oliver (ob@oasd.de)
019     */
020    package patterntesting.runtime.monitor;
021    
022    import org.slf4j.*;
023    
024    import patterntesting.runtime.util.Converter;
025    
026    /**
027     * The Class MemoryGuard.
028     *
029     * @author <a href="boehm@javatux.de">oliver</a>
030     * @since 19.01.2009
031     * @version $Revision: 1.8 $
032     */
033    public final class MemoryGuard {
034    
035        private static final Logger log = LoggerFactory.getLogger(MemoryGuard.class);
036        private static final long maxMem = Runtime.getRuntime().maxMemory();
037        private static BackgroundLogger backgroundTask;
038    
039        /** No need to instantiate it (utility class). */
040        private MemoryGuard() {}
041    
042        /**
043         * Gets the free memory.
044         *
045         * @return the free memory
046         */
047        public static long getFreeMemory() {
048            Runtime runtime = Runtime.getRuntime();
049            long totalMem = runtime.totalMemory();
050            return runtime.freeMemory() + (maxMem - totalMem);
051        }
052    
053        /**
054         * Gets the free memory in percent.
055         *
056         * @return the free memory in percent
057         */
058        public static int getFreeMemoryInPercent() {
059            long freeMem = getFreeMemory();
060            int rate = (int) ((freeMem + 50) * 100 / maxMem);
061            return rate;
062        }
063    
064        /**
065         * Gets the free memory as string.
066         *
067         * @return the free memory as string
068         */
069        public static String getFreeMemoryAsString() {
070            return Converter.getMemoryAsString(getFreeMemory());
071        }
072    
073        /**
074         * Log memory.
075         *
076         * @see #logMemory(Logger)
077         */
078        public static void logMemory() {
079            logMemory(log);
080        }
081    
082        /**
083         * Logs a message if free memory falls down below x% of maximal heap size
084         * where x is:
085         * <ul>
086         * <li>        x <  1%: logs a fatal message</li>
087         * <li>  1% <= x <  2%: logs an error</li>
088         * <li>  1% <= x < 10%: logs a warning</li>
089         * <li> 10% <= x < 20%: logs an info message</li>
090         * <li> 20% <= x < 50%: logs debug message</li>
091         * <li>   x >= 50%:     tracing</li>
092         * </ul>.
093         *
094         * @param lg the log
095         */
096        public static void logMemory(final Logger lg) {
097            int freeMemRate = getFreeMemoryInPercent();
098            if (freeMemRate < 10) {
099                System.gc();
100                if (lg.isTraceEnabled()) {
101                    lg.trace("gc() called because free memory is below 10% ("
102                            + freeMemRate + "%)");
103                }
104                freeMemRate = getFreeMemoryInPercent();
105            }
106            if (freeMemRate < 2) {
107                if (lg.isErrorEnabled()) {
108                    lg.error(getMemoryLogMessage(freeMemRate));
109                }
110            } else if (freeMemRate < 10) {
111                if (lg.isWarnEnabled()) {
112                    lg.warn(getMemoryLogMessage(freeMemRate));
113                }
114            } else if (freeMemRate < 20) {
115                if (lg.isInfoEnabled()) {
116                    lg.info(getMemoryLogMessage(freeMemRate));
117                }
118            } else if (freeMemRate < 50) {
119                if (lg.isDebugEnabled()) {
120                    lg.debug(getMemoryLogMessage(freeMemRate));
121                }
122            } else {
123                if (lg.isTraceEnabled()) {
124                    lg.trace(getMemoryLogMessage(freeMemRate));
125                }
126            }
127        }
128    
129        /**
130         * Gets the memory log message.
131         *
132         * @return the memory log message
133         */
134        public static String getMemoryLogMessage() {
135            int freeMemRate = getFreeMemoryInPercent();
136            return getMemoryLogMessage(freeMemRate);
137        }
138    
139        private static String getMemoryLogMessage(final int freeMemRate) {
140            return freeMemRate + "% of memory is free (" + getFreeMemoryAsString()
141                    + ")";
142        }
143    
144        /**
145         * Every x milliseconds the free memory is checked and printed into the log
146         * (according the log level). This is done with in a separated background
147         * thread. If there is already a background thread running it will be
148         * suspended before a new background thread will be started.
149         *
150         * @param interval (the sleep intervall in milliseconds, e.g. 300000 for 5 min.)
151         *
152         * @throws InterruptedException the interrupted exception
153         */
154        public static synchronized void logFreeMemory(final long interval)
155                            throws InterruptedException {
156            if (log.isTraceEnabled()) {
157                if (backgroundTask == null) {
158                            backgroundTask = new BackgroundLogger(interval);
159                            log.trace("starting " + backgroundTask + "...");
160                    Thread backgroundThread = new Thread(backgroundTask, "background");
161                    backgroundThread.setDaemon(true);
162                    backgroundThread.setPriority(Thread.MIN_PRIORITY);
163                    backgroundThread.start();
164                    } else {
165                            backgroundTask.setInterval(interval);
166                            log.trace(backgroundTask + " changed");
167                    }
168            }
169        }
170    
171    
172    
173        /**
174         * This inner class do the job for us. It polls the memory size and logs
175         * it.
176         *
177         * @author oliver
178         * @since 01.07.2009
179         */
180        static class BackgroundLogger implements Runnable {
181    
182            private long interval;
183    
184                /**
185                 * Instantiates a new background logger.
186                 *
187                 * @param interval the interval
188                 */
189                public BackgroundLogger(final long interval) {
190                    this.interval = interval;
191            }
192    
193                /**
194                 * Sets the interval.
195                 *
196                 * @param interval the new interval
197                 */
198                public void setInterval(final long interval) {
199                    this.interval = interval;
200            }
201    
202            /**
203             * @see java.lang.Runnable#run()
204             */
205            public void run() {
206                while (true) {
207                    logMemory();
208                    try {
209                        Thread.sleep(interval);
210                    } catch (InterruptedException e) {
211                        log.debug(this + " interrupted", e);
212                        break;
213                    }
214                }
215            }
216    
217                    /**
218                     * @see java.lang.Object#toString()
219                     */
220                    @Override
221                    public String toString() {
222                            return this.getClass().getSimpleName() + "(" + interval + "ms)";
223                    }
224        }
225    
226    }
227    
228    /**
229     * $Log: MemoryGuard.java,v $
230     * Revision 1.8  2011/07/09 21:43:22  oboehm
231     * switched from commons-logging to SLF4J
232     *
233     * Revision 1.7  2010/07/22 16:50:45  oboehm
234     * checkstyle fixing and tuning
235     *
236     * Revision 1.6  2010/07/05 20:26:55  oboehm
237     * background logger is only started in TRACE level
238     *
239     * Revision 1.5  2010/06/08 21:32:53  oboehm
240     * nearly half of the checkstyle warnings fixed
241     *
242     * Revision 1.4  2010/04/22 18:32:01  oboehm
243     * compiler warnings fixed
244     *
245     * Revision 1.3  2010/04/22 18:27:19  oboehm
246     * code cleanup of src/main/java
247     *
248     * Revision 1.2  2010/01/06 15:57:16  oboehm
249     * some FindBug warnings removed
250     * warning level increased
251     *
252     * Revision 1.1  2010/01/05 13:26:17  oboehm
253     * begin with 1.0
254     *
255     * Revision 1.13  2009/12/28 10:07:23  oboehm
256     * missing Javadocs completed
257     *
258     * Revision 1.12  2009/12/19 22:34:09  oboehm
259     * trailing spaces removed
260     *
261     * Revision 1.11  2009/12/14 17:14:31  oboehm
262     * trailing tabs removed
263     *
264     * Revision 1.10  2009/09/25 14:49:43  oboehm
265     * javadocs completed with the help of JAutodoc
266     *
267     * Revision 1.9  2009/07/04 10:54:03  oboehm
268     * logFreeMemory can now be called more than once
269     *
270     * Revision 1.8  2009/03/24 08:51:44  oboehm
271     * compiler warning fixed
272     *
273     * Revision 1.7  2009/03/24 08:51:05  oboehm
274     * RunBackgroundAspect no longer available - manual implemented
275     *
276     * $Source: /cvsroot/patterntesting/PatternTesting10/patterntesting-rt/src/main/java/patterntesting/runtime/monitor/MemoryGuard.java,v $
277     */