/*
* 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.ImmutableList;
import com.google.common.primitives.UnsignedBytes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
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;
int mDistanceToGcRoot = Integer.MAX_VALUE;
boolean mReferencesAdded = false;
Instance mNextInstanceToGcRoot = null;
// 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> mHardReferences = new ArrayList<Instance>();
// List of all objects that hold a soft/weak/phantom reference to this object.
// Don't create an actual list until we need to.
private ArrayList<Instance> mSoftReferences = null;
Instance(long id, @NonNull StackTrace stackTrace) {
mId = id;
mStack = stackTrace;
}
public long getId() {
return mId;
}
public long getUniqueId() {
return getId() & mHeap.mSnapshot.getIdSizeMask();
}
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() {
CompositeSizeVisitor visitor = new CompositeSizeVisitor();
visitor.doVisit(ImmutableList.of(this));
return visitor.getCompositeSize();
}
// 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 int getDistanceToGcRoot() {
return mDistanceToGcRoot;
}
public Instance getNextInstanceToGcRoot() {
return mNextInstanceToGcRoot;
}
public void setDistanceToGcRoot(int newDistance) {
assert(newDistance < mDistanceToGcRoot);
mDistanceToGcRoot = newDistance;
}
public void setNextInstanceToGcRoot(Instance instance) {
mNextInstanceToGcRoot = instance;
}
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];
}
public long getTotalRetainedSize() {
if (mRetainedSizes == null) {
return 0;
}
long totalSize = 0;
for (long mRetainedSize : mRetainedSizes) {
totalSize += mRetainedSize;
}
return totalSize;
}
/**
* Add to the list of objects that references this Instance.
*
* @param field the named variable in #reference pointing to this instance. If the name of the field is "referent", and #reference is a
* soft reference type, then reference is counted as a soft reference instead of the usual hard reference.
* @param reference another instance that references this instance
*/
public void addReference(@Nullable Field field, @NonNull Instance reference) {
if (reference.getIsSoftReference() && field != null && field.getName().equals("referent")) {
if (mSoftReferences == null) {
mSoftReferences = new ArrayList<Instance>();
}
mSoftReferences.add(reference);
}
else {
mHardReferences.add(reference);
}
}
@NonNull
public ArrayList<Instance> getHardReferences() {
return mHardReferences;
}
@Nullable
public ArrayList<Instance> getSoftReferences() {
return mSoftReferences;
}
/**
* There is an underlying assumption that a class that is a soft reference will only have one referent.
*
* @return true if the instance is a soft reference type, or false otherwise
*/
public boolean getIsSoftReference() {
return false;
}
@Nullable
protected Object readValue(@NonNull Type type) {
switch (type) {
case OBJECT:
long id = readId();
return mHeap.mSnapshot.findInstance(id);
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 (mHeap.mSnapshot.getTypeSize(Type.OBJECT)) {
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 CompositeSizeVisitor extends NonRecursiveVisitor {
int mSize = 0;
@Override
protected void defaultAction(Instance node) {
mSize += node.getSize();
}
public int getCompositeSize() {
return mSize;
}
}
}