diff options
Diffstat (limited to 'tools/ahat/src/heapdump/AhatInstance.java')
-rw-r--r-- | tools/ahat/src/heapdump/AhatInstance.java | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java new file mode 100644 index 0000000000..e6b9c00384 --- /dev/null +++ b/tools/ahat/src/heapdump/AhatInstance.java @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat.heapdump; + +import com.android.tools.perflib.heap.ClassObj; +import com.android.tools.perflib.heap.Instance; +import com.android.tools.perflib.heap.RootObj; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public abstract class AhatInstance implements Diffable<AhatInstance> { + private long mId; + private long mSize; + private long mTotalRetainedSize; + private long mRetainedSizes[]; // Retained size indexed by heap index + private boolean mIsReachable; + private AhatHeap mHeap; + private AhatInstance mImmediateDominator; + private AhatInstance mNextInstanceToGcRoot; + private String mNextInstanceToGcRootField = "???"; + private AhatClassObj mClassObj; + private AhatInstance[] mHardReverseReferences; + private AhatInstance[] mSoftReverseReferences; + private Site mSite; + + // If this instance is a root, mRootTypes contains a set of the root types. + // If this instance is not a root, mRootTypes is null. + private List<String> mRootTypes; + + // List of instances this instance immediately dominates. + private List<AhatInstance> mDominated = new ArrayList<AhatInstance>(); + + private AhatInstance mBaseline; + + public AhatInstance(long id) { + mId = id; + mBaseline = this; + } + + /** + * Initializes this AhatInstance based on the given perflib instance. + * The AhatSnapshot should be used to look up AhatInstances and AhatHeaps. + * There is no guarantee that the AhatInstances returned by + * snapshot.findInstance have been initialized yet. + */ + void initialize(AhatSnapshot snapshot, Instance inst) { + mId = inst.getId(); + mSize = inst.getSize(); + mTotalRetainedSize = inst.getTotalRetainedSize(); + mIsReachable = inst.isReachable(); + + List<AhatHeap> heaps = snapshot.getHeaps(); + mRetainedSizes = new long[heaps.size()]; + for (AhatHeap heap : heaps) { + mRetainedSizes[heap.getIndex()] = inst.getRetainedSize(heap.getIndex()); + } + + mHeap = snapshot.getHeap(inst.getHeap().getName()); + + Instance dom = inst.getImmediateDominator(); + if (dom == null || dom instanceof RootObj) { + mImmediateDominator = null; + } else { + mImmediateDominator = snapshot.findInstance(dom.getId()); + mImmediateDominator.mDominated.add(this); + } + + ClassObj clsObj = inst.getClassObj(); + if (clsObj != null) { + mClassObj = snapshot.findClassObj(clsObj.getId()); + } + + // A couple notes about reverse references: + // * perflib sometimes returns unreachable reverse references. If + // snapshot.findInstance returns null, it means the reverse reference is + // not reachable, so we filter it out. + // * We store the references as AhatInstance[] instead of + // ArrayList<AhatInstance> because it saves a lot of space and helps + // with performance when there are a lot of AhatInstances. + ArrayList<AhatInstance> ahatRefs = new ArrayList<AhatInstance>(); + ahatRefs = new ArrayList<AhatInstance>(); + for (Instance ref : inst.getHardReverseReferences()) { + AhatInstance ahat = snapshot.findInstance(ref.getId()); + if (ahat != null) { + ahatRefs.add(ahat); + } + } + mHardReverseReferences = new AhatInstance[ahatRefs.size()]; + ahatRefs.toArray(mHardReverseReferences); + + List<Instance> refs = inst.getSoftReverseReferences(); + ahatRefs.clear(); + if (refs != null) { + for (Instance ref : refs) { + AhatInstance ahat = snapshot.findInstance(ref.getId()); + if (ahat != null) { + ahatRefs.add(ahat); + } + } + } + mSoftReverseReferences = new AhatInstance[ahatRefs.size()]; + ahatRefs.toArray(mSoftReverseReferences); + } + + /** + * Returns a unique identifier for the instance. + */ + public long getId() { + return mId; + } + + /** + * Returns the shallow number of bytes this object takes up. + */ + public long getSize() { + return mSize; + } + + /** + * Returns the number of bytes belonging to the given heap that this instance + * retains. + */ + public long getRetainedSize(AhatHeap heap) { + int index = heap.getIndex(); + return 0 <= index && index < mRetainedSizes.length ? mRetainedSizes[heap.getIndex()] : 0; + } + + /** + * Returns the total number of bytes this instance retains. + */ + public long getTotalRetainedSize() { + return mTotalRetainedSize; + } + + /** + * Returns whether this object is strongly-reachable. + */ + public boolean isReachable() { + return mIsReachable; + } + + /** + * Returns the heap that this instance is allocated on. + */ + public AhatHeap getHeap() { + return mHeap; + } + + /** + * Returns true if this instance is marked as a root instance. + */ + public boolean isRoot() { + return mRootTypes != null; + } + + /** + * Marks this instance as being a root of the given type. + */ + void addRootType(String type) { + if (mRootTypes == null) { + mRootTypes = new ArrayList<String>(); + mRootTypes.add(type); + } else if (!mRootTypes.contains(type)) { + mRootTypes.add(type); + } + } + + /** + * Returns a list of string descriptions of the root types of this object. + * Returns null if this object is not a root. + */ + public Collection<String> getRootTypes() { + return mRootTypes; + } + + /** + * Returns the immediate dominator of this instance. + * Returns null if this is a root instance. + */ + public AhatInstance getImmediateDominator() { + return mImmediateDominator; + } + + /** + * Returns a list of those objects immediately dominated by the given + * instance. + */ + public List<AhatInstance> getDominated() { + return mDominated; + } + + /** + * Returns the site where this instance was allocated. + */ + public Site getSite() { + return mSite; + } + + /** + * Sets the allocation site of this instance. + */ + void setSite(Site site) { + mSite = site; + } + + /** + * Returns true if the given instance is a class object + */ + public boolean isClassObj() { + // Overridden by AhatClassObj. + return false; + } + + /** + * Returns this as an AhatClassObj if this is an AhatClassObj. + * Returns null if this is not an AhatClassObj. + */ + public AhatClassObj asClassObj() { + // Overridden by AhatClassObj. + return null; + } + + /** + * Returns the class object instance for the class of this object. + */ + public AhatClassObj getClassObj() { + return mClassObj; + } + + /** + * Returns the name of the class this object belongs to. + */ + public String getClassName() { + AhatClassObj classObj = getClassObj(); + return classObj == null ? "???" : classObj.getName(); + } + + /** + * Returns true if the given instance is an array instance + */ + public boolean isArrayInstance() { + // Overridden by AhatArrayInstance. + return false; + } + + /** + * Returns this as an AhatArrayInstance if this is an AhatArrayInstance. + * Returns null if this is not an AhatArrayInstance. + */ + public AhatArrayInstance asArrayInstance() { + // Overridden by AhatArrayInstance. + return null; + } + + /** + * Returns true if the given instance is a class instance + */ + public boolean isClassInstance() { + return false; + } + + /** + * Returns this as an AhatClassInstance if this is an AhatClassInstance. + * Returns null if this is not an AhatClassInstance. + */ + public AhatClassInstance asClassInstance() { + return null; + } + + /** + * Return the referent associated with this instance. + * This is relevent for instances of java.lang.ref.Reference. + * Returns null if the instance has no referent associated with it. + */ + public AhatInstance getReferent() { + // Overridden by AhatClassInstance. + return null; + } + + /** + * Returns a list of objects with hard references to this object. + */ + public List<AhatInstance> getHardReverseReferences() { + return Arrays.asList(mHardReverseReferences); + } + + /** + * Returns a list of objects with soft references to this object. + */ + public List<AhatInstance> getSoftReverseReferences() { + return Arrays.asList(mSoftReverseReferences); + } + + /** + * Returns the value of a field of an instance. + * Returns null if the field value is null, the field couldn't be read, or + * there are multiple fields with the same name. + */ + public Value getField(String fieldName) { + // Overridden by AhatClassInstance. + return null; + } + + /** + * Reads a reference field of this instance. + * Returns null if the field value is null, or if the field couldn't be read. + */ + public AhatInstance getRefField(String fieldName) { + // Overridden by AhatClassInstance. + return null; + } + + /** + * Assuming inst represents a DexCache object, return the dex location for + * that dex cache. Returns null if the given instance doesn't represent a + * DexCache object or the location could not be found. + * If maxChars is non-negative, the returned location is truncated to + * maxChars in length. + */ + public String getDexCacheLocation(int maxChars) { + return null; + } + + /** + * Return the bitmap instance associated with this object, or null if there + * is none. This works for android.graphics.Bitmap instances and their + * underlying Byte[] instances. + */ + public AhatInstance getAssociatedBitmapInstance() { + return null; + } + + /** + * Read the string value from this instance. + * Returns null if this object can't be interpreted as a string. + * The returned string is truncated to maxChars characters. + * If maxChars is negative, the returned string is not truncated. + */ + public String asString(int maxChars) { + // By default instances can't be interpreted as a string. This method is + // overridden by AhatClassInstance and AhatArrayInstance for those cases + // when an instance can be interpreted as a string. + return null; + } + + /** + * Reads the string value from an hprof Instance. + * Returns null if the object can't be interpreted as a string. + */ + public String asString() { + return asString(-1); + } + + /** + * Return the bitmap associated with the given instance, if any. + * This is relevant for instances of android.graphics.Bitmap and byte[]. + * Returns null if there is no bitmap associated with the given instance. + */ + public BufferedImage asBitmap() { + return null; + } + + /** + * Returns a sample path from a GC root to this instance. + * This instance is included as the last element of the path with an empty + * field description. + */ + public List<PathElement> getPathFromGcRoot() { + List<PathElement> path = new ArrayList<PathElement>(); + + AhatInstance dom = this; + for (PathElement elem = new PathElement(this, ""); elem != null; + elem = getNextPathElementToGcRoot(elem.instance)) { + if (elem.instance.equals(dom)) { + elem.isDominator = true; + dom = dom.getImmediateDominator(); + } + path.add(elem); + } + Collections.reverse(path); + return path; + } + + /** + * Returns the next instance to GC root from this object and a string + * description of which field of that object refers to the given instance. + * Returns null if the given instance has no next instance to the gc root. + */ + private static PathElement getNextPathElementToGcRoot(AhatInstance inst) { + AhatInstance parent = inst.mNextInstanceToGcRoot; + if (parent == null) { + return null; + } + return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField); + } + + void setNextInstanceToGcRoot(AhatInstance inst, String field) { + mNextInstanceToGcRoot = inst; + mNextInstanceToGcRootField = field; + } + + /** Returns a human-readable identifier for this object. + * For class objects, the string is the class name. + * For class instances, the string is the class name followed by '@' and the + * hex id of the instance. + * For array instances, the string is the array type followed by the size in + * square brackets, followed by '@' and the hex id of the instance. + */ + @Override public abstract String toString(); + + /** + * Read the byte[] value from an hprof Instance. + * Returns null if the instance is not a byte array. + */ + byte[] asByteArray() { + return null; + } + + public void setBaseline(AhatInstance baseline) { + mBaseline = baseline; + } + + @Override public AhatInstance getBaseline() { + return mBaseline; + } + + @Override public boolean isPlaceHolder() { + return false; + } + + /** + * Returns a new place holder instance corresponding to this instance. + */ + AhatInstance newPlaceHolderInstance() { + return new AhatPlaceHolderInstance(this); + } +} |