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}