001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package org.conqat.lib.commons.system; 018 019/** 020 * Combines a timer and a memory monitor in a simple utility class. Measures 021 * both total memory and the delta between {@link PerformanceMonitor#start()} 022 * and {@link PerformanceMonitor#stop()}. 023 * <p> 024 * In order to avoid programming mistakes, the calls to the methods 025 * {@link PerformanceMonitor#start()} and {@link PerformanceMonitor#stop()} must 026 * adhere to a simple protocol:<br> 027 * The {@link PerformanceMonitor} can be in on of the three states NOT_RUN, 028 * RUNNING, FINISHED. 029 * <p> 030 * {@link PerformanceMonitor#start()} May only be called in state NOT_RUN and 031 * {@link PerformanceMonitor#stop()} may only be called in state RUNNING. 032 * <p> 033 * All other calls to {@link PerformanceMonitor#start()} and 034 * {@link PerformanceMonitor#stop()} result in {@link IllegalArgumentException}s 035 * 036 * @author Elmar Juergens 037 */ 038public class PerformanceMonitor { 039 040 /** The state the {@link PerformanceMonitor} is currently in */ 041 private EPerformanceMonitorState state = EPerformanceMonitorState.NOT_RUN; 042 043 /** {@link MemoryMonitor} used to measure total memory consumption */ 044 private MemoryMonitor memMonitor; 045 046 /** 047 * Flag that determines whether a memory monitor (that uses its own thread) 048 * should be used to get a more exact measurement of maximum memory consumption. 049 */ 050 private final boolean useMemoryMonitor; 051 052 /** 053 * Timestamp the call to the {@link PerformanceMonitor#start()} method occurred 054 */ 055 private long startTimeInMillis; 056 057 /** 058 * Memory consumption when the call to {@link PerformanceMonitor#start()} 059 * occurred. 060 */ 061 private long startMemoryInBytes; 062 063 /** 064 * The total amount of time spent between {@link #start()} and {@link #stop()} 065 * calls. 066 */ 067 private long totalSpentTimeInMillis = 0; 068 069 /** 070 * The maximum amount of memory used at all {@link #start()} and {@link #stop()} 071 * calls. 072 */ 073 private long maxMemoryInBytes = 0; 074 075 /** 076 * Constructor has package level to allow tests to access it, yet enforce use of 077 * factory methods for public use. 078 */ 079 public PerformanceMonitor(boolean useMemoryMonitor) { 080 this.useMemoryMonitor = useMemoryMonitor; 081 } 082 083 /** 084 * Convenience factory method: Creates a new {@link PerformanceMonitor} and 085 * starts it. 086 * 087 * @param useMemoryMonitor 088 * detemines whether the PerformanceMonitor internally uses a 089 * {@link MemoryMonitor} to measure memory consumption. This requires 090 * more resources, since the {@link MemoryMonitor} runs its own 091 * thread, but promises more precise measurement of maximum memory 092 * consumption. 093 */ 094 public static PerformanceMonitor create(boolean useMemoryMonitor) { 095 PerformanceMonitor monitor = new PerformanceMonitor(useMemoryMonitor); 096 monitor.start(); 097 return monitor; 098 } 099 100 /** 101 * Convenience factory method: Creates a new {@link PerformanceMonitor} and 102 * starts it. PerformanceMonitor does not use a MemoryMonitor internally 103 */ 104 public static PerformanceMonitor create() { 105 return create(false); 106 } 107 108 /** 109 * Starts the {@link PerformanceMonitor}. It will measure time and maximal 110 * memory consumption until the method {@link PerformanceMonitor#stop()} is 111 * called. 112 * <p> 113 * This method may only be called, if the {@link PerformanceMonitor} is in state 114 * NOT_RUN. (i.e., after it has been created via the constructor). Node: the 115 * create() method creates <b>and</b> starts the monitor. 116 * <p> 117 * All subsequent calls to this method will result in a 118 * {@link IllegalStateException} 119 */ 120 public void start() { 121 long currentMemoryInBytes = Runtime.getRuntime().totalMemory(); 122 123 if (state == EPerformanceMonitorState.RUNNING) { 124 throw new IllegalStateException("PerformanceMonitor is already running and cannot be restarted"); 125 } else if (state == EPerformanceMonitorState.NOT_RUN) { 126 startMemoryInBytes = currentMemoryInBytes; 127 } 128 129 state = EPerformanceMonitorState.RUNNING; 130 if (useMemoryMonitor) { 131 memMonitor = new MemoryMonitor(); 132 memMonitor.start(); 133 } 134 135 maxMemoryInBytes = Math.max(currentMemoryInBytes, maxMemoryInBytes); 136 startTimeInMillis = System.currentTimeMillis(); 137 } 138 139 /** 140 * Stops the {@link PerformanceMonitor}. 141 * <p> 142 * This method may only be called, if the {@link PerformanceMonitor} is in state 143 * RUNNING. (i.e., after a call to {@link PerformanceMonitor#start()} ). 144 * <p> 145 * If the {@link PerformanceMonitor} is in any other state, a call to this 146 * method results in an {@link IllegalStateException} 147 * 148 * @return The elapsed time in milliseconds. 149 */ 150 public long stop() { 151 if (state != EPerformanceMonitorState.RUNNING) { 152 throw new IllegalStateException("PerformanceMonitor can only be stopped if it is running"); 153 } 154 155 totalSpentTimeInMillis += System.currentTimeMillis() - startTimeInMillis; 156 maxMemoryInBytes = Math.max(Runtime.getRuntime().totalMemory(), maxMemoryInBytes); 157 state = EPerformanceMonitorState.FINISHED; 158 159 if (useMemoryMonitor) { 160 memMonitor.stop(); 161 } 162 163 return getMilliseconds(); 164 } 165 166 /** 167 * Returns whether the PerformanceMonitor is stopped and will return meaningful 168 * values. 169 */ 170 public boolean isStopped() { 171 return state == EPerformanceMonitorState.FINISHED; 172 } 173 174 /** 175 * Gets the measured time in seconds. (Fractions of seconds are discarded) 176 */ 177 public long getSeconds() { 178 return getMilliseconds() / 1000; 179 } 180 181 /** 182 * Gets the measured time in milliseconds. If measuring is not stopped, returns 183 * the time since start of the measuring. 184 */ 185 public long getMilliseconds() { 186 if (state == EPerformanceMonitorState.RUNNING) { 187 return System.currentTimeMillis() - startTimeInMillis + totalSpentTimeInMillis; 188 } 189 return totalSpentTimeInMillis; 190 } 191 192 /** Gets the maximal memory consumption in bytes */ 193 public long getMaxMemUsageInBytes() { 194 if (useMemoryMonitor) { 195 return memMonitor.getMaximumMemoryUsage(); 196 } 197 return maxMemoryInBytes; 198 } 199 200 /** Gets the maximal memory consumption in kilobytes */ 201 public long getMaxMemUsageInKBs() { 202 return getMaxMemUsageInBytes() / 1024; 203 } 204 205 /** Gets the delta in memory consumption in bytes */ 206 public long getDeltaMemUsageInBytes() { 207 return getMaxMemUsageInBytes() - startMemoryInBytes; 208 } 209 210 /** Gets the delta in memory consumption in kilobytes */ 211 public long getDeltaMemUsageInKBs() { 212 return getDeltaMemUsageInBytes() / 1024; 213 } 214 215}