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