1 /* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 // 26 // The ObjectHeap is an abstraction over all generations in the VM 27 // It gives access to all present objects and classes. 28 // 29 30 package sun.jvm.hotspot.oops; 31 32 import java.util.*; 33 34 import sun.jvm.hotspot.debugger.*; 35 import sun.jvm.hotspot.gc_interface.*; 36 import sun.jvm.hotspot.gc_implementation.g1.*; 37 import sun.jvm.hotspot.gc_implementation.shenandoah.*; 38 import sun.jvm.hotspot.gc_implementation.parallelScavenge.*; 39 import sun.jvm.hotspot.memory.*; 40 import sun.jvm.hotspot.runtime.*; 41 import sun.jvm.hotspot.types.*; 42 import sun.jvm.hotspot.utilities.*; 43 44 public class ObjectHeap { 45 46 private static final boolean DEBUG; 47 48 static { 49 DEBUG = System.getProperty("sun.jvm.hotspot.oops.ObjectHeap.DEBUG") != null; 50 } 51 52 private Address boolArrayKlassHandle; 53 private Address byteArrayKlassHandle; 54 private Address charArrayKlassHandle; 55 private Address intArrayKlassHandle; 56 private Address shortArrayKlassHandle; 57 private Address longArrayKlassHandle; 58 private Address singleArrayKlassHandle; 59 private Address doubleArrayKlassHandle; 60 61 private TypeArrayKlass boolArrayKlassObj; 62 private TypeArrayKlass byteArrayKlassObj; 63 private TypeArrayKlass charArrayKlassObj; 64 private TypeArrayKlass intArrayKlassObj; 65 private TypeArrayKlass shortArrayKlassObj; 66 private TypeArrayKlass longArrayKlassObj; 67 private TypeArrayKlass singleArrayKlassObj; 68 private TypeArrayKlass doubleArrayKlassObj; 69 70 public void initialize(TypeDataBase db) throws WrongTypeException { 71 // Lookup the roots in the object hierarchy. 72 Type universeType = db.lookupType("Universe"); 73 74 boolArrayKlassHandle = universeType.getAddressField("_boolArrayKlassObj").getValue(); 75 boolArrayKlassObj = new TypeArrayKlass(boolArrayKlassHandle); 76 77 byteArrayKlassHandle = universeType.getAddressField("_byteArrayKlassObj").getValue(); 78 byteArrayKlassObj = new TypeArrayKlass(byteArrayKlassHandle); 79 80 charArrayKlassHandle = universeType.getAddressField("_charArrayKlassObj").getValue(); 81 charArrayKlassObj = new TypeArrayKlass(charArrayKlassHandle); 82 83 intArrayKlassHandle = universeType.getAddressField("_intArrayKlassObj").getValue(); 84 intArrayKlassObj = new TypeArrayKlass(intArrayKlassHandle); 85 86 shortArrayKlassHandle = universeType.getAddressField("_shortArrayKlassObj").getValue(); 87 shortArrayKlassObj = new TypeArrayKlass(shortArrayKlassHandle); 88 89 longArrayKlassHandle = universeType.getAddressField("_longArrayKlassObj").getValue(); 90 longArrayKlassObj = new TypeArrayKlass(longArrayKlassHandle); 91 92 singleArrayKlassHandle = universeType.getAddressField("_singleArrayKlassObj").getValue(); 93 singleArrayKlassObj = new TypeArrayKlass(singleArrayKlassHandle); 94 95 doubleArrayKlassHandle = universeType.getAddressField("_doubleArrayKlassObj").getValue(); 96 doubleArrayKlassObj = new TypeArrayKlass(doubleArrayKlassHandle); 97 } 98 99 public ObjectHeap(TypeDataBase db) throws WrongTypeException { 100 // Get commonly used sizes of basic types 101 oopSize = VM.getVM().getOopSize(); 102 byteSize = db.getJByteType().getSize(); 103 charSize = db.getJCharType().getSize(); 104 booleanSize = db.getJBooleanType().getSize(); 105 intSize = db.getJIntType().getSize(); 106 shortSize = db.getJShortType().getSize(); 107 longSize = db.getJLongType().getSize(); 108 floatSize = db.getJFloatType().getSize(); 109 doubleSize = db.getJDoubleType().getSize(); 110 111 initialize(db); 112 } 113 114 /** Comparison operation for oops, either or both of which may be null */ 115 public boolean equal(Oop o1, Oop o2) { 116 if (o1 != null) return o1.equals(o2); 117 return (o2 == null); 118 } 119 120 // Cached sizes of basic types 121 private long oopSize; 122 private long byteSize; 123 private long charSize; 124 private long booleanSize; 125 private long intSize; 126 private long shortSize; 127 private long longSize; 128 private long floatSize; 129 private long doubleSize; 130 131 public long getOopSize() { return oopSize; } 132 public long getByteSize() { return byteSize; } 133 public long getCharSize() { return charSize; } 134 public long getBooleanSize() { return booleanSize; } 135 public long getIntSize() { return intSize; } 136 public long getShortSize() { return shortSize; } 137 public long getLongSize() { return longSize; } 138 public long getFloatSize() { return floatSize; } 139 public long getDoubleSize() { return doubleSize; } 140 141 // Accessors for well-known system classes (from Universe) 142 public TypeArrayKlass getBoolArrayKlassObj() { return boolArrayKlassObj; } 143 public TypeArrayKlass getByteArrayKlassObj() { return byteArrayKlassObj; } 144 public TypeArrayKlass getCharArrayKlassObj() { return charArrayKlassObj; } 145 public TypeArrayKlass getIntArrayKlassObj() { return intArrayKlassObj; } 146 public TypeArrayKlass getShortArrayKlassObj() { return shortArrayKlassObj; } 147 public TypeArrayKlass getLongArrayKlassObj() { return longArrayKlassObj; } 148 public TypeArrayKlass getSingleArrayKlassObj() { return singleArrayKlassObj; } 149 public TypeArrayKlass getDoubleArrayKlassObj() { return doubleArrayKlassObj; } 150 151 /** Takes a BasicType and returns the corresponding primitive array 152 klass */ 153 public Klass typeArrayKlassObj(int t) { 154 if (t == BasicType.getTBoolean()) return getBoolArrayKlassObj(); 155 if (t == BasicType.getTChar()) return getCharArrayKlassObj(); 156 if (t == BasicType.getTFloat()) return getSingleArrayKlassObj(); 157 if (t == BasicType.getTDouble()) return getDoubleArrayKlassObj(); 158 if (t == BasicType.getTByte()) return getByteArrayKlassObj(); 159 if (t == BasicType.getTShort()) return getShortArrayKlassObj(); 160 if (t == BasicType.getTInt()) return getIntArrayKlassObj(); 161 if (t == BasicType.getTLong()) return getLongArrayKlassObj(); 162 throw new RuntimeException("Illegal basic type " + t); 163 } 164 165 /** an interface to filter objects while walking heap */ 166 public static interface ObjectFilter { 167 public boolean canInclude(Oop obj); 168 } 169 170 /** The base heap iteration mechanism */ 171 public void iterate(HeapVisitor visitor) { 172 iterateLiveRegions(collectLiveRegions(), visitor, null); 173 } 174 175 /** iterate objects satisfying a specified ObjectFilter */ 176 public void iterate(HeapVisitor visitor, ObjectFilter of) { 177 iterateLiveRegions(collectLiveRegions(), visitor, of); 178 } 179 180 /** iterate objects of given Klass. param 'includeSubtypes' tells whether to 181 * include objects of subtypes or not */ 182 public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k, boolean includeSubtypes) { 183 if (includeSubtypes) { 184 if (k.isFinal()) { 185 // do the simpler "exact" klass loop 186 iterateExact(visitor, k); 187 } else { 188 iterateSubtypes(visitor, k); 189 } 190 } else { 191 // there can no object of abstract classes and interfaces 192 if (!k.isAbstract() && !k.isInterface()) { 193 iterateExact(visitor, k); 194 } 195 } 196 } 197 198 /** iterate objects of given Klass (objects of subtypes included) */ 199 public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k) { 200 iterateObjectsOfKlass(visitor, k, true); 201 } 202 203 /** This routine can be used to iterate through the heap at an 204 extremely low level (stepping word-by-word) to provide the 205 ability to do very low-level debugging */ 206 public void iterateRaw(RawHeapVisitor visitor) { 207 List liveRegions = collectLiveRegions(); 208 209 // Summarize size 210 long totalSize = 0; 211 for (int i = 0; i < liveRegions.size(); i += 2) { 212 Address bottom = (Address) liveRegions.get(i); 213 Address top = (Address) liveRegions.get(i+1); 214 totalSize += top.minus(bottom); 215 } 216 visitor.prologue(totalSize); 217 218 for (int i = 0; i < liveRegions.size(); i += 2) { 219 Address bottom = (Address) liveRegions.get(i); 220 Address top = (Address) liveRegions.get(i+1); 221 222 // Traverses the space from bottom to top 223 while (bottom.lessThan(top)) { 224 visitor.visitAddress(bottom); 225 bottom = bottom.addOffsetTo(VM.getVM().getAddressSize()); 226 } 227 } 228 229 visitor.epilogue(); 230 } 231 232 public boolean isValidMethod(Address handle) { 233 try { 234 Method m = (Method)Metadata.instantiateWrapperFor(handle); 235 return true; 236 } catch (Exception e) { 237 return false; 238 } 239 } 240 241 // Creates an instance from the Oop hierarchy based based on the handle 242 public Oop newOop(OopHandle handle) { 243 // The only known way to detect the right type of an oop is 244 // traversing the class chain until a well-known klass is recognized. 245 // A more direct solution would require the klasses to expose 246 // the C++ vtbl structure. 247 248 // Handle the null reference 249 if (handle == null) return null; 250 251 // Then check if obj.klass() is one of the root objects 252 Klass klass = Oop.getKlassForOopHandle(handle); 253 if (klass != null) { 254 if (klass instanceof TypeArrayKlass) return new TypeArray(handle, this); 255 if (klass instanceof ObjArrayKlass) return new ObjArray(handle, this); 256 if (klass instanceof InstanceKlass) return new Instance(handle, this); 257 } 258 259 if (DEBUG) { 260 System.err.println("Unknown oop at " + handle); 261 System.err.println("Oop's klass is " + klass); 262 } 263 264 throw new UnknownOopException(); 265 } 266 267 // Print all objects in the object heap 268 public void print() { 269 HeapPrinter printer = new HeapPrinter(System.out); 270 iterate(printer); 271 } 272 273 //--------------------------------------------------------------------------- 274 // Internals only below this point 275 // 276 277 private void iterateExact(HeapVisitor visitor, final Klass k) { 278 iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() { 279 public boolean canInclude(Oop obj) { 280 Klass tk = obj.getKlass(); 281 // null Klass is seen sometimes! 282 return (tk != null && tk.equals(k)); 283 } 284 }); 285 } 286 287 private void iterateSubtypes(HeapVisitor visitor, final Klass k) { 288 iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() { 289 public boolean canInclude(Oop obj) { 290 Klass tk = obj.getKlass(); 291 // null Klass is seen sometimes! 292 return (tk != null && tk.isSubtypeOf(k)); 293 } 294 }); 295 } 296 297 private void iterateLiveRegions(List liveRegions, HeapVisitor visitor, ObjectFilter of) { 298 // Summarize size 299 long totalSize = 0; 300 for (int i = 0; i < liveRegions.size(); i += 2) { 301 Address bottom = (Address) liveRegions.get(i); 302 Address top = (Address) liveRegions.get(i+1); 303 totalSize += top.minus(bottom); 304 } 305 visitor.prologue(totalSize); 306 307 CompactibleFreeListSpace cmsSpaceOld = null; 308 CollectedHeap heap = VM.getVM().getUniverse().heap(); 309 310 if (heap instanceof GenCollectedHeap) { 311 GenCollectedHeap genHeap = (GenCollectedHeap) heap; 312 Generation genOld = genHeap.getGen(1); 313 if (genOld instanceof ConcurrentMarkSweepGeneration) { 314 ConcurrentMarkSweepGeneration concGen = (ConcurrentMarkSweepGeneration)genOld; 315 cmsSpaceOld = concGen.cmsSpace(); 316 } 317 } 318 319 // Offset of the first oop from region's bottom 320 int oop_offset = heap.oop_region_offset_words() * VM.getVM().getHeapWordSize(); 321 int oop_extra_size = heap.oop_extra_words() * VM.getVM().getHeapWordSize(); 322 323 for (int i = 0; i < liveRegions.size(); i += 2) { 324 Address bottom = (Address) liveRegions.get(i); 325 Address top = (Address) liveRegions.get(i+1); 326 327 try { 328 // Traverses the space from bottom to top 329 OopHandle handle = bottom.addOffsetToAsOopHandle(oop_offset); 330 331 while (handle.lessThan(top)) { 332 Oop obj = null; 333 334 try { 335 obj = newOop(handle); 336 } catch (UnknownOopException exp) { 337 if (DEBUG) { 338 throw new RuntimeException(" UnknownOopException " + exp); 339 } 340 } 341 if (obj == null) { 342 //Find the object size using Printezis bits and skip over 343 long size = 0; 344 345 if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle) ){ 346 size = cmsSpaceOld.collector().blockSizeUsingPrintezisBits(handle); 347 } 348 349 if (size <= 0) { 350 //Either Printezis bits not set or handle is not in cms space. 351 throw new UnknownOopException(); 352 } 353 354 handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(size)); 355 continue; 356 } 357 if (of == null || of.canInclude(obj)) { 358 if (visitor.doObj(obj)) { 359 // doObj() returns true to abort this loop. 360 break; 361 } 362 } 363 if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle)) { 364 handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(obj.getObjectSize()) ); 365 } else { 366 handle = handle.addOffsetToAsOopHandle(obj.getObjectSize() + oop_extra_size); 367 } 368 } 369 } 370 catch (AddressException e) { 371 // This is okay at the top of these regions 372 } 373 catch (UnknownOopException e) { 374 // This is okay at the top of these regions 375 } 376 } 377 378 visitor.epilogue(); 379 } 380 381 private void addLiveRegions(String name, List input, List output) { 382 for (Iterator itr = input.iterator(); itr.hasNext();) { 383 MemRegion reg = (MemRegion) itr.next(); 384 Address top = reg.end(); 385 Address bottom = reg.start(); 386 if (Assert.ASSERTS_ENABLED) { 387 Assert.that(top != null, "top address in a live region should not be null"); 388 } 389 if (Assert.ASSERTS_ENABLED) { 390 Assert.that(bottom != null, "bottom address in a live region should not be null"); 391 } 392 output.add(top); 393 output.add(bottom); 394 if (DEBUG) { 395 System.err.println("Live region: " + name + ": " + bottom + ", " + top); 396 } 397 } 398 } 399 400 private class LiveRegionsCollector implements SpaceClosure { 401 LiveRegionsCollector(List l) { 402 liveRegions = l; 403 } 404 405 public void doSpace(Space s) { 406 addLiveRegions(s.toString(), s.getLiveRegions(), liveRegions); 407 } 408 private List liveRegions; 409 } 410 411 // Returns a List<Address> where the addresses come in pairs. These 412 // designate the live regions of the heap. 413 private List collectLiveRegions() { 414 // We want to iterate through all live portions of the heap, but 415 // do not want to abort the heap traversal prematurely if we find 416 // a problem (like an allocated but uninitialized object at the 417 // top of a generation). To do this we enumerate all generations' 418 // bottom and top regions, and factor in TLABs if necessary. 419 420 // List<Address>. Addresses come in pairs. 421 List liveRegions = new ArrayList(); 422 LiveRegionsCollector lrc = new LiveRegionsCollector(liveRegions); 423 424 CollectedHeap heap = VM.getVM().getUniverse().heap(); 425 426 if (heap instanceof GenCollectedHeap) { 427 GenCollectedHeap genHeap = (GenCollectedHeap) heap; 428 // Run through all generations, obtaining bottom-top pairs. 429 for (int i = 0; i < genHeap.nGens(); i++) { 430 Generation gen = genHeap.getGen(i); 431 gen.spaceIterate(lrc, true); 432 } 433 } else if (heap instanceof ParallelScavengeHeap) { 434 ParallelScavengeHeap psh = (ParallelScavengeHeap) heap; 435 PSYoungGen youngGen = psh.youngGen(); 436 // Add eden space 437 addLiveRegions("eden", youngGen.edenSpace().getLiveRegions(), liveRegions); 438 // Add from-space but not to-space 439 addLiveRegions("from", youngGen.fromSpace().getLiveRegions(), liveRegions); 440 PSOldGen oldGen = psh.oldGen(); 441 addLiveRegions("old ", oldGen.objectSpace().getLiveRegions(), liveRegions); 442 } else if (heap instanceof G1CollectedHeap) { 443 G1CollectedHeap g1h = (G1CollectedHeap) heap; 444 g1h.heapRegionIterate(lrc); 445 } else if (heap instanceof ShenandoahHeap) { 446 ShenandoahHeap sh = (ShenandoahHeap) heap; 447 sh.heapRegionIterate(lrc); 448 } else { 449 if (Assert.ASSERTS_ENABLED) { 450 Assert.that(false, "Expecting GenCollectedHeap, G1CollectedHeap, " + 451 "SheandoahHeap or ParallelScavengeHeap, but got " + 452 heap.getClass().getName()); 453 } 454 } 455 456 // If UseTLAB is enabled, snip out regions associated with TLABs' 457 // dead regions. Note that TLABs can be present in any generation. 458 459 // FIXME: consider adding fewer boundaries to live region list. 460 // Theoretically only need to stop at TLAB's top and resume at its 461 // end. 462 463 if (VM.getVM().getUseTLAB()) { 464 for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) { 465 ThreadLocalAllocBuffer tlab = thread.tlab(); 466 if (tlab.start() != null) { 467 if ((tlab.top() == null) || (tlab.end() == null)) { 468 System.err.print("Warning: skipping invalid TLAB for thread "); 469 thread.printThreadIDOn(System.err); 470 System.err.println(); 471 } else { 472 if (DEBUG) { 473 System.err.print("TLAB for " + thread.getThreadName() + ", #"); 474 thread.printThreadIDOn(System.err); 475 System.err.print(": "); 476 tlab.printOn(System.err); 477 } 478 // Go from: 479 // - below start() to start() 480 // - start() to top() 481 // - end() and above 482 liveRegions.add(tlab.start()); 483 liveRegions.add(tlab.start()); 484 liveRegions.add(tlab.top()); 485 liveRegions.add(tlab.hardEnd()); 486 } 487 } 488 } 489 } 490 491 // Now sort live regions 492 sortLiveRegions(liveRegions); 493 494 if (Assert.ASSERTS_ENABLED) { 495 Assert.that(liveRegions.size() % 2 == 0, "Must have even number of region boundaries"); 496 } 497 498 if (DEBUG) { 499 System.err.println("liveRegions:"); 500 for (int i = 0; i < liveRegions.size(); i += 2) { 501 Address bottom = (Address) liveRegions.get(i); 502 Address top = (Address) liveRegions.get(i+1); 503 System.err.println(" " + bottom + " - " + top); 504 } 505 } 506 507 return liveRegions; 508 } 509 510 private void sortLiveRegions(List liveRegions) { 511 Collections.sort(liveRegions, new Comparator() { 512 public int compare(Object o1, Object o2) { 513 Address a1 = (Address) o1; 514 Address a2 = (Address) o2; 515 if (AddressOps.lt(a1, a2)) { 516 return -1; 517 } else if (AddressOps.gt(a1, a2)) { 518 return 1; 519 } 520 return 0; 521 } 522 }); 523 } 524 }