/* * Copyright (C) 2008 Google Inc. * * 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.tools.perflib.heap; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.tools.perflib.heap.io.HprofBuffer; import com.google.common.collect.Sets; import com.google.common.primitives.UnsignedBytes; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; public abstract class Instance { protected final long mId; // The stack in which this object was allocated @NonNull protected final StackTrace mStack; // Id of the ClassObj of which this object is an instance long mClassId; // The heap in which this object was allocated (app, zygote, etc) Heap mHeap; // The size of this object int mSize; // Another identifier for this Instance, that we computed during the analysis phase. int mTopologicalOrder; // The immediate dominator of this instance, or null if not reachable from any GC roots. @Nullable private Instance mImmediateDominator; // The retained size of this object, indexed by heap (default, image, app, zygote). // Intuitively, this represents the amount of memory that could be reclaimed in each heap if // the instance were removed. // To save space, we only keep a primitive array here following the order in mSnapshot.mHeaps. private long[] mRetainedSizes; // List of all objects that hold a live reference to this object private final ArrayList<Instance> mReferences = new ArrayList<Instance>(); Instance(long id, @NonNull StackTrace stackTrace) { mId = id; mStack = stackTrace; } public long getId() { return mId; } public abstract void accept(Visitor visitor); public void setClassId(long classId) { mClassId = classId; } public ClassObj getClassObj() { return mHeap.mSnapshot.findClass(mClassId); } public final int getCompositeSize() { CollectingVisitor visitor = new CollectingVisitor(); this.accept(visitor); int size = 0; for (Instance instance : visitor.getVisited()) { size += instance.getSize(); } return size; } // Returns the instrinsic size of a given object public int getSize() { return mSize; } public void setSize(int size) { mSize = size; } public void setHeap(Heap heap) { mHeap = heap; } public Heap getHeap() { return mHeap; } public int getTopologicalOrder() { return mTopologicalOrder; } public void setTopologicalOrder(int topologicalOrder) { mTopologicalOrder = topologicalOrder; } @Nullable public Instance getImmediateDominator() { return mImmediateDominator; } public void setImmediateDominator(@NonNull Instance dominator) { mImmediateDominator = dominator; } public void resetRetainedSize() { List<Heap> allHeaps = mHeap.mSnapshot.mHeaps; if (mRetainedSizes == null) { mRetainedSizes = new long[allHeaps.size()]; } else { Arrays.fill(mRetainedSizes, 0); } mRetainedSizes[allHeaps.indexOf(mHeap)] = getSize(); } public void addRetainedSize(int heapIndex, long size) { mRetainedSizes[heapIndex] += size; } public long getRetainedSize(int heapIndex) { return mRetainedSizes[heapIndex]; } // Add to the list of objects that have a hard reference to this Instance public void addReference(Instance reference) { mReferences.add(reference); } @NonNull public ArrayList<Instance> getReferences() { return mReferences; } @Nullable protected Object readValue(@NonNull Type type) { switch (type) { case OBJECT: long id = readId(); Instance result = mHeap.mSnapshot.findReference(id); if (result != null) { result.addReference(this); } return result; case BOOLEAN: return getBuffer().readByte() != 0; case CHAR: return getBuffer().readChar(); case FLOAT: return getBuffer().readFloat(); case DOUBLE: return getBuffer().readDouble(); case BYTE: return getBuffer().readByte(); case SHORT: return getBuffer().readShort(); case INT: return getBuffer().readInt(); case LONG: return getBuffer().readLong(); } return null; } protected long readId() { // As long as we don't interpret IDs, reading signed values here is fine. switch (Type.OBJECT.getSize()) { case 1: return getBuffer().readByte(); case 2: return getBuffer().readShort(); case 4: return getBuffer().readInt(); case 8: return getBuffer().readLong(); } return 0; } protected int readUnsignedByte(){ return UnsignedBytes.toInt(getBuffer().readByte()); } protected int readUnsignedShort() { return getBuffer().readShort() & 0xffff; } protected HprofBuffer getBuffer() { return mHeap.mSnapshot.mBuffer; } public static class CollectingVisitor implements Visitor { private final Set<Instance> mVisited = Sets.newHashSet(); @Override public boolean visitEnter(Instance instance) { // If we're in the set then we and our children have been visited return mVisited.add(instance); } @Override public void visitLeave(Instance instance) { } public Set<Instance> getVisited() { return mVisited; } } }