001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.insane.scanner;
043:
044: import java.lang.ref.Reference;
045: import java.lang.reflect.Array;
046: import java.lang.reflect.Field;
047: import java.lang.reflect.Modifier;
048: import java.util.*;
049: import javax.swing.SwingUtilities;
050: import org.netbeans.insane.impl.InsaneEngine;
051:
052: /**
053: *
054: * @author Nenik
055: */
056: public final class ScannerUtils {
057:
058: /** Static factory class, no instance ever allowed */
059: private ScannerUtils() {
060: assert false;
061: }
062:
063: /** Creates a visitor that will wrap and delegate to more visitors
064: * during one scan
065: *
066: * @param parts aray of Visitors to delegate to.
067: * @return a wrapper Visitor
068: */
069: public static Visitor compoundVisitor(final Visitor[] parts) {
070: return new Visitor() {
071: private final Visitor[] sub = parts.clone();
072:
073: public void visitClass(Class cls) {
074: for (int i = 0; i < sub.length; i++)
075: sub[i].visitClass(cls);
076: }
077:
078: public void visitObject(ObjectMap map, Object object) {
079: for (int i = 0; i < sub.length; i++)
080: sub[i].visitObject(map, object);
081: }
082:
083: public void visitObjectReference(ObjectMap map,
084: Object from, Object to, Field ref) {
085: for (int i = 0; i < sub.length; i++)
086: sub[i].visitObjectReference(map, from, to, ref);
087: }
088:
089: public void visitArrayReference(ObjectMap map, Object from,
090: Object to, int index) {
091: for (int i = 0; i < sub.length; i++)
092: sub[i].visitArrayReference(map, from, to, index);
093: }
094:
095: public void visitStaticReference(ObjectMap map, Object to,
096: Field ref) {
097: for (int i = 0; i < sub.length; i++)
098: sub[i].visitStaticReference(map, to, ref);
099: }
100: };
101: }
102:
103: /** Creates a filter that will wrap and delegate to more filters, performing
104: * a logical and operation on their results
105: *
106: * @param parts aray of Filters to delegate to.
107: * @return a wrapper Filter
108: */
109: // does AND operation
110: public static Filter compoundFilter(final Filter[] parts) {
111: return new Filter() {
112: private final Filter[] sub = parts.clone();
113:
114: public boolean accept(Object o, Object r, Field ref) {
115: for (int i = 0; i < sub.length; i++) {
116: if (!sub[i].accept(o, r, ref))
117: return false;
118: }
119: return true;
120: }
121: };
122: }
123:
124: /**
125: * A Filter factory that creates a Filter ignoring given collection
126: * of objects and/or their outgoing references.
127: *
128: * @param except a Collection of objects to be ignored
129: * @param include whether ignore the objects themselves (false) or only
130: * their outgoing references (true).
131: * @return the Filter implementation
132: */
133: public static Filter skipObjectsFilter(Collection<Object> except,
134: final boolean include) {
135: class Except implements Filter {
136: private final IdentityHashMap<Object, Boolean> skip = new IdentityHashMap<Object, Boolean>();
137:
138: Except(Collection<Object> col) {
139: for (Iterator<Object> it = col.iterator(); it.hasNext(); skip
140: .put(it.next(), Boolean.TRUE))
141: ;
142: }
143:
144: public boolean accept(Object o, Object refFrom, Field ref) {
145: return !skip.containsKey(include ? refFrom : o);
146: }
147: }
148: return new Except(except);
149: }
150:
151: /**
152: * A Filter factory that creates a Filter ignoring given references
153: *
154: * @param except a Collection of Fields to be ignored.
155: * @return the Filter implementation
156: */
157: public static Filter skipReferencesFilter(
158: final Collection<Field> except) {
159: return new Filter() {
160: private final Set<Field> skip = new HashSet<Field>(except);
161:
162: public boolean accept(Object o, Object r, Field ref) {
163: return !skip.contains(ref);
164: }
165: };
166: }
167:
168: /**
169: * A Filter factory that creates a Filter ignoring weak and soft references.
170: *
171: * @return the Filter implementation
172: */
173: public static Filter skipNonStrongReferencesFilter() {
174: Class clsReference = Reference.class;
175: try {
176: Field referentOfReference = clsReference
177: .getDeclaredField("referent");
178: return skipReferencesFilter(Collections
179: .singleton(referentOfReference));
180: } catch (NoSuchFieldException x) {
181: NoSuchFieldError err = new NoSuchFieldError(x.toString());
182: err.initCause(x);
183: throw err;
184: }
185: }
186:
187: public static Filter noFilter() {
188: return new Filter() {
189: public boolean accept(Object o, Object r, Field ref) {
190: return true;
191: }
192: };
193: }
194:
195: /*
196: * Computes the amount of heap space used for a single object.
197: *
198: * @param o the object to be evaluated
199: * @return the size of the object, not counting the size
200: * of the objects referenced from this object
201: */
202: public static int sizeOf(Object o) {
203: return ClassInfo.sizeOf(o);
204: }
205:
206: /*
207: * Computes the amount of heap space used for a graph of objects.
208: *
209: * @param roots a Collection of objects to be evaluated.
210: * @param f a Filter for excluding objects. null means accept all objects.
211: * @return the size of all objects in the filtered transitive closure
212: * of the roots collection.
213: */
214: public static int recursiveSizeOf(Collection roots, Filter f)
215: throws Exception {
216: // XXX - can be more effective to compute the size in special-purpose visitor
217: CountingVisitor counter = new CountingVisitor();
218: scan(f, counter, roots, false);
219: return counter.getTotalSize();
220: }
221:
222: /**
223: * Traverse the graph of objects reachable from roots Collection, notifying
224: * the Visitor.
225: *
226: * @param f a Filter for excluding objects. null means accept all objects.
227: * @param v a Visitor to be notified on all found objects and references.
228: * @param roots a Collection of objects to be evaluated.
229: */
230: public static void scan(Filter f, Visitor v, Collection roots,
231: boolean analyzeStaticData) throws Exception {
232: new InsaneEngine(f, v, analyzeStaticData).traverse(roots);
233: }
234:
235: /**
236: * @return a set of objects that may be sufficient to transitively reference
237: * near all objects on the java heap.
238: */
239: public static Set<Object> interestingRoots() {
240: return new HashSet<Object>(Arrays.asList(new Object[] {
241: Thread.currentThread(),
242: ScannerUtils.class.getClassLoader() }));
243: }
244:
245: /**
246: * Traverse the graph of objects reachable from roots Collection, notifying
247: * the Visitor. It performs the scan from inside AWT queue and tries to
248: * suspend other threads during the scan.
249: *
250: * @param f a Filter for excluding objects. null means accept all objects.
251: * @param v a Visitor to be notified on all found objects and references.
252: * @param roots a Collection of objects to be evaluated.
253: */
254: public static void scanExclusivelyInAWT(final Filter f,
255: final Visitor v, final Set roots) throws Exception {
256: final Thread me = Thread.currentThread();
257: final Exception[] ret = new Exception[1];
258: Runnable performer = new Runnable() {
259: public void run() {
260: try {
261: // suspendAllThreads(new HashSet(Arrays.asList(new Object[] {me, Thread.currentThread()})));
262: scan(f, v, roots, true);
263: } catch (Exception e) {
264: ret[0] = e;
265: } finally {
266: // resumeAllThreads();
267: }
268: }
269: };
270: if (SwingUtilities.isEventDispatchThread()) {
271: performer.run();
272: } else {
273: SwingUtilities.invokeAndWait(performer);
274: }
275: if (ret[0] != null)
276: throw ret[0];
277: }
278:
279: private static Thread[] getAllThreads() {
280: ThreadGroup act = Thread.currentThread().getThreadGroup();
281: while (act.getParent() != null)
282: act = act.getParent();
283: Thread[] all = new Thread[2 * act.activeCount() + 5];
284: int cnt = act.enumerate(all);
285: Thread[] ret = new Thread[cnt];
286: System.arraycopy(all, 0, ret, 0, cnt);
287: return ret;
288: }
289:
290: @SuppressWarnings("deprecation")
291: private static void suspendAllThreads(Set except) {
292: Thread[] threads = getAllThreads();
293:
294: for (int i = 0; i < threads.length; i++) {
295: if (!except.contains(threads[i])) {
296: if ((threads[i].getName().indexOf("VM") == -1)
297: && (threads[i].getName().indexOf("CompilerTh") == -1)
298: && (threads[i].getName().indexOf("Signal") == -1)) {
299: System.out.println("suspending " + threads[i]);
300: threads[i].suspend();
301: }
302: }
303: }
304: }
305:
306: @SuppressWarnings("deprecation")
307: private static void resumeAllThreads() {
308: Thread[] threads = getAllThreads();
309:
310: for (int i = 0; i < threads.length; i++) {
311: threads[i].resume();
312: }
313: }
314:
315: private static class ClassInfo {
316: private static Map<Class, ClassInfo> classInfoRegistry = new WeakHashMap<Class, ClassInfo>();
317: private int size;
318:
319: private ClassInfo(Class cls) {
320: // this.cls = cls;
321: if (cls.isArray()) {
322: Class base = cls.getComponentType();
323: size = -getSize(base, true);
324: } else {
325: // iterate all fields and sum the sizes
326: int sum = 0;
327: for (Class act = cls; act != null; act = act
328: .getSuperclass()) {
329: Field[] flds = act.getDeclaredFields();
330: for (int i = 0; i < flds.length; i++) { // count all nonstatic fields
331: if ((flds[i].getModifiers() & Modifier.STATIC) == 0) {
332: sum += getSize(flds[i].getType(), false);
333: }
334: } // fields
335: } // classes
336: size = (sum + 8 + 7) & 0xFFFFFFF8; // 8byte align
337: }
338: }
339:
340: static int sizeOf(Object o) {
341: Class cls = o.getClass();
342: ClassInfo info = classInfoRegistry.get(cls);
343: if (info == null) {
344: info = new ClassInfo(cls);
345: classInfoRegistry.put(cls, info);
346: }
347:
348: return (info.size > 0) ? info.size : (19 - info.size
349: * Array.getLength(o)) & 0xFFFFFFF8;
350: }
351:
352: private static int getSize(Class type, boolean array) {
353: if (array) {
354: if (type == Byte.TYPE || type == Boolean.TYPE) {
355: return 1;
356: } else if (type == Short.TYPE || type == Character.TYPE) {
357: return 2;
358: } else if (type == Integer.TYPE || type == Float.TYPE) {
359: return 4;
360: } else if (type == Long.TYPE || type == Double.TYPE) {
361: return 8;
362: } else {
363: return 4; // some //subclass// of Object[]
364: }
365: } else {
366: if (type == Long.TYPE || type == Double.TYPE) {
367: return 8;
368: } else {
369: return 4;
370: }
371: }
372: }
373: }
374:
375: }
|