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.dominators.DominatorsComputation; 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.List; 27 import java.util.Queue; 28 29 /** 30 * A Java instance from a parsed heap dump. It is the base class used for all 31 * kinds of Java instances, including normal Java objects, class objects, and 32 * arrays. 33 */ 34 public abstract class AhatInstance implements Diffable<AhatInstance>, 35 DominatorsComputation.Node { 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 computeReverseReferences(). 51 private AhatInstance mNextInstanceToGcRoot; 52 private String mNextInstanceToGcRootField; 53 private ArrayList<AhatInstance> mHardReverseReferences; 54 private ArrayList<AhatInstance> mSoftReverseReferences; 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 site.addInstance(this); 84 mClassObj = classObj; 85 } 86 87 /** 88 * Returns a unique identifier for this instance. 89 * 90 * @return id of the instance 91 */ getId()92 public long getId() { 93 return mId; 94 } 95 96 /** 97 * Returns the number of bytes used for this object in the heap. 98 * The returned size is a shallow size for the object that does not include 99 * sizes of other objects dominated by this object. 100 * 101 * @return the shallow size of the object 102 */ getSize()103 public Size getSize() { 104 return new Size(mClassObj.getInstanceSize() + getExtraJavaSize(), mRegisteredNativeSize); 105 } 106 107 /** 108 * Returns the number of bytes taken up by this object on the Java heap 109 * beyond the standard instance size as recorded by the class of this 110 * instance. 111 * 112 * For example, class objects will have extra size for static fields and 113 * array objects will have extra size for the array elements. 114 */ getExtraJavaSize()115 abstract long getExtraJavaSize(); 116 117 /** 118 * Returns the number of bytes retained by this object in the given heap. 119 * The returned size includes the shallow size of this object and the size 120 * of all objects directly or indirectly retained by this object. Only those 121 * objects allocated on the given heap are included in the reported size. 122 * 123 * @param heap the heap to get the retained size for 124 * @return the retained size of the object 125 */ getRetainedSize(AhatHeap heap)126 public Size getRetainedSize(AhatHeap heap) { 127 int index = heap.getIndex(); 128 if (mRetainedSizes != null && 0 <= index && index < mRetainedSizes.length) { 129 return mRetainedSizes[heap.getIndex()]; 130 } 131 return Size.ZERO; 132 } 133 134 /** 135 * Returns the total number of bytes retained by this object. The returned 136 * size includes the shallow size of this object and the size of all objects 137 * directly or indirectly retained by this object. 138 * 139 * @return the total retained size of the object 140 */ getTotalRetainedSize()141 public Size getTotalRetainedSize() { 142 Size size = Size.ZERO; 143 if (mRetainedSizes != null) { 144 for (int i = 0; i < mRetainedSizes.length; i++) { 145 size = size.plus(mRetainedSizes[i]); 146 } 147 } 148 return size; 149 } 150 151 /** 152 * Increment the number of registered native bytes tied to this object. 153 */ addRegisteredNativeSize(long size)154 void addRegisteredNativeSize(long size) { 155 mRegisteredNativeSize += size; 156 } 157 158 /** 159 * Returns true if this object is strongly reachable. An object is strongly 160 * reachable if there exists a path of (strong) references from some root 161 * object to this object. 162 * 163 * @return true if the object is strongly reachable 164 */ isStronglyReachable()165 public boolean isStronglyReachable() { 166 return mImmediateDominator != null; 167 } 168 169 /** 170 * Returns true if this object is reachable only through a 171 * soft/weak/phantom/finalizer reference. An object is weakly reachable if 172 * it is not strongly reachable but there still exists a path of references 173 * from some root object to this object. Because the object is not strongly 174 * reachable, any such path must contain a SoftReference, WeakReference, 175 * PhantomReference, or FinalizerReference somewhere along it. 176 * <p> 177 * Unlike a strongly reachable object, a weakly reachable object is allowed 178 * to be garbage collected. 179 * 180 * @return true if the object is weakly reachable 181 */ isWeaklyReachable()182 public boolean isWeaklyReachable() { 183 return !isStronglyReachable() && mNextInstanceToGcRoot != null; 184 } 185 186 /** 187 * Returns true if this object is completely unreachable. An object is 188 * completely unreachable if there is no path to the object from some root 189 * object, neither through strong nor soft/weak/phantom/finalizer 190 * references. 191 * 192 * @return true if the object is completely unreachable 193 */ isUnreachable()194 public boolean isUnreachable() { 195 return !isStronglyReachable() && !isWeaklyReachable(); 196 } 197 198 /** 199 * Returns the heap that this instance is allocated on. 200 * 201 * @return heap the instance is allocated on 202 */ getHeap()203 public AhatHeap getHeap() { 204 return mHeap; 205 } 206 207 /** 208 * Returns an iterator over the references this AhatInstance has to other 209 * AhatInstances. 210 */ getReferences()211 abstract Iterable<Reference> getReferences(); 212 213 /** 214 * Returns true if this instance is a GC root. 215 * 216 * @return true if this instance is a GC root. 217 * @see getRootTypes 218 */ isRoot()219 public boolean isRoot() { 220 return mRootTypes != 0; 221 } 222 223 /** 224 * Marks this instance as being a root of the given type. 225 */ addRootType(RootType type)226 void addRootType(RootType type) { 227 mRootTypes |= type.mask; 228 } 229 230 /** 231 * Returns a list of the root types of this object. 232 * Returns null if this object is not a root. 233 * 234 * @return list of the objects root types 235 */ getRootTypes()236 public Collection<RootType> getRootTypes() { 237 if (!isRoot()) { 238 return null; 239 } 240 241 List<RootType> types = new ArrayList<RootType>(); 242 for (RootType type : RootType.values()) { 243 if ((mRootTypes & type.mask) != 0) { 244 types.add(type); 245 } 246 } 247 return types; 248 } 249 250 /** 251 * Returns the immediate dominator of this instance. 252 * Returns null if this is a root instance. 253 * 254 * @return the immediate dominator of this instance 255 */ getImmediateDominator()256 public AhatInstance getImmediateDominator() { 257 return mImmediateDominator; 258 } 259 260 /** 261 * Returns a list of objects immediately dominated by this instance. 262 * 263 * @return list of immediately dominated objects 264 */ getDominated()265 public List<AhatInstance> getDominated() { 266 return mDominated; 267 } 268 269 /** 270 * Returns the site where this instance was allocated. 271 * 272 * @return the object's allocation site 273 */ getSite()274 public Site getSite() { 275 return mSite; 276 } 277 278 /** 279 * Returns true if this instance is a class object 280 * 281 * @return true if this instance is a class object 282 */ isClassObj()283 public boolean isClassObj() { 284 // Overridden by AhatClassObj. 285 return false; 286 } 287 288 /** 289 * Returns this as an AhatClassObj if this is an AhatClassObj. 290 * Returns null if this is not an AhatClassObj. 291 * 292 * @return this instance as a class object 293 */ asClassObj()294 public AhatClassObj asClassObj() { 295 // Overridden by AhatClassObj. 296 return null; 297 } 298 299 /** 300 * Returns the class object for this instance. 301 * For example, if this object is an instance of java.lang.String, this 302 * method returns the AhatClassObj for java.lang.String. 303 * 304 * @return the instance's class object 305 */ getClassObj()306 public AhatClassObj getClassObj() { 307 return mClassObj; 308 } 309 310 /** 311 * Returns the name of the class this object belongs to. 312 * For example, if this object is an instance of java.lang.String, returns 313 * "java.lang.String". 314 * 315 * @return the name of this instance's class 316 */ getClassName()317 public String getClassName() { 318 AhatClassObj classObj = getClassObj(); 319 return classObj == null ? "???" : classObj.getName(); 320 } 321 322 /** 323 * Returns true if the given instance is an array instance. 324 * 325 * @return true if the given instance is an array instance 326 */ isArrayInstance()327 public boolean isArrayInstance() { 328 // Overridden by AhatArrayInstance. 329 return false; 330 } 331 332 /** 333 * Returns this as an AhatArrayInstance if this is an AhatArrayInstance. 334 * Returns null if this is not an AhatArrayInstance. 335 * 336 * @return this instance as an array instance 337 */ asArrayInstance()338 public AhatArrayInstance asArrayInstance() { 339 // Overridden by AhatArrayInstance. 340 return null; 341 } 342 343 /** 344 * Returns true if this instance is a class instance. 345 * 346 * @return true if this instance is a class instance 347 */ isClassInstance()348 public boolean isClassInstance() { 349 return false; 350 } 351 352 /** 353 * Returns this as an AhatClassInstance if this is an AhatClassInstance. 354 * Returns null if this is not an AhatClassInstance. 355 * 356 * @return this instance as a class instance 357 */ asClassInstance()358 public AhatClassInstance asClassInstance() { 359 return null; 360 } 361 362 /** 363 * Returns the <code>referent</code> associated with this instance. 364 * This is only relevant for instances of java.lang.ref.Reference or its 365 * subclasses. Returns null if the instance has no referent associated with 366 * it. 367 * 368 * @return the referent associated with this instance 369 */ getReferent()370 public AhatInstance getReferent() { 371 // Overridden by AhatClassInstance. 372 return null; 373 } 374 375 /** 376 * Returns a list of objects with (strong) references to this object. 377 * 378 * @return the objects referencing this object 379 */ getHardReverseReferences()380 public List<AhatInstance> getHardReverseReferences() { 381 if (mHardReverseReferences != null) { 382 return mHardReverseReferences; 383 } 384 return Collections.emptyList(); 385 } 386 387 /** 388 * Returns a list of objects with soft/weak/phantom/finalizer references to 389 * this object. 390 * 391 * @return the objects weakly referencing this object 392 */ getSoftReverseReferences()393 public List<AhatInstance> getSoftReverseReferences() { 394 if (mSoftReverseReferences != null) { 395 return mSoftReverseReferences; 396 } 397 return Collections.emptyList(); 398 } 399 400 /** 401 * Returns the value of a field of this instance. Returns null if the field 402 * value is null, the field couldn't be read, or there are multiple fields 403 * with the same name. 404 * 405 * @param fieldName the name of the field to get the value of 406 * @return the field value 407 */ getField(String fieldName)408 public Value getField(String fieldName) { 409 // Overridden by AhatClassInstance. 410 return null; 411 } 412 413 /** 414 * Reads a reference field of this instance. Returns null if the field value 415 * is null, of primitive type, or if the field couldn't be read. There is no 416 * way using this method to distinguish between a reference field with value 417 * <code>null</code> and an invalid field. 418 * 419 * @param fieldName the name of the reference field to get the value of 420 * @return the reference field value 421 */ getRefField(String fieldName)422 public AhatInstance getRefField(String fieldName) { 423 // Overridden by AhatClassInstance. 424 return null; 425 } 426 427 /** 428 * Returns the dex location associated with this object. Only applies to 429 * instances of dalvik.system.DexCache. If this is an instance of DexCache, 430 * returns the dex location for that dex cache. Otherwise returns null. 431 * If maxChars is non-negative, the returned location is truncated to 432 * maxChars in length. 433 * 434 * @param maxChars the maximum length of the returned string 435 * @return the dex location associated with this object 436 */ getDexCacheLocation(int maxChars)437 public String getDexCacheLocation(int maxChars) { 438 return null; 439 } 440 441 /** 442 * Returns the android.graphics.Bitmap instance associated with this object. 443 * Instances of android.graphics.Bitmap return themselves. If this is a 444 * byte[] array containing pixel data for an instance of 445 * android.graphics.Bitmap, that instance of android.graphics.Bitmap is 446 * returned. Otherwise null is returned. 447 * 448 * @return the bitmap instance associated with this object 449 */ getAssociatedBitmapInstance()450 public AhatInstance getAssociatedBitmapInstance() { 451 return null; 452 } 453 454 /** 455 * Returns the (bounded-length) string associated with this instance. 456 * Applies to instances of java.lang.String, char[], and in some cases 457 * byte[]. Returns null if this object cannot be interpreted as a string. 458 * If maxChars is non-negative, the returned string is truncated to maxChars 459 * characters in length. 460 * 461 * @param maxChars the maximum length of the returned string 462 * @return the string associated with this instance 463 */ asString(int maxChars)464 public String asString(int maxChars) { 465 // By default instances can't be interpreted as a string. This method is 466 // overridden by AhatClassInstance and AhatArrayInstance for those cases 467 // when an instance can be interpreted as a string. 468 return null; 469 } 470 471 /** 472 * Returns the string associated with this instance. Applies to instances of 473 * java.lang.String, char[], and in some cases byte[]. Returns null if this 474 * object cannot be interpreted as a string. 475 * 476 * @return the string associated with this instance 477 */ asString()478 public String asString() { 479 return asString(-1); 480 } 481 482 /** 483 * Returns the bitmap pixel data associated with this instance. 484 * This is relevant for instances of android.graphics.Bitmap and byte[]. 485 * Returns null if there is no bitmap pixel data associated with the given 486 * instance. 487 * 488 * @return the bitmap pixel data associated with this image 489 */ asBitmap()490 public BufferedImage asBitmap() { 491 return null; 492 } 493 494 static class RegisteredNativeAllocation { 495 public AhatInstance referent; 496 public long size; 497 }; 498 499 /** 500 * Return the registered native allocation that this instance represents, if 501 * any. This is relevant for instances of sun.misc.Cleaner. 502 */ asRegisteredNativeAllocation()503 RegisteredNativeAllocation asRegisteredNativeAllocation() { 504 return null; 505 } 506 507 /** 508 * Returns a sample path from a GC root to this instance. The first element 509 * of the returned path is a GC root object. This instance is included as 510 * the last element of the path with an empty field description. 511 * <p> 512 * If the instance is strongly reachable, a path of string references will 513 * be returned. If the instance is weakly reachable, the returned path will 514 * include a soft/weak/phantom/finalizer reference somewhere along it. 515 * Returns null if this instance is not reachable. 516 * 517 * @return sample path from a GC root to this instance 518 * @see PathElement 519 */ getPathFromGcRoot()520 public List<PathElement> getPathFromGcRoot() { 521 if (isUnreachable()) { 522 return null; 523 } 524 525 List<PathElement> path = new ArrayList<PathElement>(); 526 527 AhatInstance dom = this; 528 for (PathElement elem = new PathElement(this, ""); elem != null; 529 elem = getNextPathElementToGcRoot(elem.instance)) { 530 if (elem.instance.equals(dom)) { 531 elem.isDominator = true; 532 dom = dom.getImmediateDominator(); 533 } 534 path.add(elem); 535 } 536 Collections.reverse(path); 537 return path; 538 } 539 540 /** 541 * Returns the next instance to GC root from this object and a string 542 * description of which field of that object refers to the given instance. 543 * Returns null if the given instance has no next instance to the gc root. 544 */ getNextPathElementToGcRoot(AhatInstance inst)545 private static PathElement getNextPathElementToGcRoot(AhatInstance inst) { 546 if (inst.isRoot()) { 547 return null; 548 } 549 return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField); 550 } 551 552 /** 553 * Returns a human-readable identifier for this object. 554 * For class objects, the string is the class name. 555 * For class instances, the string is the class name followed by '@' and the 556 * hex id of the instance. 557 * For array instances, the string is the array type followed by the size in 558 * square brackets, followed by '@' and the hex id of the instance. 559 * 560 * @return human-readable identifier for this object 561 */ toString()562 @Override public abstract String toString(); 563 564 /** 565 * Read the byte[] value from an hprof Instance. 566 * Returns null if the instance is not a byte array. 567 */ asByteArray()568 byte[] asByteArray() { 569 return null; 570 } 571 setBaseline(AhatInstance baseline)572 void setBaseline(AhatInstance baseline) { 573 mBaseline = baseline; 574 } 575 getBaseline()576 @Override public AhatInstance getBaseline() { 577 return mBaseline; 578 } 579 isPlaceHolder()580 @Override public boolean isPlaceHolder() { 581 return false; 582 } 583 584 /** 585 * Returns a new place holder instance corresponding to this instance. 586 */ newPlaceHolderInstance()587 AhatInstance newPlaceHolderInstance() { 588 return new AhatPlaceHolderInstance(this); 589 } 590 setTemporaryUserData(Object state)591 void setTemporaryUserData(Object state) { 592 mTemporaryUserData = state; 593 } 594 getTemporaryUserData()595 Object getTemporaryUserData() { 596 return mTemporaryUserData; 597 } 598 599 /** 600 * Initialize the reverse reference fields of this instance and all other 601 * instances reachable from it. Initializes the following fields: 602 * mNextInstanceToGcRoot 603 * mNextInstanceToGcRootField 604 * mHardReverseReferences 605 * mSoftReverseReferences 606 */ computeReverseReferences(SuperRoot root)607 static void computeReverseReferences(SuperRoot root) { 608 // Start by doing a breadth first search through strong references. 609 // Then continue the breadth first search through weak references. 610 Queue<Reference> strong = new ArrayDeque<Reference>(); 611 Queue<Reference> weak = new ArrayDeque<Reference>(); 612 613 for (Reference ref : root.getReferences()) { 614 strong.add(ref); 615 } 616 617 while (!strong.isEmpty()) { 618 Reference ref = strong.poll(); 619 assert ref.strong; 620 621 if (ref.ref.mNextInstanceToGcRoot == null) { 622 // This is the first time we have seen ref.ref. 623 ref.ref.mNextInstanceToGcRoot = ref.src; 624 ref.ref.mNextInstanceToGcRootField = ref.field; 625 ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>(); 626 627 for (Reference childRef : ref.ref.getReferences()) { 628 if (childRef.strong) { 629 strong.add(childRef); 630 } else { 631 weak.add(childRef); 632 } 633 } 634 } 635 636 // Note: We specifically exclude 'root' from the reverse references 637 // because it is a fake SuperRoot instance not present in the original 638 // heap dump. 639 if (ref.src != root) { 640 ref.ref.mHardReverseReferences.add(ref.src); 641 } 642 } 643 644 while (!weak.isEmpty()) { 645 Reference ref = weak.poll(); 646 647 if (ref.ref.mNextInstanceToGcRoot == null) { 648 // This is the first time we have seen ref.ref. 649 ref.ref.mNextInstanceToGcRoot = ref.src; 650 ref.ref.mNextInstanceToGcRootField = ref.field; 651 ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>(); 652 653 for (Reference childRef : ref.ref.getReferences()) { 654 weak.add(childRef); 655 } 656 } 657 658 if (ref.strong) { 659 ref.ref.mHardReverseReferences.add(ref.src); 660 } else { 661 if (ref.ref.mSoftReverseReferences == null) { 662 ref.ref.mSoftReverseReferences = new ArrayList<AhatInstance>(); 663 } 664 ref.ref.mSoftReverseReferences.add(ref.src); 665 } 666 } 667 } 668 669 /** 670 * Recursively compute the retained size of the given instance and all 671 * other instances it dominates. 672 */ computeRetainedSize(AhatInstance inst, int numHeaps)673 static void computeRetainedSize(AhatInstance inst, int numHeaps) { 674 // Note: We can't use a recursive implementation because it can lead to 675 // stack overflow. Use an iterative implementation instead. 676 // 677 // Objects not yet processed will have mRetainedSizes set to null. 678 // Once prepared, an object will have mRetaiedSizes set to an array of 0 679 // sizes. 680 Deque<AhatInstance> deque = new ArrayDeque<AhatInstance>(); 681 deque.push(inst); 682 683 while (!deque.isEmpty()) { 684 inst = deque.pop(); 685 if (inst.mRetainedSizes == null) { 686 inst.mRetainedSizes = new Size[numHeaps]; 687 for (int i = 0; i < numHeaps; i++) { 688 inst.mRetainedSizes[i] = Size.ZERO; 689 } 690 if (!(inst instanceof SuperRoot)) { 691 inst.mRetainedSizes[inst.mHeap.getIndex()] = 692 inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.getSize()); 693 } 694 deque.push(inst); 695 for (AhatInstance dominated : inst.mDominated) { 696 deque.push(dominated); 697 } 698 } else { 699 for (AhatInstance dominated : inst.mDominated) { 700 for (int i = 0; i < numHeaps; i++) { 701 inst.mRetainedSizes[i] = inst.mRetainedSizes[i].plus(dominated.mRetainedSizes[i]); 702 } 703 } 704 } 705 } 706 } 707 708 @Override setDominatorsComputationState(Object state)709 public void setDominatorsComputationState(Object state) { 710 setTemporaryUserData(state); 711 } 712 713 @Override getDominatorsComputationState()714 public Object getDominatorsComputationState() { 715 return getTemporaryUserData(); 716 } 717 718 @Override getReferencesForDominators()719 public Iterable<? extends DominatorsComputation.Node> getReferencesForDominators() { 720 return new DominatorReferenceIterator(getReferences()); 721 } 722 723 @Override setDominator(DominatorsComputation.Node dominator)724 public void setDominator(DominatorsComputation.Node dominator) { 725 mImmediateDominator = (AhatInstance)dominator; 726 mImmediateDominator.mDominated.add(this); 727 } 728 } 729