diff options
author | 2017-08-25 16:49:41 +0100 | |
---|---|---|
committer | 2017-09-13 13:05:35 +0100 | |
commit | a490555f81e0e945cdfe9cf3bbfa2a8134d9a470 (patch) | |
tree | 019ae2f8311d9f8bae9ba72e7fa5ac7975d848d6 | |
parent | 8d02c895a872926fba8618ae30d5c855ef337fe7 (diff) |
Store instance fields and types with class objects.
Rather than duplicating them for every class instance.
Test: m ahat-test
Change-Id: Ifddf49918ca8532332928ab09ed9983d6ad8858f
-rw-r--r-- | tools/ahat/src/ObjectHandler.java | 2 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/AhatArrayInstance.java | 4 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/AhatClassInstance.java | 182 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/AhatClassObj.java | 24 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/AhatInstance.java | 2 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java | 8 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/AhatPlaceHolderInstance.java | 4 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/DiffFields.java | 16 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/DominatorReferenceIterator.java | 6 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/Field.java | 27 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/SkipNullsIterator.java (renamed from tools/ahat/src/heapdump/ReferenceIterator.java) | 35 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/SuperRoot.java | 5 | ||||
-rw-r--r-- | tools/ahat/src/heapdump/Value.java | 3 |
13 files changed, 233 insertions, 85 deletions
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java index 8262910bf0..f4926aa83b 100644 --- a/tools/ahat/src/ObjectHandler.java +++ b/tools/ahat/src/ObjectHandler.java @@ -152,7 +152,7 @@ class ObjectHandler implements AhatHandler { * ignored otherwise. */ private static void printFields(Doc doc, Query query, String id, boolean diff, - List<FieldValue> current, List<FieldValue> baseline) { + Iterable<FieldValue> current, Iterable<FieldValue> baseline) { if (!diff) { // To avoid having to special case when diff is disabled, always diff diff --git a/tools/ahat/src/heapdump/AhatArrayInstance.java b/tools/ahat/src/heapdump/AhatArrayInstance.java index 4b18bf749d..8d23276fde 100644 --- a/tools/ahat/src/heapdump/AhatArrayInstance.java +++ b/tools/ahat/src/heapdump/AhatArrayInstance.java @@ -128,7 +128,7 @@ public class AhatArrayInstance extends AhatInstance { } @Override - ReferenceIterator getReferences() { + Iterable<Reference> getReferences() { // The list of references will be empty if this is a primitive array. List<Reference> refs = Collections.emptyList(); if (!mValues.isEmpty()) { @@ -153,7 +153,7 @@ public class AhatArrayInstance extends AhatInstance { }; } } - return new ReferenceIterator(refs); + return new SkipNullsIterator(refs); } @Override public boolean isArrayInstance() { diff --git a/tools/ahat/src/heapdump/AhatClassInstance.java b/tools/ahat/src/heapdump/AhatClassInstance.java index 211592388c..f7d8431a7b 100644 --- a/tools/ahat/src/heapdump/AhatClassInstance.java +++ b/tools/ahat/src/heapdump/AhatClassInstance.java @@ -19,12 +19,16 @@ package com.android.ahat.heapdump; import com.android.tools.perflib.heap.ClassInstance; import com.android.tools.perflib.heap.Instance; import java.awt.image.BufferedImage; -import java.util.AbstractList; -import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; public class AhatClassInstance extends AhatInstance { - private FieldValue[] mFieldValues; + // Instance fields of the object. These are stored in order of the instance + // field descriptors from the class object, starting with this class first, + // followed by the super class, and so on. We store the values separate from + // the field types and names to save memory. + private Value[] mFields; public AhatClassInstance(long id) { super(id); @@ -35,18 +39,14 @@ public class AhatClassInstance extends AhatInstance { ClassInstance classInst = (ClassInstance)inst; List<ClassInstance.FieldValue> fieldValues = classInst.getValues(); - mFieldValues = new FieldValue[fieldValues.size()]; - for (int i = 0; i < mFieldValues.length; i++) { - ClassInstance.FieldValue field = fieldValues.get(i); - String name = field.getField().getName(); - String type = field.getField().getType().toString(); - Value value = snapshot.getValue(field.getValue()); - mFieldValues[i] = new FieldValue(name, type, value); + mFields = new Value[fieldValues.size()]; + for (int i = 0; i < mFields.length; i++) { + mFields[i] = snapshot.getValue(fieldValues.get(i).getValue()); } } @Override public Value getField(String fieldName) { - for (FieldValue field : mFieldValues) { + for (FieldValue field : getInstanceFields()) { if (fieldName.equals(field.name)) { return field.value; } @@ -90,36 +90,21 @@ public class AhatClassInstance extends AhatInstance { /** * Returns the list of class instance fields for this instance. */ - public List<FieldValue> getInstanceFields() { - return Arrays.asList(mFieldValues); + public Iterable<FieldValue> getInstanceFields() { + return new InstanceFieldIterator(mFields, getClassObj()); } @Override - ReferenceIterator getReferences() { - List<Reference> refs = new AbstractList<Reference>() { - @Override - public int size() { - return mFieldValues.length; - } - - @Override - public Reference get(int index) { - FieldValue field = mFieldValues[index]; - Value value = field.value; - if (value != null && value.isAhatInstance()) { - boolean strong = !field.name.equals("referent") - || !isInstanceOfClass("java.lang.ref.Reference"); - AhatInstance ref = value.asAhatInstance(); - return new Reference(AhatClassInstance.this, "." + field.name, ref, strong); - } - return null; - } - }; - return new ReferenceIterator(refs); + Iterable<Reference> getReferences() { + if (isInstanceOfClass("java.lang.ref.Reference")) { + return new WeakReferentReferenceIterator(); + } + return new StrongReferenceIterator(); } /** - * Returns true if this is an instance of a class with the given name. + * Returns true if this is an instance of a (subclass of a) class with the + * given name. */ private boolean isInstanceOfClass(String className) { AhatClassObj cls = getClassObj(); @@ -262,4 +247,131 @@ public class AhatClassInstance extends AhatInstance { bitmap.setRGB(0, 0, info.width, info.height, abgr, 0, info.width); return bitmap; } + + private static class InstanceFieldIterator implements Iterable<FieldValue>, + Iterator<FieldValue> { + // The complete list of instance field values to iterate over, including + // superclass field values. + private Value[] mValues; + private int mValueIndex; + + // The list of field descriptors specific to the current class in the + // class hierarchy, not including superclass field descriptors. + // mFields and mFieldIndex are reset each time we walk up to the next + // superclass in the call hierarchy. + private Field[] mFields; + private int mFieldIndex; + private AhatClassObj mNextClassObj; + + public InstanceFieldIterator(Value[] values, AhatClassObj classObj) { + mValues = values; + mFields = classObj.getInstanceFields(); + mValueIndex = 0; + mFieldIndex = 0; + mNextClassObj = classObj.getSuperClassObj(); + } + + @Override + public boolean hasNext() { + // If we have reached the end of the fields in the current class, + // continue walking up the class hierarchy to get superclass fields as + // well. + while (mFieldIndex == mFields.length && mNextClassObj != null) { + mFields = mNextClassObj.getInstanceFields(); + mFieldIndex = 0; + mNextClassObj = mNextClassObj.getSuperClassObj(); + } + return mFieldIndex < mFields.length; + } + + @Override + public FieldValue next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Field field = mFields[mFieldIndex++]; + Value value = mValues[mValueIndex++]; + return new FieldValue(field.name, field.type, value); + } + + @Override + public Iterator<FieldValue> iterator() { + return this; + } + } + + /** + * A Reference iterator that iterates over the fields of this instance + * assuming all field references are strong references. + */ + private class StrongReferenceIterator implements Iterable<Reference>, + Iterator<Reference> { + private Iterator<FieldValue> mIter = getInstanceFields().iterator(); + private Reference mNext = null; + + @Override + public boolean hasNext() { + while (mNext == null && mIter.hasNext()) { + FieldValue field = mIter.next(); + if (field.value != null && field.value.isAhatInstance()) { + AhatInstance ref = field.value.asAhatInstance(); + mNext = new Reference(AhatClassInstance.this, "." + field.name, ref, true); + } + } + return mNext != null; + } + + @Override + public Reference next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Reference next = mNext; + mNext = null; + return next; + } + + @Override + public Iterator<Reference> iterator() { + return this; + } + } + + /** + * A Reference iterator that iterates over the fields of a subclass of + * java.lang.ref.Reference, where the 'referent' field is considered weak. + */ + private class WeakReferentReferenceIterator implements Iterable<Reference>, + Iterator<Reference> { + private Iterator<FieldValue> mIter = getInstanceFields().iterator(); + private Reference mNext = null; + + @Override + public boolean hasNext() { + while (mNext == null && mIter.hasNext()) { + FieldValue field = mIter.next(); + if (field.value != null && field.value.isAhatInstance()) { + boolean strong = !field.name.equals("referent"); + AhatInstance ref = field.value.asAhatInstance(); + mNext = new Reference(AhatClassInstance.this, "." + field.name, ref, strong); + } + } + return mNext != null; + } + + @Override + public Reference next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Reference next = mNext; + mNext = null; + return next; + } + + @Override + public Iterator<Reference> iterator() { + return this; + } + } } diff --git a/tools/ahat/src/heapdump/AhatClassObj.java b/tools/ahat/src/heapdump/AhatClassObj.java index 052d7a8e88..08c70974c6 100644 --- a/tools/ahat/src/heapdump/AhatClassObj.java +++ b/tools/ahat/src/heapdump/AhatClassObj.java @@ -17,7 +17,6 @@ package com.android.ahat.heapdump; import com.android.tools.perflib.heap.ClassObj; -import com.android.tools.perflib.heap.Field; import com.android.tools.perflib.heap.Instance; import java.util.AbstractList; import java.util.Arrays; @@ -30,6 +29,7 @@ public class AhatClassObj extends AhatInstance { private AhatClassObj mSuperClassObj; private AhatInstance mClassLoader; private FieldValue[] mStaticFieldValues; + private Field[] mInstanceFields; public AhatClassObj(long id) { super(id); @@ -51,15 +51,22 @@ public class AhatClassObj extends AhatInstance { mClassLoader = snapshot.findInstance(loader.getId()); } - Collection<Map.Entry<Field, Object>> fieldValues = classObj.getStaticFieldValues().entrySet(); + Collection<Map.Entry<com.android.tools.perflib.heap.Field, Object>> fieldValues + = classObj.getStaticFieldValues().entrySet(); mStaticFieldValues = new FieldValue[fieldValues.size()]; int index = 0; - for (Map.Entry<Field, Object> field : fieldValues) { + for (Map.Entry<com.android.tools.perflib.heap.Field, Object> field : fieldValues) { String name = field.getKey().getName(); String type = field.getKey().getType().toString(); Value value = snapshot.getValue(field.getValue()); mStaticFieldValues[index++] = new FieldValue(name, type, value); } + + com.android.tools.perflib.heap.Field[] fields = classObj.getFields(); + mInstanceFields = new Field[fields.length]; + for (int i = 0; i < fields.length; i++) { + mInstanceFields[i] = new Field(fields[i].getName(), fields[i].getType().toString()); + } } /** @@ -90,8 +97,15 @@ public class AhatClassObj extends AhatInstance { return Arrays.asList(mStaticFieldValues); } + /** + * Returns the fields of instances of this class. + */ + public Field[] getInstanceFields() { + return mInstanceFields; + } + @Override - ReferenceIterator getReferences() { + Iterable<Reference> getReferences() { List<Reference> refs = new AbstractList<Reference>() { @Override public int size() { @@ -108,7 +122,7 @@ public class AhatClassObj extends AhatInstance { return null; } }; - return new ReferenceIterator(refs); + return new SkipNullsIterator(refs); } @Override public boolean isClassObj() { diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java index e1b83b899d..0e7855801d 100644 --- a/tools/ahat/src/heapdump/AhatInstance.java +++ b/tools/ahat/src/heapdump/AhatInstance.java @@ -148,7 +148,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, * Returns an iterator over the references this AhatInstance has to other * AhatInstances. */ - abstract ReferenceIterator getReferences(); + abstract Iterable<Reference> getReferences(); /** * Returns true if this instance is marked as a root instance. diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java b/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java index 2b3e056a1e..8b4c6796aa 100644 --- a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java +++ b/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java @@ -29,10 +29,6 @@ public class AhatPlaceHolderClassObj extends AhatClassObj { baseline.setBaseline(this); } - @Override public Size getSize() { - return Size.ZERO; - } - @Override public Size getRetainedSize(AhatHeap heap) { return Size.ZERO; } @@ -68,4 +64,8 @@ public class AhatPlaceHolderClassObj extends AhatClassObj { @Override public AhatInstance getClassLoader() { return getBaseline().asClassObj().getClassLoader().getBaseline(); } + + @Override public Field[] getInstanceFields() { + return getBaseline().asClassObj().getInstanceFields(); + } } diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java index d797b11030..9abc952072 100644 --- a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java +++ b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java @@ -65,8 +65,8 @@ public class AhatPlaceHolderInstance extends AhatInstance { } @Override - ReferenceIterator getReferences() { + Iterable<Reference> getReferences() { List<Reference> refs = Collections.emptyList(); - return new ReferenceIterator(refs); + return refs; } } diff --git a/tools/ahat/src/heapdump/DiffFields.java b/tools/ahat/src/heapdump/DiffFields.java index dd73456c0f..e3c671fe21 100644 --- a/tools/ahat/src/heapdump/DiffFields.java +++ b/tools/ahat/src/heapdump/DiffFields.java @@ -17,7 +17,6 @@ package com.android.ahat.heapdump; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -29,14 +28,19 @@ import java.util.List; public class DiffFields { /** * Return the result of diffing two collections of field values. - * The input collections 'current' and 'baseline' are not modified by this function. */ - public static List<DiffedFieldValue> diff(Collection<FieldValue> current, - Collection<FieldValue> baseline) { - List<FieldValue> currentSorted = new ArrayList<FieldValue>(current); + public static List<DiffedFieldValue> diff(Iterable<FieldValue> current, + Iterable<FieldValue> baseline) { + List<FieldValue> currentSorted = new ArrayList<FieldValue>(); + for (FieldValue field : current) { + currentSorted.add(field); + } Collections.sort(currentSorted, FOR_DIFF); - List<FieldValue> baselineSorted = new ArrayList<FieldValue>(baseline); + List<FieldValue> baselineSorted = new ArrayList<FieldValue>(); + for (FieldValue field : baseline) { + baselineSorted.add(field); + } Collections.sort(baselineSorted, FOR_DIFF); // Merge the two lists to form the diffed list of fields. diff --git a/tools/ahat/src/heapdump/DominatorReferenceIterator.java b/tools/ahat/src/heapdump/DominatorReferenceIterator.java index ce2e6efa6e..0b99e496cc 100644 --- a/tools/ahat/src/heapdump/DominatorReferenceIterator.java +++ b/tools/ahat/src/heapdump/DominatorReferenceIterator.java @@ -25,11 +25,11 @@ import java.util.NoSuchElementException; */ class DominatorReferenceIterator implements Iterator<AhatInstance>, Iterable<AhatInstance> { - private ReferenceIterator mIter; + private Iterator<Reference> mIter; private AhatInstance mNext; - public DominatorReferenceIterator(ReferenceIterator iter) { - mIter = iter; + public DominatorReferenceIterator(Iterable<Reference> iter) { + mIter = iter.iterator(); mNext = null; } diff --git a/tools/ahat/src/heapdump/Field.java b/tools/ahat/src/heapdump/Field.java new file mode 100644 index 0000000000..01f87c726e --- /dev/null +++ b/tools/ahat/src/heapdump/Field.java @@ -0,0 +1,27 @@ +/* + * 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; + +public class Field { + public final String name; + public final String type; + + public Field(String name, String type) { + this.name = name; + this.type = type; + } +} diff --git a/tools/ahat/src/heapdump/ReferenceIterator.java b/tools/ahat/src/heapdump/SkipNullsIterator.java index a707fb24ef..e99fe5e8ea 100644 --- a/tools/ahat/src/heapdump/ReferenceIterator.java +++ b/tools/ahat/src/heapdump/SkipNullsIterator.java @@ -17,49 +17,40 @@ package com.android.ahat.heapdump; import java.util.Iterator; -import java.util.List; import java.util.NoSuchElementException; -class ReferenceIterator implements Iterator<Reference>, - Iterable<Reference> { - private List<Reference> mRefs; - private int mLength; - private int mNextIndex; - private Reference mNext; +/** + * An iterator that skips over nulls. + */ +class SkipNullsIterator<T> implements Iterator<T>, Iterable<T> { + Iterator<T> mIter; + private T mNext; - /** - * Construct a ReferenceIterator that iterators over the given list of - * references. Elements of the given list of references may be null, in - * which case the ReferenceIterator will skip over them. - */ - public ReferenceIterator(List<Reference> refs) { - mRefs = refs; - mLength = refs.size(); - mNextIndex = 0; + public SkipNullsIterator(Iterable<T> iterable) { + mIter = iterable.iterator(); mNext = null; } @Override public boolean hasNext() { - while (mNext == null && mNextIndex < mLength) { - mNext = mRefs.get(mNextIndex); - mNextIndex++; + while (mNext == null && mIter.hasNext()) { + mNext = mIter.next(); } return mNext != null; } @Override - public Reference next() { + public T next() { if (!hasNext()) { throw new NoSuchElementException(); } - Reference next = mNext; + T next = mNext; mNext = null; return next; } @Override - public Iterator<Reference> iterator() { + public Iterator<T> iterator() { return this; } } diff --git a/tools/ahat/src/heapdump/SuperRoot.java b/tools/ahat/src/heapdump/SuperRoot.java index 54410cf1a6..d377113862 100644 --- a/tools/ahat/src/heapdump/SuperRoot.java +++ b/tools/ahat/src/heapdump/SuperRoot.java @@ -39,8 +39,8 @@ public class SuperRoot extends AhatInstance implements DominatorsComputation.Nod } @Override - ReferenceIterator getReferences() { - List<Reference> refs = new AbstractList<Reference>() { + Iterable<Reference> getReferences() { + return new AbstractList<Reference>() { @Override public int size() { return mRoots.size(); @@ -52,6 +52,5 @@ public class SuperRoot extends AhatInstance implements DominatorsComputation.Nod return new Reference(null, field, mRoots.get(index), true); } }; - return new ReferenceIterator(refs); } } diff --git a/tools/ahat/src/heapdump/Value.java b/tools/ahat/src/heapdump/Value.java index 38a6815007..7f86c01efb 100644 --- a/tools/ahat/src/heapdump/Value.java +++ b/tools/ahat/src/heapdump/Value.java @@ -52,7 +52,8 @@ public abstract class Value { } else if (object instanceof Long) { return Value.pack(((Long)object).longValue()); } - throw new IllegalArgumentException("AhatInstance or primitive type required"); + throw new IllegalArgumentException( + "AhatInstance or primitive type required, but got: " + object.toString()); } public static Value pack(boolean value) { |