1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * The contents of this file are subject to the terms of either the Universal Permissive License 7 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 8 * 9 * or the following license: 10 * 11 * Redistribution and use in source and binary forms, with or without modification, are permitted 12 * provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 15 * and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 18 * conditions and the following disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 22 * endorse or promote products derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 package org.openjdk.jmc.flightrecorder.memleak; 34 35 import java.lang.reflect.Modifier; 36 import java.util.ArrayList; 37 import java.util.HashSet; 38 import java.util.List; 39 import java.util.Set; 40 41 import org.openjdk.jmc.common.IMCOldObject; 42 import org.openjdk.jmc.common.IMCOldObjectArray; 43 import org.openjdk.jmc.common.IMCOldObjectField; 44 import org.openjdk.jmc.common.IMCOldObjectGcRoot; 45 import org.openjdk.jmc.common.IMCType; 46 import org.openjdk.jmc.common.item.IItem; 47 import org.openjdk.jmc.common.unit.IQuantity; 48 49 /** 50 * A data type representing alive objects in a Java heap used in a {@link ReferenceTreeModel}. 51 */ 52 public class ReferenceTreeObject implements IMCOldObject { 53 54 public enum ReferenceTreeObjectType { 55 Array, InstanceField, JavaObject, LeakObject 56 } 57 58 private final List<ReferenceTreeObject> children = new ArrayList<>(); 59 private final Set<IItem> items = new HashSet<>(); 60 61 private String rootDescription; 62 private IMCOldObject object; 63 private int objectsKeptAliveCount; 64 private ReferenceTreeObject parent; 65 private IQuantity timestamp; 66 private double leakRelevance; 67 private int distanceFromRoot; 68 public static final int FORMAT_PACKAGE = 0b00001; 69 public static final int FORMAT_FIELD = 0b00010; 70 public static final int FORMAT_STATIC_MODIFIER = 0b00100; 71 public static final int FORMAT_OTHER_MODIFIERS = 0b01000; 72 public static final int FORMAT_ARRAY_INFO = 0b10000; 73 74 /** 75 * @param timestamp 76 * a timestamp representing when the object was allocated 77 * @param object 78 * the object itself 79 */ 80 ReferenceTreeObject(IQuantity timestamp, IMCOldObject object) { 81 this.timestamp = timestamp; 82 this.object = object; 83 leakRelevance = -1; 84 distanceFromRoot = 0; 85 } 86 87 /** 88 * @param distance 89 * the distance from the root object 90 */ 91 void setDistanceFromRoot(int distance) { 92 this.distanceFromRoot = distance; 93 } 94 95 /** 96 * @return the number of steps in a referral chain from the root object 97 */ 98 public int getDistanceFromRoot() { 99 return distanceFromRoot; 100 } 101 102 /** 103 * @param relevance 104 * how relevant this object is as a leak candidate 105 */ 106 void setLeakRelevance(double relevance) { 107 this.leakRelevance = relevance; 108 } 109 110 /** 111 * @return the relevance of this object for memory leak detection 112 */ 113 public double getLeakRelevance() { 114 return this.leakRelevance; 115 } 116 117 /** 118 * @param node 119 * a child to be added to this object 120 */ 121 void addChild(ReferenceTreeObject node) { 122 if (!children.contains(node)) { 123 children.add(node); 124 node.setParent(this); 125 } 126 } 127 128 /** 129 * @param item 130 * an item this object keeps alive 131 */ 132 void addItem(IItem item) { 133 items.add(item); 134 } 135 136 /** 137 * This method is used when it is necessary to get information about all objects this object 138 * keeps alive in the {@link ReferenceTreeModel}. E.g. the Mission Control GUI uses this when a 139 * user selects a row in the tree to show everything below it as well in the properties view. 140 * 141 * @return a set representing all {@link IItem} objects this object keeps alive, including 142 * itself 143 */ 144 public Set<IItem> getItems() { 145 return items; 146 } 147 148 /** 149 * @param root 150 * a GC root description 151 */ 152 void addRoot(IMCOldObjectGcRoot root) { 153 if (root != null) { 154 rootDescription = root.toString(); 155 } 156 } 157 158 /** 159 * @return the GC root description 160 */ 161 public String getRootDescription() { 162 return rootDescription; 163 } 164 165 @Override 166 public IQuantity getAddress() { 167 return object.getAddress(); 168 } 169 170 @Override 171 public IMCOldObjectArray getReferrerArray() { 172 return object.getReferrerArray(); 173 } 174 175 /** 176 * @return if this object is an array, gets information representing that array, {@code null} 177 * otherwise 178 */ 179 public IMCOldObjectArray getArray() { 180 if (getChildren().size() > 0) { 181 return getChildren().get(0).getReferrerArray(); 182 } 183 return null; 184 } 185 186 /** 187 * @return the children of this object 188 */ 189 public List<ReferenceTreeObject> getChildren() { 190 return children; 191 } 192 193 @Override 194 public IMCOldObjectField getReferrerField() { 195 return object.getReferrerField(); 196 } 197 198 /** 199 * @return if this object is a field, gets information representing that field, {@code null} 200 * otherwise 201 */ 202 public IMCOldObjectField getField() { 203 if (getChildren().size() > 0) { 204 return getChildren().get(0).getReferrerField(); 205 } 206 return null; 207 } 208 209 @Override 210 public String getDescription() { 211 return object.getDescription(); 212 } 213 214 /** 215 * @return the number of objects this object keeps alive 216 */ 217 public int getObjectsKeptAliveCount() { 218 return objectsKeptAliveCount; 219 } 220 221 /** 222 * This method returns an object that is keeping this object alive. 223 * 224 * @return the object linking to this object from the direction of the gc root 225 */ 226 public ReferenceTreeObject getParent() { 227 return parent; 228 } 229 230 @Override 231 public IMCOldObject getReferrer() { 232 return object.getReferrer(); 233 } 234 235 /** 236 * @return the timestamp this object was allocated 237 */ 238 public IQuantity getTimestamp() { 239 return timestamp; 240 } 241 242 @Override 243 public IMCType getType() { 244 return object.getType(); 245 } 246 247 @Override 248 public int hashCode() { 249 return object.hashCode(); 250 } 251 252 @Override 253 public boolean equals(Object o) { 254 if (o instanceof ReferenceTreeObject) { 255 ReferenceTreeObject that = (ReferenceTreeObject) o; 256 return that.getAddress().equals(this.getAddress()); 257 } 258 return false; 259 } 260 261 /** 262 * Increments the number of objects this object keeps alive. 263 */ 264 void incrementObjectsKeptAliveCount() { 265 objectsKeptAliveCount++; 266 } 267 268 /** 269 * @param parent 270 * the parent of this object 271 */ 272 public void setParent(ReferenceTreeObject parent) { 273 this.parent = parent; 274 } 275 276 @Override 277 public int getReferrerSkip() { 278 return object.getReferrerSkip(); 279 } 280 281 /** 282 * Returns a string representation of this object. 283 * 284 * @param displayFormatting 285 * an int describing how this object is to be represented, using bitwise masking of 286 * constants defined in {@link ReferenceTreeObject} 287 * @return a human readable string representation of this object 288 */ 289 public String toString(int displayFormatting) { 290 String text = getType().getTypeName(); 291 if ((displayFormatting & ReferenceTreeObject.FORMAT_PACKAGE) != 0) { 292 text = getType().getFullName(); 293 } 294 if (this.getChildren().size() > 0 && getField() != null) { 295 if ((displayFormatting & ReferenceTreeObject.FORMAT_FIELD) != 0) { 296 text = text + "." + getField().getName(); //$NON-NLS-1$ 297 } 298 if (getField().getModifier() != null) { 299 String modifiers = Modifier.toString(getField().getModifier()); 300 if ((displayFormatting & ReferenceTreeObject.FORMAT_STATIC_MODIFIER) != 0 301 && (displayFormatting & ReferenceTreeObject.FORMAT_OTHER_MODIFIERS) == 0) { 302 if (modifiers.contains("static")) { //$NON-NLS-1$ 303 text = "static " + text; //$NON-NLS-1$ 304 } 305 } else if ((displayFormatting & ReferenceTreeObject.FORMAT_OTHER_MODIFIERS) != 0 306 && (displayFormatting & ReferenceTreeObject.FORMAT_STATIC_MODIFIER) == 0) { 307 String nonStaticModifiers = modifiers.replaceAll("static ", ""); //$NON-NLS-1$ //$NON-NLS-2$ 308 if (!"".equals(nonStaticModifiers)) { //$NON-NLS-1$ 309 text = nonStaticModifiers + " " + text; //$NON-NLS-1$ 310 } 311 } else if ((displayFormatting & ReferenceTreeObject.FORMAT_STATIC_MODIFIER) != 0 312 && (displayFormatting & ReferenceTreeObject.FORMAT_OTHER_MODIFIERS) != 0) { 313 if (!"".equals(modifiers)) { //$NON-NLS-1$ 314 text = modifiers + " " + text; //$NON-NLS-1$ 315 } 316 } 317 } 318 } 319 if ((displayFormatting & ReferenceTreeObject.FORMAT_ARRAY_INFO) != 0 && this.getArray() != null) { 320 if (text.endsWith("[]")) { //$NON-NLS-1$ 321 text = text.substring(0, text.length() - 1) + getArray().getIndex() + "/" + getArray().getSize() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ 322 } else { 323 text = text + getArray().getIndex() + "/" + getArray().getSize(); //$NON-NLS-1$ 324 } 325 } 326 return text.trim(); 327 } 328 329 }