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 */