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 true if this instance is a bitmap instance.
396    * @return true if this instance is a bitmap instance
397    */
isBitmapInstance()398   public boolean isBitmapInstance() {
399     return false;
400   }
401 
402   /**
403    * Returns this as an AhatBitmapInstance if this is an AhatBitmapInstance.
404    * Returns null if this is not an AhatBitmapInstance.
405    *
406    * @return this instance as a bitmap instance
407    */
asBitmapInstance()408   public AhatBitmapInstance asBitmapInstance() {
409     return null;
410   }
411 
412   /**
413    * Returns the <code>referent</code> associated with this instance.
414    * This is only relevant for instances of java.lang.ref.Reference or its
415    * subclasses. Returns null if the instance has no referent associated with
416    * it.
417    *
418    * @return the referent associated with this instance
419    */
getReferent()420   public AhatInstance getReferent() {
421     // Overridden by AhatClassInstance.
422     return null;
423   }
424 
425   /**
426    * Returns a list of objects with any kind of reference to this object.
427    *
428    * @return the objects referencing this object
429    */
getReverseReferences()430   public List<AhatInstance> getReverseReferences() {
431     if (mReverseReferences != null) {
432       return mReverseReferences;
433     }
434     return Collections.emptyList();
435   }
436 
437   /**
438    * Returns a list of objects with (strong) references to this object.
439    *
440    * @deprecated Use {@link #getReverseReferences()} instead.
441    *
442    * @return the objects referencing this object
443    */
getHardReverseReferences()444   @Deprecated public List<AhatInstance> getHardReverseReferences() {
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 a list of objects with soft/weak/phantom/finalizer references to
456    * this object.
457    *
458    * @deprecated Use {@link #getReverseReferences()} instead.
459    *
460    * @return the objects weakly referencing this object
461    */
getSoftReverseReferences()462   @Deprecated public List<AhatInstance> getSoftReverseReferences() {
463     List<AhatInstance> refs = new ArrayList<AhatInstance>();
464     for (AhatInstance ref : getReverseReferences()) {
465       if (ref.getReachability() != Reachability.STRONG || ref.getReferent() == this) {
466         refs.add(ref);
467       }
468     }
469     return refs;
470   }
471 
472   /**
473    * Returns the value of a field of this instance. Returns null if the field
474    * value is null, the field couldn't be read, or there are multiple fields
475    * with the same name.
476    *
477    * @param fieldName the name of the field to get the value of
478    * @return the field value
479    */
getField(String fieldName)480   public Value getField(String fieldName) {
481     // Overridden by AhatClassInstance.
482     return null;
483   }
484 
485   /**
486    * Reads a reference field of this instance. Returns null if the field value
487    * is null, of primitive type, or if the field couldn't be read. There is no
488    * way using this method to distinguish between a reference field with value
489    * <code>null</code> and an invalid field.
490    *
491    * @param fieldName the name of the reference field to get the value of
492    * @return the reference field value
493    */
getRefField(String fieldName)494   public AhatInstance getRefField(String fieldName) {
495     // Overridden by AhatClassInstance.
496     return null;
497   }
498 
499   /**
500    * Returns the dex location associated with this object. Only applies to
501    * instances of dalvik.system.DexCache. If this is an instance of DexCache,
502    * returns the dex location for that dex cache. Otherwise returns null.
503    * If maxChars is non-negative, the returned location is truncated to
504    * maxChars in length.
505    *
506    * @param maxChars the maximum length of the returned string
507    * @return the dex location associated with this object
508    */
getDexCacheLocation(int maxChars)509   public String getDexCacheLocation(int maxChars) {
510     return null;
511   }
512 
513   /**
514    * Returns the name of the Binder proxy interface associated with this object.
515    * Only applies to instances of android.os.BinderProxy. If this is an
516    * instance of BinderProxy, returns the fully qualified binder interface name,
517    * otherwise returns null.
518    *
519    * @return the name of the binder interface associated with this object
520    */
getBinderProxyInterfaceName()521   public String getBinderProxyInterfaceName() {
522     return null;
523   }
524 
525   /**
526    * Returns the descriptor of the Binder token associated with this object.
527    * Only applies to instances of android.os.Binder. If this is an instance of
528    * android.os.Binder with a subclass of the name "descriptor$Stub", the
529    * object in question is a binder stub, and this function will return null.
530    * In that case, @see AhatInstance#getBinderStubInterfaceName
531    *
532    * @return the descriptor of this object, if it's a binder token
533    */
getBinderTokenDescriptor()534   public String getBinderTokenDescriptor() {
535     return null;
536   }
537 
538   /**
539    * Returns the name of the Binder stub interface associated with this object.
540    * Only applies to instances which are a subclass of android.os.Binder,
541    * and are an instance of class 'descriptor$Stub', where descriptor
542    * is the descriptor of the android.os.Binder object.
543    *
544    * @return the name of the binder interface associated with this object,
545    *         or null if this is not a binder stub interface.
546    */
getBinderStubInterfaceName()547   public String getBinderStubInterfaceName() {
548     return null;
549   }
550 
551   /**
552    * Returns the android.graphics.Bitmap instance associated with this object.
553    * Instances of android.graphics.Bitmap return themselves. If this is a
554    * byte[] array containing pixel data for an instance of
555    * android.graphics.Bitmap, that instance of android.graphics.Bitmap is
556    * returned. Otherwise null is returned.
557    *
558    * @return the bitmap instance associated with this object
559    */
getAssociatedBitmapInstance()560   public AhatInstance getAssociatedBitmapInstance() {
561     return null;
562   }
563 
564   /**
565    * Returns the class object that this object represents the overhead for.
566    * ART adds a fake byte[] $classOverhead static field to classes to show the
567    * overheads associated with the class. If this is one such byte[] instance,
568    * returns the class it is associated with. Otherwise null is returned.
569    *
570    * @return the class instance that this is the overhead for
571    */
getAssociatedClassForOverhead()572   public AhatClassObj getAssociatedClassForOverhead() {
573     return null;
574   }
575 
576   /**
577    * Returns the (bounded-length) string associated with this instance.
578    * Applies to instances of java.lang.String, char[], and in some cases
579    * byte[]. Returns null if this object cannot be interpreted as a string.
580    * If maxChars is non-negative, the returned string is truncated to maxChars
581    * characters in length.
582    *
583    * @param maxChars the maximum length of the returned string
584    * @return the string associated with this instance
585    */
asString(int maxChars)586   public String asString(int maxChars) {
587     // By default instances can't be interpreted as a string. This method is
588     // overridden by AhatClassInstance and AhatArrayInstance for those cases
589     // when an instance can be interpreted as a string.
590     return null;
591   }
592 
593   /**
594    * Returns the string associated with this instance. Applies to instances of
595    * java.lang.String, char[], and in some cases byte[]. Returns null if this
596    * object cannot be interpreted as a string.
597    *
598    * @return the string associated with this instance
599    */
asString()600   public String asString() {
601     return asString(-1);
602   }
603 
604   static class RegisteredNativeAllocation {
605     public AhatInstance referent;
606     public long size;
607   };
608 
609   /**
610    * Return the registered native allocation that this instance represents, if
611    * any. This is relevant for instances of sun.misc.Cleaner.
612    */
asRegisteredNativeAllocation()613   RegisteredNativeAllocation asRegisteredNativeAllocation() {
614     return null;
615   }
616 
617   /**
618    * Returns a sample path from a GC root to this instance. The first element
619    * of the returned path is a GC root object. This instance is included as
620    * the last element of the path with an empty field description.
621    * <p>
622    * If the instance is strongly reachable, a path of string references will
623    * be returned. If the instance is weakly reachable, the returned path will
624    * include a soft/weak/phantom/finalizer reference somewhere along it.
625    * Returns null if this instance is not reachable.
626    *
627    * @return sample path from a GC root to this instance
628    * @see PathElement
629    */
getPathFromGcRoot()630   public List<PathElement> getPathFromGcRoot() {
631     if (isUnreachable()) {
632       return null;
633     }
634 
635     List<PathElement> path = new ArrayList<PathElement>();
636 
637     AhatInstance dom = this;
638     for (PathElement elem = new PathElement(this, ""); elem != null;
639         elem = getNextPathElementToGcRoot(elem.instance)) {
640       if (elem.instance.equals(dom)) {
641         elem.isDominator = true;
642         dom = dom.getImmediateDominator();
643       }
644       path.add(elem);
645     }
646     Collections.reverse(path);
647     return path;
648   }
649 
650   /**
651    * Returns the next instance to GC root from this object and a string
652    * description of which field of that object refers to the given instance.
653    * Returns null if the given instance has no next instance to the gc root.
654    */
getNextPathElementToGcRoot(AhatInstance inst)655   private static PathElement getNextPathElementToGcRoot(AhatInstance inst) {
656     if (inst.isRoot()) {
657       return null;
658     }
659     return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField);
660   }
661 
662   /**
663    * Returns a human-readable identifier for this object.
664    * For class objects, the string is the class name.
665    * For class instances, the string is the class name followed by '@' and the
666    * hex id of the instance.
667    * For array instances, the string is the array type followed by the size in
668    * square brackets, followed by '@' and the hex id of the instance.
669    *
670    * @return human-readable identifier for this object
671    */
toString()672   @Override public abstract String toString();
673 
674   /**
675    * Read the byte[] value from an hprof Instance.
676    * Returns null if the instance is not a byte array.
677    */
asByteArray()678   byte[] asByteArray() {
679     return null;
680   }
681 
setBaseline(AhatInstance baseline)682   void setBaseline(AhatInstance baseline) {
683     mBaseline = baseline;
684   }
685 
getBaseline()686   @Override public AhatInstance getBaseline() {
687     return mBaseline;
688   }
689 
isPlaceHolder()690   @Override public boolean isPlaceHolder() {
691     return false;
692   }
693 
694   /**
695    * Returns a new placeholder instance corresponding to this instance.
696    */
newPlaceHolderInstance()697   AhatInstance newPlaceHolderInstance() {
698     return new AhatPlaceHolderInstance(this);
699   }
700 
setTemporaryUserData(Object state)701   void setTemporaryUserData(Object state) {
702     mTemporaryUserData = state;
703   }
704 
getTemporaryUserData()705   Object getTemporaryUserData() {
706     return mTemporaryUserData;
707   }
708 
709   /**
710    * Determine the reachability of the all instances reachable from the given
711    * root instance. Initializes the following fields:
712    *   mReachability
713    *   mNextInstanceToGcRoot
714    *   mNextInstanceToGcRootField
715    *   mReverseReferences
716    *
717    * @param progress used to track progress of the traversal.
718    * @param numInsts upper bound on the total number of instances reachable
719    *                 from the root, solely used for the purposes of tracking
720    *                 progress.
721    */
computeReachability(SuperRoot root, Progress progress, long numInsts)722   static void computeReachability(SuperRoot root, Progress progress, long numInsts) {
723     // Start by doing a breadth first search through strong references.
724     // Then continue the breadth first through each weaker kind of reference.
725     progress.start("Computing reachability", numInsts);
726     EnumMap<Reachability, Queue<Reference>> queues = new EnumMap<>(Reachability.class);
727     for (Reachability reachability : Reachability.values()) {
728       queues.put(reachability, new ArrayDeque<Reference>());
729     }
730 
731     for (Reference ref : root.getReferences()) {
732       queues.get(Reachability.STRONG).add(ref);
733     }
734 
735     for (Reachability reachability : Reachability.values()) {
736       Queue<Reference> queue = queues.get(reachability);
737       while (!queue.isEmpty()) {
738         Reference ref = queue.poll();
739         if (ref.ref.mReachability == Reachability.UNREACHABLE) {
740           // This is the first time we have seen ref.ref.
741           progress.advance();
742           ref.ref.mReachability = reachability;
743           ref.ref.mNextInstanceToGcRoot = ref.src;
744           ref.ref.mNextInstanceToGcRootField = ref.field;
745           ref.ref.mReverseReferences = new ArrayList<AhatInstance>();
746 
747           for (Reference childRef : ref.ref.getReferences()) {
748             if (childRef.reachability.notWeakerThan(reachability)) {
749               queue.add(childRef);
750             } else {
751               queues.get(childRef.reachability).add(childRef);
752             }
753           }
754         }
755 
756         // Note: We specifically exclude 'root' from the reverse references
757         // because it is a fake SuperRoot instance not present in the original
758         // heap dump.
759         if (ref.src != root) {
760           ref.ref.mReverseReferences.add(ref.src);
761         }
762       }
763     }
764     progress.done();
765   }
766 
767   /**
768    * Recursively compute the retained size of the given instance and all
769    * other instances it dominates.
770    */
computeRetainedSize(AhatInstance inst, int numHeaps)771   static void computeRetainedSize(AhatInstance inst, int numHeaps) {
772     // Note: We can't use a recursive implementation because it can lead to
773     // stack overflow. Use an iterative implementation instead.
774     //
775     // Objects not yet processed will have mRetainedSizes set to null.
776     // Once prepared, an object will have mRetaiedSizes set to an array of 0
777     // sizes.
778     Deque<AhatInstance> deque = new ArrayDeque<AhatInstance>();
779     deque.push(inst);
780 
781     while (!deque.isEmpty()) {
782       inst = deque.pop();
783       if (inst.mRetainedSizes == null) {
784         inst.mRetainedSizes = new Size[numHeaps];
785         for (int i = 0; i < numHeaps; i++) {
786           inst.mRetainedSizes[i] = Size.ZERO;
787         }
788         if (!(inst instanceof SuperRoot)) {
789           inst.mRetainedSizes[inst.mHeap.getIndex()] =
790             inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.getSize());
791         }
792         deque.push(inst);
793         for (AhatInstance dominated : inst.mDominated) {
794           deque.push(dominated);
795         }
796       } else {
797         for (AhatInstance dominated : inst.mDominated) {
798           for (int i = 0; i < numHeaps; i++) {
799             inst.mRetainedSizes[i] = inst.mRetainedSizes[i].plus(dominated.mRetainedSizes[i]);
800           }
801         }
802       }
803     }
804   }
805 
getReferencesForDominators(Reachability retained)806   Iterable<AhatInstance> getReferencesForDominators(Reachability retained) {
807     return new DominatorReferenceIterator(retained, getReferences());
808   }
809 
setDominator(AhatInstance dominator)810   void setDominator(AhatInstance dominator) {
811     mImmediateDominator = dominator;
812     mImmediateDominator.mDominated.add(this);
813   }
814 }
815