1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ahat.heapdump;
18 
19 import com.android.ahat.progress.Progress;
20 import java.awt.image.BufferedImage;
21 import java.util.ArrayDeque;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Deque;
26 import java.util.EnumMap;
27 import java.util.List;
28 import java.util.Queue;
29 
30 /**
31  * A Java instance from a parsed heap dump. It is the base class used for all
32  * kinds of Java instances, including normal Java objects, class objects, and
33  * arrays.
34  */
35 public abstract class AhatInstance implements Diffable<AhatInstance> {
36   // The id of this instance from the heap dump.
37   private final long mId;
38 
39   // Fields initialized in initialize().
40   private AhatHeap mHeap;
41   private AhatClassObj mClassObj;
42   private Site mSite;
43 
44   // Bit vector of the root types of this object.
45   private int mRootTypes;
46 
47   // Field initialized via addRegisterednativeSize.
48   private long mRegisteredNativeSize = 0;
49 
50   // Fields initialized in computeReachability().
51   private Reachability mReachability = Reachability.UNREACHABLE;
52   private AhatInstance mNextInstanceToGcRoot;
53   private String mNextInstanceToGcRootField;
54   private ArrayList<AhatInstance> mReverseReferences;
55 
56   // Fields initialized in DominatorsComputation.computeDominators().
57   // mDominated - the list of instances immediately dominated by this instance.
58   // mRetainedSizes - retained size indexed by heap index.
59   private AhatInstance mImmediateDominator;
60   private List<AhatInstance> mDominated = new ArrayList<AhatInstance>();
61   private Size[] mRetainedSizes;
62 
63   // The baseline instance for purposes of diff.
64   private AhatInstance mBaseline;
65 
66   // temporary user data associated with this instance. This is used for a
67   // couple different purposes:
68   // 1. During parsing of instances, to store temporary field data.
69   // 2. During dominators computation, to store the dominators computation state.
70   private Object mTemporaryUserData;
71 
AhatInstance(long id)72   AhatInstance(long id) {
73     mId = id;
74     mBaseline = this;
75   }
76 
77   /**
78    * Initialize this AhatInstance based on the the given info.
79    */
initialize(AhatHeap heap, Site site, AhatClassObj classObj)80   void initialize(AhatHeap heap, Site site, AhatClassObj classObj) {
81     mHeap = heap;
82     mSite = site;
83     mClassObj = classObj;
84   }
85 
86   /**
87    * Returns a unique identifier for this instance.
88    *
89    * @return id of the instance
90    */
getId()91   public long getId() {
92     return mId;
93   }
94 
95   /**
96    * Returns the number of bytes used for this object in the heap.
97    * The returned size is a shallow size for the object that does not include
98    * sizes of other objects dominated by this object.
99    *
100    * @return the shallow size of the object
101    */
getSize()102   public Size getSize() {
103     return new Size(mClassObj.getInstanceSize() + getExtraJavaSize(), mRegisteredNativeSize);
104   }
105 
106   /**
107    * Returns the number of bytes taken up by this object on the Java heap
108    * beyond the standard instance size as recorded by the class of this
109    * instance.
110    *
111    * For example, class objects will have extra size for static fields and
112    * array objects will have extra size for the array elements.
113    */
getExtraJavaSize()114   abstract long getExtraJavaSize();
115 
116   /**
117    * Returns the number of bytes retained by this object in the given heap.
118    * The returned size includes the shallow size of this object and the size
119    * of all objects directly or indirectly retained by this object. Only those
120    * objects allocated on the given heap are included in the reported size.
121    *
122    * @param heap the heap to get the retained size for
123    * @return the retained size of the object
124    */
getRetainedSize(AhatHeap heap)125   public Size getRetainedSize(AhatHeap heap) {
126     int index = heap.getIndex();
127     if (mRetainedSizes != null && 0 <= index && index < mRetainedSizes.length) {
128       return mRetainedSizes[heap.getIndex()];
129     }
130     return Size.ZERO;
131   }
132 
133   /**
134    * Returns the total number of bytes retained by this object. The returned
135    * size includes the shallow size of this object and the size of all objects
136    * directly or indirectly retained by this object.
137    *
138    * @return the total retained size of the object
139    */
getTotalRetainedSize()140   public Size getTotalRetainedSize() {
141     Size size = Size.ZERO;
142     if (mRetainedSizes != null) {
143       for (int i = 0; i < mRetainedSizes.length; i++) {
144         size = size.plus(mRetainedSizes[i]);
145       }
146     }
147     return size;
148   }
149 
150   /**
151    * Increment the number of registered native bytes tied to this object.
152    */
addRegisteredNativeSize(long size)153   void addRegisteredNativeSize(long size) {
154     mRegisteredNativeSize += size;
155   }
156 
157   /**
158    * Returns the reachability of the instance.
159    *
160    * @return the reachability of the instance.
161    */
getReachability()162   public Reachability getReachability() {
163     return mReachability;
164   }
165 
166   /**
167    * Returns true if this object is strongly reachable. An object is strongly
168    * reachable if there exists a path of (strong) references from some root
169    * object to this object.
170    *
171    * @return true if the object is strongly reachable
172    */
isStronglyReachable()173   public boolean isStronglyReachable() {
174     return mReachability == Reachability.STRONG;
175   }
176 
177   /**
178    * Returns true if this object is reachable only through a
179    * soft/weak/phantom/finalizer reference. An object is weakly reachable if
180    * it is not strongly reachable but there still exists a path of references
181    * from some root object to this object.  Because the object is not strongly
182    * reachable, any such path must contain a SoftReference, WeakReference,
183    * PhantomReference, or FinalizerReference somewhere along it.
184    * <p>
185    * Unlike a strongly reachable object, a weakly reachable object is allowed
186    * to be garbage collected.
187    *
188    * @deprecated Use {@link #getReachability()} instead, which can distinguish
189    *             among soft, weak, phantom, and other kinds of references.
190    *
191    * @return true if the object is weakly reachable
192    */
isWeaklyReachable()193   @Deprecated public boolean isWeaklyReachable() {
194     return !isStronglyReachable() && !isUnreachable();
195   }
196 
197   /**
198    * Returns true if this object is completely unreachable. An object is
199    * completely unreachable if there is no path to the object from some root
200    * object, neither through strong nor soft/weak/phantom/finalizer
201    * references.
202    *
203    * @return true if the object is completely unreachable
204    */
isUnreachable()205   public boolean isUnreachable() {
206     return mReachability == Reachability.UNREACHABLE;
207   }
208 
209   /**
210    * Returns the heap that this instance is allocated on.
211    *
212    * @return heap the instance is allocated on
213    */
getHeap()214   public AhatHeap getHeap() {
215     return mHeap;
216   }
217 
218   /**
219    * Returns an iterator over the references this AhatInstance has to other
220    * AhatInstances.
221    */
getReferences()222   abstract Iterable<Reference> getReferences();
223 
224   /**
225    * Returns true if this instance is a GC root.
226    *
227    * @return true if this instance is a GC root.
228    */
isRoot()229   public boolean isRoot() {
230     return mRootTypes != 0;
231   }
232 
233   /**
234    * Marks this instance as being a root of the given type.
235    */
addRootType(RootType type)236   void addRootType(RootType type) {
237     mRootTypes |= type.mask;
238   }
239 
240   /**
241    * Returns a list of the root types of this object.
242    * Returns null if this object is not a root.
243    *
244    * @return list of the objects root types
245    */
getRootTypes()246   public Collection<RootType> getRootTypes() {
247     if (!isRoot()) {
248       return null;
249     }
250 
251     List<RootType> types = new ArrayList<RootType>();
252     for (RootType type : RootType.values()) {
253       if ((mRootTypes & type.mask) != 0) {
254         types.add(type);
255       }
256     }
257     return types;
258   }
259 
260   /**
261    * Returns the immediate dominator of this instance.
262    * Returns null if this is a root instance.
263    *
264    * @return the immediate dominator of this instance
265    */
getImmediateDominator()266   public AhatInstance getImmediateDominator() {
267     if (mImmediateDominator instanceof SuperRoot) {
268       return null;
269     }
270     return mImmediateDominator;
271   }
272 
273   /**
274    * Returns a list of objects immediately dominated by this instance.
275    *
276    * @return list of immediately dominated objects
277    */
getDominated()278   public List<AhatInstance> getDominated() {
279     return mDominated;
280   }
281 
282   /**
283    * Returns the site where this instance was allocated.
284    *
285    * @return the object's allocation site
286    */
getSite()287   public Site getSite() {
288     return mSite;
289   }
290 
291   /**
292    * Returns true if this instance is a class object
293    *
294    * @return true if this instance is a class object
295    */
isClassObj()296   public boolean isClassObj() {
297     // Overridden by AhatClassObj.
298     return false;
299   }
300 
301   /**
302    * Returns this as an AhatClassObj if this is an AhatClassObj.
303    * Returns null if this is not an AhatClassObj.
304    *
305    * @return this instance as a class object
306    */
asClassObj()307   public AhatClassObj asClassObj() {
308     // Overridden by AhatClassObj.
309     return null;
310   }
311 
312   /**
313    * Returns the class object for this instance.
314    * For example, if this object is an instance of java.lang.String, this
315    * method returns the AhatClassObj for java.lang.String.
316    *
317    * @return the instance's class object
318    */
getClassObj()319   public AhatClassObj getClassObj() {
320     return mClassObj;
321   }
322 
323   /**
324    * Returns the name of the class this object belongs to.
325    * For example, if this object is an instance of java.lang.String, returns
326    * "java.lang.String".
327    *
328    * @return the name of this instance's class
329    */
getClassName()330   public String getClassName() {
331     AhatClassObj classObj = getClassObj();
332     return classObj == null ? "???" : classObj.getName();
333   }
334 
335   /**
336    * Returns true if this is an instance of a (subclass of a) class with the
337    * given name.
338    *
339    * @param className the name of the class to check for
340    * @return true if this is an instance of a (subclass of a) class with the
341    *              given name
342    */
isInstanceOfClass(String className)343   public boolean isInstanceOfClass(String className) {
344     AhatClassObj cls = getClassObj();
345     while (cls != null) {
346       if (className.equals(cls.getName())) {
347         return true;
348       }
349       cls = cls.getSuperClassObj();
350     }
351     return false;
352   }
353 
354   /**
355    * Returns true if the given instance is an array instance.
356    *
357    * @return true if the given instance is an array instance
358    */
isArrayInstance()359   public boolean isArrayInstance() {
360     // Overridden by AhatArrayInstance.
361     return false;
362   }
363 
364   /**
365    * Returns this as an AhatArrayInstance if this is an AhatArrayInstance.
366    * Returns null if this is not an AhatArrayInstance.
367    *
368    * @return this instance as an array instance
369    */
asArrayInstance()370   public AhatArrayInstance asArrayInstance() {
371     // Overridden by AhatArrayInstance.
372     return null;
373   }
374 
375   /**
376    * Returns true if this instance is a class instance.
377    *
378    * @return true if this instance is a class instance
379    */
isClassInstance()380   public boolean isClassInstance() {
381     return false;
382   }
383 
384   /**
385    * Returns this as an AhatClassInstance if this is an AhatClassInstance.
386    * Returns null if this is not an AhatClassInstance.
387    *
388    * @return this instance as a class instance
389    */
asClassInstance()390   public AhatClassInstance asClassInstance() {
391     return null;
392   }
393 
394   /**
395    * Returns the <code>referent</code> associated with this instance.
396    * This is only relevant for instances of java.lang.ref.Reference or its
397    * subclasses. Returns null if the instance has no referent associated with
398    * it.
399    *
400    * @return the referent associated with this instance
401    */
getReferent()402   public AhatInstance getReferent() {
403     // Overridden by AhatClassInstance.
404     return null;
405   }
406 
407   /**
408    * Returns a list of objects with any kind of reference to this object.
409    *
410    * @return the objects referencing this object
411    */
getReverseReferences()412   public List<AhatInstance> getReverseReferences() {
413     if (mReverseReferences != null) {
414       return mReverseReferences;
415     }
416     return Collections.emptyList();
417   }
418 
419   /**
420    * Returns a list of objects with (strong) references to this object.
421    *
422    * @deprecated Use {@link #getReverseReferences()} instead.
423    *
424    * @return the objects referencing this object
425    */
getHardReverseReferences()426   @Deprecated public List<AhatInstance> getHardReverseReferences() {
427     List<AhatInstance> refs = new ArrayList<AhatInstance>();
428     for (AhatInstance ref : getReverseReferences()) {
429       if (ref.getReachability() == Reachability.STRONG && ref.getReferent() != this) {
430         refs.add(ref);
431       }
432     }
433     return refs;
434   }
435 
436   /**
437    * Returns a list of objects with soft/weak/phantom/finalizer references to
438    * this object.
439    *
440    * @deprecated Use {@link #getReverseReferences()} instead.
441    *
442    * @return the objects weakly referencing this object
443    */
getSoftReverseReferences()444   @Deprecated public List<AhatInstance> getSoftReverseReferences() {
445     List<AhatInstance> refs = new ArrayList<AhatInstance>();
446     for (AhatInstance ref : getReverseReferences()) {
447       if (ref.getReachability() != Reachability.STRONG || ref.getReferent() == this) {
448         refs.add(ref);
449       }
450     }
451     return refs;
452   }
453 
454   /**
455    * Returns the value of a field of this instance. Returns null if the field
456    * value is null, the field couldn't be read, or there are multiple fields
457    * with the same name.
458    *
459    * @param fieldName the name of the field to get the value of
460    * @return the field value
461    */
getField(String fieldName)462   public Value getField(String fieldName) {
463     // Overridden by AhatClassInstance.
464     return null;
465   }
466 
467   /**
468    * Reads a reference field of this instance. Returns null if the field value
469    * is null, of primitive type, or if the field couldn't be read. There is no
470    * way using this method to distinguish between a reference field with value
471    * <code>null</code> and an invalid field.
472    *
473    * @param fieldName the name of the reference field to get the value of
474    * @return the reference field value
475    */
getRefField(String fieldName)476   public AhatInstance getRefField(String fieldName) {
477     // Overridden by AhatClassInstance.
478     return null;
479   }
480 
481   /**
482    * Returns the dex location associated with this object. Only applies to
483    * instances of dalvik.system.DexCache. If this is an instance of DexCache,
484    * returns the dex location for that dex cache. Otherwise returns null.
485    * If maxChars is non-negative, the returned location is truncated to
486    * maxChars in length.
487    *
488    * @param maxChars the maximum length of the returned string
489    * @return the dex location associated with this object
490    */
getDexCacheLocation(int maxChars)491   public String getDexCacheLocation(int maxChars) {
492     return null;
493   }
494 
495   /**
496    * Returns the name of the Binder proxy interface associated with this object.
497    * Only applies to instances of android.os.BinderProxy. If this is an
498    * instance of BinderProxy, returns the fully qualified binder interface name,
499    * otherwise returns null.
500    *
501    * @return the name of the binder interface associated with this object
502    */
getBinderProxyInterfaceName()503   public String getBinderProxyInterfaceName() {
504     return null;
505   }
506 
507   /**
508    * Returns the descriptor of the Binder token associated with this object.
509    * Only applies to instances of android.os.Binder. If this is an instance of
510    * android.os.Binder with a subclass of the name "descriptor$Stub", the
511    * object in question is a binder stub, and this function will return null.
512    * In that case, @see AhatInstance#getBinderStubInterfaceName
513    *
514    * @return the descriptor of this object, if it's a binder token
515    */
getBinderTokenDescriptor()516   public String getBinderTokenDescriptor() {
517     return null;
518   }
519 
520   /**
521    * Returns the name of the Binder stub interface associated with this object.
522    * Only applies to instances which are a subclass of android.os.Binder,
523    * and are an instance of class 'descriptor$Stub', where descriptor
524    * is the descriptor of the android.os.Binder object.
525    *
526    * @return the name of the binder interface associated with this object,
527    *         or null if this is not a binder stub interface.
528    */
getBinderStubInterfaceName()529   public String getBinderStubInterfaceName() {
530     return null;
531   }
532 
533   /**
534    * Returns the android.graphics.Bitmap instance associated with this object.
535    * Instances of android.graphics.Bitmap return themselves. If this is a
536    * byte[] array containing pixel data for an instance of
537    * android.graphics.Bitmap, that instance of android.graphics.Bitmap is
538    * returned. Otherwise null is returned.
539    *
540    * @return the bitmap instance associated with this object
541    */
getAssociatedBitmapInstance()542   public AhatInstance getAssociatedBitmapInstance() {
543     return null;
544   }
545 
546   /**
547    * Returns the class object that this object represents the overhead for.
548    * ART adds a fake byte[] $classOverhead static field to classes to show the
549    * overheads associated with the class. If this is one such byte[] instance,
550    * returns the class it is associated with. Otherwise null is returned.
551    *
552    * @return the class instance that this is the overhead for
553    */
getAssociatedClassForOverhead()554   public AhatClassObj getAssociatedClassForOverhead() {
555     return null;
556   }
557 
558   /**
559    * Returns the (bounded-length) string associated with this instance.
560    * Applies to instances of java.lang.String, char[], and in some cases
561    * byte[]. Returns null if this object cannot be interpreted as a string.
562    * If maxChars is non-negative, the returned string is truncated to maxChars
563    * characters in length.
564    *
565    * @param maxChars the maximum length of the returned string
566    * @return the string associated with this instance
567    */
asString(int maxChars)568   public String asString(int maxChars) {
569     // By default instances can't be interpreted as a string. This method is
570     // overridden by AhatClassInstance and AhatArrayInstance for those cases
571     // when an instance can be interpreted as a string.
572     return null;
573   }
574 
575   /**
576    * Returns the string associated with this instance. Applies to instances of
577    * java.lang.String, char[], and in some cases byte[]. Returns null if this
578    * object cannot be interpreted as a string.
579    *
580    * @return the string associated with this instance
581    */
asString()582   public String asString() {
583     return asString(-1);
584   }
585 
586   /**
587    * Returns the bitmap pixel data associated with this instance.
588    * This is relevant for instances of android.graphics.Bitmap and byte[].
589    * Returns null if there is no bitmap pixel data associated with the given
590    * instance.
591    *
592    * @return the bitmap pixel data associated with this image
593    */
asBitmap()594   public BufferedImage asBitmap() {
595     return null;
596   }
597 
598   static class RegisteredNativeAllocation {
599     public AhatInstance referent;
600     public long size;
601   };
602 
603   /**
604    * Return the registered native allocation that this instance represents, if
605    * any. This is relevant for instances of sun.misc.Cleaner.
606    */
asRegisteredNativeAllocation()607   RegisteredNativeAllocation asRegisteredNativeAllocation() {
608     return null;
609   }
610 
611   /**
612    * Returns a sample path from a GC root to this instance. The first element
613    * of the returned path is a GC root object. This instance is included as
614    * the last element of the path with an empty field description.
615    * <p>
616    * If the instance is strongly reachable, a path of string references will
617    * be returned. If the instance is weakly reachable, the returned path will
618    * include a soft/weak/phantom/finalizer reference somewhere along it.
619    * Returns null if this instance is not reachable.
620    *
621    * @return sample path from a GC root to this instance
622    * @see PathElement
623    */
getPathFromGcRoot()624   public List<PathElement> getPathFromGcRoot() {
625     if (isUnreachable()) {
626       return null;
627     }
628 
629     List<PathElement> path = new ArrayList<PathElement>();
630 
631     AhatInstance dom = this;
632     for (PathElement elem = new PathElement(this, ""); elem != null;
633         elem = getNextPathElementToGcRoot(elem.instance)) {
634       if (elem.instance.equals(dom)) {
635         elem.isDominator = true;
636         dom = dom.getImmediateDominator();
637       }
638       path.add(elem);
639     }
640     Collections.reverse(path);
641     return path;
642   }
643 
644   /**
645    * Returns the next instance to GC root from this object and a string
646    * description of which field of that object refers to the given instance.
647    * Returns null if the given instance has no next instance to the gc root.
648    */
getNextPathElementToGcRoot(AhatInstance inst)649   private static PathElement getNextPathElementToGcRoot(AhatInstance inst) {
650     if (inst.isRoot()) {
651       return null;
652     }
653     return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField);
654   }
655 
656   /**
657    * Returns a human-readable identifier for this object.
658    * For class objects, the string is the class name.
659    * For class instances, the string is the class name followed by '@' and the
660    * hex id of the instance.
661    * For array instances, the string is the array type followed by the size in
662    * square brackets, followed by '@' and the hex id of the instance.
663    *
664    * @return human-readable identifier for this object
665    */
toString()666   @Override public abstract String toString();
667 
668   /**
669    * Read the byte[] value from an hprof Instance.
670    * Returns null if the instance is not a byte array.
671    */
asByteArray()672   byte[] asByteArray() {
673     return null;
674   }
675 
setBaseline(AhatInstance baseline)676   void setBaseline(AhatInstance baseline) {
677     mBaseline = baseline;
678   }
679 
getBaseline()680   @Override public AhatInstance getBaseline() {
681     return mBaseline;
682   }
683 
isPlaceHolder()684   @Override public boolean isPlaceHolder() {
685     return false;
686   }
687 
688   /**
689    * Returns a new place holder instance corresponding to this instance.
690    */
newPlaceHolderInstance()691   AhatInstance newPlaceHolderInstance() {
692     return new AhatPlaceHolderInstance(this);
693   }
694 
setTemporaryUserData(Object state)695   void setTemporaryUserData(Object state) {
696     mTemporaryUserData = state;
697   }
698 
getTemporaryUserData()699   Object getTemporaryUserData() {
700     return mTemporaryUserData;
701   }
702 
703   /**
704    * Determine the reachability of the all instances reachable from the given
705    * root instance. Initializes the following fields:
706    *   mReachability
707    *   mNextInstanceToGcRoot
708    *   mNextInstanceToGcRootField
709    *   mReverseReferences
710    *
711    * @param progress used to track progress of the traversal.
712    * @param numInsts upper bound on the total number of instances reachable
713    *                 from the root, solely used for the purposes of tracking
714    *                 progress.
715    */
computeReachability(SuperRoot root, Progress progress, long numInsts)716   static void computeReachability(SuperRoot root, Progress progress, long numInsts) {
717     // Start by doing a breadth first search through strong references.
718     // Then continue the breadth first through each weaker kind of reference.
719     progress.start("Computing reachability", numInsts);
720     EnumMap<Reachability, Queue<Reference>> queues = new EnumMap<>(Reachability.class);
721     for (Reachability reachability : Reachability.values()) {
722       queues.put(reachability, new ArrayDeque<Reference>());
723     }
724 
725     for (Reference ref : root.getReferences()) {
726       queues.get(Reachability.STRONG).add(ref);
727     }
728 
729     for (Reachability reachability : Reachability.values()) {
730       Queue<Reference> queue = queues.get(reachability);
731       while (!queue.isEmpty()) {
732         Reference ref = queue.poll();
733         if (ref.ref.mReachability == Reachability.UNREACHABLE) {
734           // This is the first time we have seen ref.ref.
735           progress.advance();
736           ref.ref.mReachability = reachability;
737           ref.ref.mNextInstanceToGcRoot = ref.src;
738           ref.ref.mNextInstanceToGcRootField = ref.field;
739           ref.ref.mReverseReferences = new ArrayList<AhatInstance>();
740 
741           for (Reference childRef : ref.ref.getReferences()) {
742             if (childRef.reachability.notWeakerThan(reachability)) {
743               queue.add(childRef);
744             } else {
745               queues.get(childRef.reachability).add(childRef);
746             }
747           }
748         }
749 
750         // Note: We specifically exclude 'root' from the reverse references
751         // because it is a fake SuperRoot instance not present in the original
752         // heap dump.
753         if (ref.src != root) {
754           ref.ref.mReverseReferences.add(ref.src);
755         }
756       }
757     }
758     progress.done();
759   }
760 
761   /**
762    * Recursively compute the retained size of the given instance and all
763    * other instances it dominates.
764    */
computeRetainedSize(AhatInstance inst, int numHeaps)765   static void computeRetainedSize(AhatInstance inst, int numHeaps) {
766     // Note: We can't use a recursive implementation because it can lead to
767     // stack overflow. Use an iterative implementation instead.
768     //
769     // Objects not yet processed will have mRetainedSizes set to null.
770     // Once prepared, an object will have mRetaiedSizes set to an array of 0
771     // sizes.
772     Deque<AhatInstance> deque = new ArrayDeque<AhatInstance>();
773     deque.push(inst);
774 
775     while (!deque.isEmpty()) {
776       inst = deque.pop();
777       if (inst.mRetainedSizes == null) {
778         inst.mRetainedSizes = new Size[numHeaps];
779         for (int i = 0; i < numHeaps; i++) {
780           inst.mRetainedSizes[i] = Size.ZERO;
781         }
782         if (!(inst instanceof SuperRoot)) {
783           inst.mRetainedSizes[inst.mHeap.getIndex()] =
784             inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.getSize());
785         }
786         deque.push(inst);
787         for (AhatInstance dominated : inst.mDominated) {
788           deque.push(dominated);
789         }
790       } else {
791         for (AhatInstance dominated : inst.mDominated) {
792           for (int i = 0; i < numHeaps; i++) {
793             inst.mRetainedSizes[i] = inst.mRetainedSizes[i].plus(dominated.mRetainedSizes[i]);
794           }
795         }
796       }
797     }
798   }
799 
getReferencesForDominators(Reachability retained)800   Iterable<AhatInstance> getReferencesForDominators(Reachability retained) {
801     return new DominatorReferenceIterator(retained, getReferences());
802   }
803 
setDominator(AhatInstance dominator)804   void setDominator(AhatInstance dominator) {
805     mImmediateDominator = dominator;
806     mImmediateDominator.mDominated.add(this);
807   }
808 }
809