001 /**
002 * Copyright (c) 2010, SIB. All rights reserved.
003 *
004 * SIB (Swiss Institute of Bioinformatics) - http://www.isb-sib.ch Host -
005 * https://sourceforge.net/projects/javaprotlib/
006 *
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions are met:
009 * Redistributions of source code must retain the above copyright notice, this
010 * list of conditions and the following disclaimer. Redistributions in binary
011 * form must reproduce the above copyright notice, this list of conditions and
012 * the following disclaimer in the documentation and/or other materials provided
013 * with the distribution. Neither the name of the SIB/GENEBIO nor the names of
014 * its contributors may be used to endorse or promote products derived from this
015 * software without specific prior written permission.
016 *
017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020 * ARE DISCLAIMED. IN NO EVENT SHALL SIB/GENEBIO BE LIABLE FOR ANY DIRECT,
021 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
024 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 */
028 package org.expasy.jpl.commons.base.mem;
029
030
031 import java.io.FileNotFoundException;
032 import java.io.FileOutputStream;
033 import java.io.PrintStream;
034 import java.lang.instrument.Instrumentation;
035 import java.lang.reflect.Array;
036 import java.lang.reflect.Field;
037 import java.lang.reflect.Modifier;
038 import java.util.IdentityHashMap;
039 import java.util.Map;
040 import java.util.Stack;
041
042
043 /**
044 * This agent trace sizeof of running objects.
045 *
046 * Implemented and extended code from
047 * http://www.javaspecialists.eu/archive/Issue142.html
048 *
049 * @author nikitin
050 *
051 * @version 1.0.0
052 *
053 */
054 public final class MemoryAgent {
055
056 private static volatile Instrumentation instrumentation;
057
058 private static PrintStream ps;
059
060 /** Flyweight objects are immutable and in cache for reusability */
061 private static boolean isSharedFlyWeightProfiled = false;
062 private static boolean isTraceEnabled = false;
063 private static boolean isPrettyByte = false;
064
065 /** Initializes agent */
066 public static void premain(final String agentArgs,
067 final Instrumentation instrumentation) {
068 MemoryAgent.instrumentation = instrumentation;
069 flushInStdout();
070 }
071
072 /** flush output in file */
073 public static void flushInFile(String filename)
074 throws FileNotFoundException {
075 ps = new PrintStream(new FileOutputStream(filename));
076 }
077
078 /** flush output in standard output stream */
079 public static void flushInStdout() {
080 ps = System.out;
081 }
082
083 /** trace enabled/disabled */
084 public static void traceEnabled() {
085 isTraceEnabled = true;
086 }
087
088 public static void traceDisabled() {
089 isTraceEnabled = false;
090 }
091
092 public static void prettyByteEnabled() {
093 isPrettyByte = true;
094 }
095
096 public static void prettyByteDisabled() {
097 isPrettyByte = false;
098 }
099
100 private static void setCountSharedFlyWeight(final boolean bool) {
101 isSharedFlyWeightProfiled = bool;
102 }
103
104 public static void switchFlyweightProfilingOn() {
105 setCountSharedFlyWeight(true);
106 }
107
108 public static void switchFlyweightProfilingOff() {
109 setCountSharedFlyWeight(false);
110 }
111
112 /** Returns object size. */
113 public static long sizeOf(final Object obj) {
114 if (instrumentation == null) {
115 throw new IllegalStateException("MemoryAgent not initialised.");
116 }
117
118 if (!isSharedFlyWeightProfiled) {
119 if (isSharedFlyweight(obj)) {
120 return 0;
121 }
122 }
123 return instrumentation.getObjectSize(obj);
124 }
125
126 /**
127 * Returns deep size of object, recursively iterating over its fields and
128 * superclasses.
129 */
130 public static long deepSizeOf(Object obj) {
131 final Map<Object, Object> visited =
132 new IdentityHashMap<Object, Object>();
133 final Stack<Object> stack = new Stack<Object>();
134
135 stack.push(obj);
136
137 if (isTraceEnabled) {
138 ps.println("push " + obj.getClass().getName());
139 }
140 long result = 0;
141 do {
142 obj = stack.pop();
143 if (obj != null) {
144 // get the size of obj
145 final long size = internalSizeOf(obj, stack, visited);
146
147 result += size;
148
149 if (isTraceEnabled) {
150 ps.println("pop " + obj.getClass().getName() + ": " + obj);
151 ps.println("sizeof = " + size);
152 ps.println("sizeof cumul = " + result);
153 }
154 } else {
155 if (isTraceEnabled) {
156 ps.println("[null object]");
157 }
158 }
159 } while (!stack.isEmpty());
160 return result;
161 }
162
163 public static void traceSizesOf(Object o) {
164 long memShallow = MemoryAgent.sizeOf(o);
165 long memDeep = MemoryAgent.deepSizeOf(o);
166
167 String byteUnit = "B";
168
169 String shalUnit = byteUnit;
170 String deepUnit = byteUnit;
171
172 if (isPrettyByte) {
173 String kiloByteUnit = "KB";
174 String megaByteUnit = "MB";
175
176 if (memShallow / 1000 > 0) {
177 memShallow /= 1000;
178 shalUnit = kiloByteUnit;
179 if (memShallow / 1000 > 0) {
180 memShallow /= 1000;
181 shalUnit = megaByteUnit;
182 }
183 }
184
185 if (memDeep / 1000 > 0) {
186 memDeep /= 1000;
187 deepUnit = kiloByteUnit;
188 if (memDeep / 1000 > 0) {
189 memDeep /= 1000;
190 deepUnit = megaByteUnit;
191 }
192 }
193 }
194
195 System.out.printf("%s, shallow=%d%s, deep=%d%s, flyweight=%s%n", o
196 .getClass().getSimpleName(), memShallow, shalUnit, memDeep,
197 deepUnit, (isSharedFlyWeightProfiled) ? "switch on" : "switch off");
198
199 }
200
201 /**
202 * Returns true if this is a well-known shared flyweight. For example,
203 * interned Strings, Booleans and Number objects
204 */
205 @SuppressWarnings("unchecked")
206 public static boolean isSharedFlyweight(final Object obj) {
207 // optimization - all of our flyweights are Comparable
208 if (obj instanceof Comparable) {
209 if (obj instanceof Enum) {
210 return true;
211 } else if (obj instanceof String) {
212 return (obj == ((String) obj).intern());
213 } else if (obj instanceof Boolean) {
214 return ((obj == Boolean.TRUE) || (obj == Boolean.FALSE));
215 } else if (obj instanceof Integer) {
216 return (obj == Integer.valueOf((Integer) obj));
217 } else if (obj instanceof Short) {
218 return (obj == Short.valueOf((Short) obj));
219 } else if (obj instanceof Byte) {
220 return (obj == Byte.valueOf((Byte) obj));
221 } else if (obj instanceof Long) {
222 return (obj == Long.valueOf((Long) obj));
223 } else if (obj instanceof Character) {
224 return (obj == Character.valueOf((Character) obj));
225 }
226 }
227 return false;
228 }
229
230 private static boolean skipObject(final Object obj,
231 final Map<Object, Object> visited) {
232 return (obj == null) || visited.containsKey(obj)
233 || (!isSharedFlyWeightProfiled && isSharedFlyweight(obj));
234 }
235
236 /**
237 * Visit all non static fields of {@code obj} (+ all super classes) to visit
238 * and push every objects in a stack to estimate interal size.
239 *
240 * @param obj the object to estimate internal size.
241 * @param stack the stack of intern objects to estimate size later.
242 * @param visited a map of all visited objects.
243 *
244 * @return the size of {@code obj}.
245 */
246 private static long internalSizeOf(final Object obj,
247 final Stack<Object> stack, final Map<Object, Object> visited) {
248 StringBuffer sb = null;
249
250 if (isTraceEnabled) {
251 sb = new StringBuffer();
252 }
253
254 if (skipObject(obj, visited)) {
255
256 if (isTraceEnabled) {
257 ps.println("skip " + obj + ", visited ? "
258 + visited.containsKey(obj) + ", is shared flyweight ? "
259 + isSharedFlyweight(obj));
260 }
261 return 0;
262 }
263
264 Class<?> clazz = obj.getClass();
265
266 if (clazz.isArray()) {
267 addArrayElementsToStack(clazz, obj, stack);
268 } else {
269 // add all non-primitive fields to the stack
270 while (clazz != null) {
271 final Field[] fields = clazz.getDeclaredFields();
272 for (final Field field : fields) {
273 if (!Modifier.isStatic(field.getModifiers())
274 && !field.getType().isPrimitive()) {
275 field.setAccessible(true);
276 try {
277 if (isTraceEnabled) {
278 sb.append("push field " + field.getName()
279 + ": " + field.getType().getSimpleName());
280 }
281 stack.add(field.get(obj));
282 } catch (final IllegalAccessException ex) {
283 throw new RuntimeException(ex);
284 }
285 } else {
286 if (isTraceEnabled) {
287 sb.append("[skip static field " + field.getName()
288 + ": " + field.getType().getSimpleName() + "]");
289 }
290 }
291 if (isTraceEnabled) {
292 sb.append("\n");
293 }
294 }
295
296 clazz = clazz.getSuperclass();
297
298 if (clazz != null) {
299 if (isTraceEnabled) {
300 sb.append("[--> goto superclass "
301 + clazz.getSimpleName() + "]");
302 }
303 }
304 }
305 }
306 visited.put(obj, null);
307
308 if (isTraceEnabled) {
309 if (sb.length() > 0) {
310 ps.println(sb);
311 }
312 }
313 return sizeOf(obj);
314 }
315
316 private static void addArrayElementsToStack(final Class<?> clazz,
317 final Object obj, final Stack<Object> stack) {
318
319 StringBuffer sb = null;
320
321 if (isTraceEnabled) {
322 sb = new StringBuffer();
323 sb.append("[push all elements of array " + clazz.getName()
324 + " (sz = " + Array.getLength(obj) + ")]\n");
325 }
326
327 if (!clazz.getComponentType().isPrimitive()) {
328 final int length = Array.getLength(obj);
329 for (int i = 0; i < length; i++) {
330 if (Array.get(obj, i) != null) {
331 if (isTraceEnabled) {
332 sb.append("push element "
333 + Array.get(obj, i).getClass().getSimpleName()
334 + "\n");
335 }
336 stack.add(Array.get(obj, i));
337 } else {
338 if (isTraceEnabled) {
339 sb.append("[skip null element]\n");
340 }
341 }
342 }
343 } else {
344 if (isTraceEnabled) {
345 sb.append("[no elements to push for a primitive array]");
346 }
347 }
348
349 if (isTraceEnabled) {
350 ps.println(sb);
351 }
352 }
353
354 }