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