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