631 lines
20 KiB
Java
631 lines
20 KiB
Java
/*
|
|
* Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
|
|
/*
|
|
* The Original Code is HAT. The Initial Developer of the
|
|
* Original Code is Bill Foote, with contributions from others
|
|
* at JavaSoft/Sun.
|
|
*/
|
|
|
|
package com.sun.tools.hat.internal.model;
|
|
|
|
import java.lang.ref.SoftReference;
|
|
import java.util.*;
|
|
import com.sun.tools.hat.internal.parser.ReadBuffer;
|
|
import com.sun.tools.hat.internal.util.Misc;
|
|
|
|
/**
|
|
*
|
|
* @author Bill Foote
|
|
*/
|
|
|
|
/**
|
|
* Represents a snapshot of the Java objects in the VM at one instant.
|
|
* This is the top-level "model" object read out of a single .hprof or .bod
|
|
* file.
|
|
*/
|
|
|
|
public class Snapshot {
|
|
|
|
public static long SMALL_ID_MASK = 0x0FFFFFFFFL;
|
|
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
|
|
|
private static final JavaField[] EMPTY_FIELD_ARRAY = new JavaField[0];
|
|
private static final JavaStatic[] EMPTY_STATIC_ARRAY = new JavaStatic[0];
|
|
|
|
// all heap objects
|
|
private Hashtable<Number, JavaHeapObject> heapObjects =
|
|
new Hashtable<Number, JavaHeapObject>();
|
|
|
|
private Hashtable<Number, JavaClass> fakeClasses =
|
|
new Hashtable<Number, JavaClass>();
|
|
|
|
// all Roots in this Snapshot
|
|
private Vector<Root> roots = new Vector<Root>();
|
|
|
|
// name-to-class map
|
|
private Map<String, JavaClass> classes =
|
|
new TreeMap<String, JavaClass>();
|
|
|
|
// new objects relative to a baseline - lazily initialized
|
|
private volatile Map<JavaHeapObject, Boolean> newObjects;
|
|
|
|
// allocation site traces for all objects - lazily initialized
|
|
private volatile Map<JavaHeapObject, StackTrace> siteTraces;
|
|
|
|
// object-to-Root map for all objects
|
|
private Map<JavaHeapObject, Root> rootsMap =
|
|
new HashMap<JavaHeapObject, Root>();
|
|
|
|
// soft cache of finalizeable objects - lazily initialized
|
|
private SoftReference<Vector> finalizablesCache;
|
|
|
|
// represents null reference
|
|
private JavaThing nullThing;
|
|
|
|
// java.lang.ref.Reference class
|
|
private JavaClass weakReferenceClass;
|
|
// index of 'referent' field in java.lang.ref.Reference class
|
|
private int referentFieldIndex;
|
|
|
|
// java.lang.Class class
|
|
private JavaClass javaLangClass;
|
|
// java.lang.String class
|
|
private JavaClass javaLangString;
|
|
// java.lang.ClassLoader class
|
|
private JavaClass javaLangClassLoader;
|
|
|
|
// unknown "other" array class
|
|
private volatile JavaClass otherArrayType;
|
|
// Stuff to exclude from reachable query
|
|
private ReachableExcludes reachableExcludes;
|
|
// the underlying heap dump buffer
|
|
private ReadBuffer readBuf;
|
|
|
|
// True iff some heap objects have isNew set
|
|
private boolean hasNewSet;
|
|
private boolean unresolvedObjectsOK;
|
|
|
|
// whether object array instances have new style class or
|
|
// old style (element) class.
|
|
private boolean newStyleArrayClass;
|
|
|
|
// object id size in the heap dump
|
|
private int identifierSize = 4;
|
|
|
|
// minimum object size - accounts for object header in
|
|
// most Java virtual machines - we assume 2 identifierSize
|
|
// (which is true for Sun's hotspot JVM).
|
|
private int minimumObjectSize;
|
|
|
|
public Snapshot(ReadBuffer buf) {
|
|
nullThing = new HackJavaValue("<null>", 0);
|
|
readBuf = buf;
|
|
}
|
|
|
|
public void setSiteTrace(JavaHeapObject obj, StackTrace trace) {
|
|
if (trace != null && trace.getFrames().length != 0) {
|
|
initSiteTraces();
|
|
siteTraces.put(obj, trace);
|
|
}
|
|
}
|
|
|
|
public StackTrace getSiteTrace(JavaHeapObject obj) {
|
|
if (siteTraces != null) {
|
|
return siteTraces.get(obj);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void setNewStyleArrayClass(boolean value) {
|
|
newStyleArrayClass = value;
|
|
}
|
|
|
|
public boolean isNewStyleArrayClass() {
|
|
return newStyleArrayClass;
|
|
}
|
|
|
|
public void setIdentifierSize(int size) {
|
|
identifierSize = size;
|
|
minimumObjectSize = 2 * size;
|
|
}
|
|
|
|
public int getIdentifierSize() {
|
|
return identifierSize;
|
|
}
|
|
|
|
public int getMinimumObjectSize() {
|
|
return minimumObjectSize;
|
|
}
|
|
|
|
public void addHeapObject(long id, JavaHeapObject ho) {
|
|
heapObjects.put(makeId(id), ho);
|
|
}
|
|
|
|
public void addRoot(Root r) {
|
|
r.setIndex(roots.size());
|
|
roots.addElement(r);
|
|
}
|
|
|
|
public void addClass(long id, JavaClass c) {
|
|
addHeapObject(id, c);
|
|
putInClassesMap(c);
|
|
}
|
|
|
|
JavaClass addFakeInstanceClass(long classID, int instSize) {
|
|
// Create a fake class name based on ID.
|
|
String name = "unknown-class<@" + Misc.toHex(classID) + ">";
|
|
|
|
// Create fake fields convering the given instance size.
|
|
// Create as many as int type fields and for the left over
|
|
// size create byte type fields.
|
|
int numInts = instSize / 4;
|
|
int numBytes = instSize % 4;
|
|
JavaField[] fields = new JavaField[numInts + numBytes];
|
|
int i;
|
|
for (i = 0; i < numInts; i++) {
|
|
fields[i] = new JavaField("unknown-field-" + i, "I");
|
|
}
|
|
for (i = 0; i < numBytes; i++) {
|
|
fields[i + numInts] = new JavaField("unknown-field-" +
|
|
i + numInts, "B");
|
|
}
|
|
|
|
// Create fake instance class
|
|
JavaClass c = new JavaClass(name, 0, 0, 0, 0, fields,
|
|
EMPTY_STATIC_ARRAY, instSize);
|
|
// Add the class
|
|
addFakeClass(makeId(classID), c);
|
|
return c;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return true iff it's possible that some JavaThing instances might
|
|
* isNew set
|
|
*
|
|
* @see JavaThing.isNew()
|
|
*/
|
|
public boolean getHasNewSet() {
|
|
return hasNewSet;
|
|
}
|
|
|
|
//
|
|
// Used in the body of resolve()
|
|
//
|
|
private static class MyVisitor extends AbstractJavaHeapObjectVisitor {
|
|
JavaHeapObject t;
|
|
public void visit(JavaHeapObject other) {
|
|
other.addReferenceFrom(t);
|
|
}
|
|
}
|
|
|
|
// To show heap parsing progress, we print a '.' after this limit
|
|
private static final int DOT_LIMIT = 5000;
|
|
|
|
/**
|
|
* Called after reading complete, to initialize the structure
|
|
*/
|
|
public void resolve(boolean calculateRefs) {
|
|
System.out.println("Resolving " + heapObjects.size() + " objects...");
|
|
|
|
// First, resolve the classes. All classes must be resolved before
|
|
// we try any objects, because the objects use classes in their
|
|
// resolution.
|
|
javaLangClass = findClass("java.lang.Class");
|
|
if (javaLangClass == null) {
|
|
System.out.println("WARNING: hprof file does not include java.lang.Class!");
|
|
javaLangClass = new JavaClass("java.lang.Class", 0, 0, 0, 0,
|
|
EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0);
|
|
addFakeClass(javaLangClass);
|
|
}
|
|
javaLangString = findClass("java.lang.String");
|
|
if (javaLangString == null) {
|
|
System.out.println("WARNING: hprof file does not include java.lang.String!");
|
|
javaLangString = new JavaClass("java.lang.String", 0, 0, 0, 0,
|
|
EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0);
|
|
addFakeClass(javaLangString);
|
|
}
|
|
javaLangClassLoader = findClass("java.lang.ClassLoader");
|
|
if (javaLangClassLoader == null) {
|
|
System.out.println("WARNING: hprof file does not include java.lang.ClassLoader!");
|
|
javaLangClassLoader = new JavaClass("java.lang.ClassLoader", 0, 0, 0, 0,
|
|
EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0);
|
|
addFakeClass(javaLangClassLoader);
|
|
}
|
|
|
|
for (JavaHeapObject t : heapObjects.values()) {
|
|
if (t instanceof JavaClass) {
|
|
t.resolve(this);
|
|
}
|
|
}
|
|
|
|
// Now, resolve everything else.
|
|
for (JavaHeapObject t : heapObjects.values()) {
|
|
if (!(t instanceof JavaClass)) {
|
|
t.resolve(this);
|
|
}
|
|
}
|
|
|
|
heapObjects.putAll(fakeClasses);
|
|
fakeClasses.clear();
|
|
|
|
weakReferenceClass = findClass("java.lang.ref.Reference");
|
|
if (weakReferenceClass == null) { // JDK 1.1.x
|
|
weakReferenceClass = findClass("sun.misc.Ref");
|
|
referentFieldIndex = 0;
|
|
} else {
|
|
JavaField[] fields = weakReferenceClass.getFieldsForInstance();
|
|
for (int i = 0; i < fields.length; i++) {
|
|
if ("referent".equals(fields[i].getName())) {
|
|
referentFieldIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (calculateRefs) {
|
|
calculateReferencesToObjects();
|
|
System.out.print("Eliminating duplicate references");
|
|
System.out.flush();
|
|
// This println refers to the *next* step
|
|
}
|
|
int count = 0;
|
|
for (JavaHeapObject t : heapObjects.values()) {
|
|
t.setupReferers();
|
|
++count;
|
|
if (calculateRefs && count % DOT_LIMIT == 0) {
|
|
System.out.print(".");
|
|
System.out.flush();
|
|
}
|
|
}
|
|
if (calculateRefs) {
|
|
System.out.println("");
|
|
}
|
|
|
|
// to ensure that Iterator.remove() on getClasses()
|
|
// result will throw exception..
|
|
classes = Collections.unmodifiableMap(classes);
|
|
}
|
|
|
|
private void calculateReferencesToObjects() {
|
|
System.out.print("Chasing references, expect "
|
|
+ (heapObjects.size() / DOT_LIMIT) + " dots");
|
|
System.out.flush();
|
|
int count = 0;
|
|
MyVisitor visitor = new MyVisitor();
|
|
for (JavaHeapObject t : heapObjects.values()) {
|
|
visitor.t = t;
|
|
// call addReferenceFrom(t) on all objects t references:
|
|
t.visitReferencedObjects(visitor);
|
|
++count;
|
|
if (count % DOT_LIMIT == 0) {
|
|
System.out.print(".");
|
|
System.out.flush();
|
|
}
|
|
}
|
|
System.out.println();
|
|
for (Root r : roots) {
|
|
r.resolve(this);
|
|
JavaHeapObject t = findThing(r.getId());
|
|
if (t != null) {
|
|
t.addReferenceFromRoot(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void markNewRelativeTo(Snapshot baseline) {
|
|
hasNewSet = true;
|
|
for (JavaHeapObject t : heapObjects.values()) {
|
|
boolean isNew;
|
|
long thingID = t.getId();
|
|
if (thingID == 0L || thingID == -1L) {
|
|
isNew = false;
|
|
} else {
|
|
JavaThing other = baseline.findThing(t.getId());
|
|
if (other == null) {
|
|
isNew = true;
|
|
} else {
|
|
isNew = !t.isSameTypeAs(other);
|
|
}
|
|
}
|
|
t.setNew(isNew);
|
|
}
|
|
}
|
|
|
|
public Enumeration<JavaHeapObject> getThings() {
|
|
return heapObjects.elements();
|
|
}
|
|
|
|
|
|
public JavaHeapObject findThing(long id) {
|
|
Number idObj = makeId(id);
|
|
JavaHeapObject jho = heapObjects.get(idObj);
|
|
return jho != null? jho : fakeClasses.get(idObj);
|
|
}
|
|
|
|
public JavaHeapObject findThing(String id) {
|
|
return findThing(Misc.parseHex(id));
|
|
}
|
|
|
|
public JavaClass findClass(String name) {
|
|
if (name.startsWith("0x")) {
|
|
return (JavaClass) findThing(name);
|
|
} else {
|
|
return classes.get(name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return an Iterator of all of the classes in this snapshot.
|
|
**/
|
|
public Iterator getClasses() {
|
|
// note that because classes is a TreeMap
|
|
// classes are already sorted by name
|
|
return classes.values().iterator();
|
|
}
|
|
|
|
public JavaClass[] getClassesArray() {
|
|
JavaClass[] res = new JavaClass[classes.size()];
|
|
classes.values().toArray(res);
|
|
return res;
|
|
}
|
|
|
|
public synchronized Enumeration getFinalizerObjects() {
|
|
Vector obj;
|
|
if (finalizablesCache != null &&
|
|
(obj = finalizablesCache.get()) != null) {
|
|
return obj.elements();
|
|
}
|
|
|
|
JavaClass clazz = findClass("java.lang.ref.Finalizer");
|
|
JavaObject queue = (JavaObject) clazz.getStaticField("queue");
|
|
JavaThing tmp = queue.getField("head");
|
|
Vector<JavaHeapObject> finalizables = new Vector<JavaHeapObject>();
|
|
if (tmp != getNullThing()) {
|
|
JavaObject head = (JavaObject) tmp;
|
|
while (true) {
|
|
JavaHeapObject referent = (JavaHeapObject) head.getField("referent");
|
|
JavaThing next = head.getField("next");
|
|
if (next == getNullThing() || next.equals(head)) {
|
|
break;
|
|
}
|
|
head = (JavaObject) next;
|
|
finalizables.add(referent);
|
|
}
|
|
}
|
|
finalizablesCache = new SoftReference<Vector>(finalizables);
|
|
return finalizables.elements();
|
|
}
|
|
|
|
public Enumeration<Root> getRoots() {
|
|
return roots.elements();
|
|
}
|
|
|
|
public Root[] getRootsArray() {
|
|
Root[] res = new Root[roots.size()];
|
|
roots.toArray(res);
|
|
return res;
|
|
}
|
|
|
|
public Root getRootAt(int i) {
|
|
return roots.elementAt(i);
|
|
}
|
|
|
|
public ReferenceChain[]
|
|
rootsetReferencesTo(JavaHeapObject target, boolean includeWeak) {
|
|
Vector<ReferenceChain> fifo = new Vector<ReferenceChain>(); // This is slow... A real fifo would help
|
|
// Must be a fifo to go breadth-first
|
|
Hashtable<JavaHeapObject, JavaHeapObject> visited = new Hashtable<JavaHeapObject, JavaHeapObject>();
|
|
// Objects are added here right after being added to fifo.
|
|
Vector<ReferenceChain> result = new Vector<ReferenceChain>();
|
|
visited.put(target, target);
|
|
fifo.addElement(new ReferenceChain(target, null));
|
|
|
|
while (fifo.size() > 0) {
|
|
ReferenceChain chain = fifo.elementAt(0);
|
|
fifo.removeElementAt(0);
|
|
JavaHeapObject curr = chain.getObj();
|
|
if (curr.getRoot() != null) {
|
|
result.addElement(chain);
|
|
// Even though curr is in the rootset, we want to explore its
|
|
// referers, because they might be more interesting.
|
|
}
|
|
Enumeration referers = curr.getReferers();
|
|
while (referers.hasMoreElements()) {
|
|
JavaHeapObject t = (JavaHeapObject) referers.nextElement();
|
|
if (t != null && !visited.containsKey(t)) {
|
|
if (includeWeak || !t.refersOnlyWeaklyTo(this, curr)) {
|
|
visited.put(t, t);
|
|
fifo.addElement(new ReferenceChain(t, chain));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReferenceChain[] realResult = new ReferenceChain[result.size()];
|
|
for (int i = 0; i < result.size(); i++) {
|
|
realResult[i] = result.elementAt(i);
|
|
}
|
|
return realResult;
|
|
}
|
|
|
|
public boolean getUnresolvedObjectsOK() {
|
|
return unresolvedObjectsOK;
|
|
}
|
|
|
|
public void setUnresolvedObjectsOK(boolean v) {
|
|
unresolvedObjectsOK = v;
|
|
}
|
|
|
|
public JavaClass getWeakReferenceClass() {
|
|
return weakReferenceClass;
|
|
}
|
|
|
|
public int getReferentFieldIndex() {
|
|
return referentFieldIndex;
|
|
}
|
|
|
|
public JavaThing getNullThing() {
|
|
return nullThing;
|
|
}
|
|
|
|
public void setReachableExcludes(ReachableExcludes e) {
|
|
reachableExcludes = e;
|
|
}
|
|
|
|
public ReachableExcludes getReachableExcludes() {
|
|
return reachableExcludes;
|
|
}
|
|
|
|
// package privates
|
|
void addReferenceFromRoot(Root r, JavaHeapObject obj) {
|
|
Root root = rootsMap.get(obj);
|
|
if (root == null) {
|
|
rootsMap.put(obj, r);
|
|
} else {
|
|
rootsMap.put(obj, root.mostInteresting(r));
|
|
}
|
|
}
|
|
|
|
Root getRoot(JavaHeapObject obj) {
|
|
return rootsMap.get(obj);
|
|
}
|
|
|
|
JavaClass getJavaLangClass() {
|
|
return javaLangClass;
|
|
}
|
|
|
|
JavaClass getJavaLangString() {
|
|
return javaLangString;
|
|
}
|
|
|
|
JavaClass getJavaLangClassLoader() {
|
|
return javaLangClassLoader;
|
|
}
|
|
|
|
JavaClass getOtherArrayType() {
|
|
if (otherArrayType == null) {
|
|
synchronized(this) {
|
|
if (otherArrayType == null) {
|
|
addFakeClass(new JavaClass("[<other>", 0, 0, 0, 0,
|
|
EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY,
|
|
0));
|
|
otherArrayType = findClass("[<other>");
|
|
}
|
|
}
|
|
}
|
|
return otherArrayType;
|
|
}
|
|
|
|
JavaClass getArrayClass(String elementSignature) {
|
|
JavaClass clazz;
|
|
synchronized(classes) {
|
|
clazz = findClass("[" + elementSignature);
|
|
if (clazz == null) {
|
|
clazz = new JavaClass("[" + elementSignature, 0, 0, 0, 0,
|
|
EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0);
|
|
addFakeClass(clazz);
|
|
// This is needed because the JDK only creates Class structures
|
|
// for array element types, not the arrays themselves. For
|
|
// analysis, though, we need to pretend that there's a
|
|
// JavaClass for the array type, too.
|
|
}
|
|
}
|
|
return clazz;
|
|
}
|
|
|
|
ReadBuffer getReadBuffer() {
|
|
return readBuf;
|
|
}
|
|
|
|
void setNew(JavaHeapObject obj, boolean isNew) {
|
|
initNewObjects();
|
|
if (isNew) {
|
|
newObjects.put(obj, Boolean.TRUE);
|
|
}
|
|
}
|
|
|
|
boolean isNew(JavaHeapObject obj) {
|
|
if (newObjects != null) {
|
|
return newObjects.get(obj) != null;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Internals only below this point
|
|
private Number makeId(long id) {
|
|
if (identifierSize == 4) {
|
|
return new Integer((int)id);
|
|
} else {
|
|
return new Long(id);
|
|
}
|
|
}
|
|
|
|
private void putInClassesMap(JavaClass c) {
|
|
String name = c.getName();
|
|
if (classes.containsKey(name)) {
|
|
// more than one class can have the same name
|
|
// if so, create a unique name by appending
|
|
// - and id string to it.
|
|
name += "-" + c.getIdString();
|
|
}
|
|
classes.put(c.getName(), c);
|
|
}
|
|
|
|
private void addFakeClass(JavaClass c) {
|
|
putInClassesMap(c);
|
|
c.resolve(this);
|
|
}
|
|
|
|
private void addFakeClass(Number id, JavaClass c) {
|
|
fakeClasses.put(id, c);
|
|
addFakeClass(c);
|
|
}
|
|
|
|
private synchronized void initNewObjects() {
|
|
if (newObjects == null) {
|
|
synchronized (this) {
|
|
if (newObjects == null) {
|
|
newObjects = new HashMap<JavaHeapObject, Boolean>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private synchronized void initSiteTraces() {
|
|
if (siteTraces == null) {
|
|
synchronized (this) {
|
|
if (siteTraces == null) {
|
|
siteTraces = new HashMap<JavaHeapObject, StackTrace>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|