1 /*
   2  * Copyright (c) 2000, 2019, 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.shared.*;
  36 import sun.jvm.hotspot.gc.epsilon.*;
  37 import sun.jvm.hotspot.gc.g1.*;
  38 import sun.jvm.hotspot.gc.shenandoah.*;
  39 import sun.jvm.hotspot.gc.parallel.*;
  40 import sun.jvm.hotspot.gc.z.*;
  41 import sun.jvm.hotspot.memory.*;
  42 import sun.jvm.hotspot.runtime.*;
  43 import sun.jvm.hotspot.types.*;
  44 import sun.jvm.hotspot.utilities.*;
  45 
  46 public class ObjectHeap {
  47 
  48   private static final boolean DEBUG;
  49 
  50   static {
  51     DEBUG = System.getProperty("sun.jvm.hotspot.oops.ObjectHeap.DEBUG") != null;
  52   }
  53 
  54   public ObjectHeap(TypeDataBase db) throws WrongTypeException {
  55     // Get commonly used sizes of basic types
  56     oopSize     = VM.getVM().getOopSize();
  57     byteSize    = db.getJByteType().getSize();
  58     charSize    = db.getJCharType().getSize();
  59     booleanSize = db.getJBooleanType().getSize();
  60     intSize     = db.getJIntType().getSize();
  61     shortSize   = db.getJShortType().getSize();
  62     longSize    = db.getJLongType().getSize();
  63     floatSize   = db.getJFloatType().getSize();
  64     doubleSize  = db.getJDoubleType().getSize();
  65   }
  66 
  67   /** Comparison operation for oops, either or both of which may be null */
  68   public boolean equal(Oop o1, Oop o2) {
  69     if (o1 != null) return o1.equals(o2);
  70     return (o2 == null);
  71   }
  72 
  73   // Cached sizes of basic types
  74   private long oopSize;
  75   private long byteSize;
  76   private long charSize;
  77   private long booleanSize;
  78   private long intSize;
  79   private long shortSize;
  80   private long longSize;
  81   private long floatSize;
  82   private long doubleSize;
  83 
  84   public long getOopSize()     { return oopSize;     }
  85   public long getByteSize()    { return byteSize;    }
  86   public long getCharSize()    { return charSize;    }
  87   public long getBooleanSize() { return booleanSize; }
  88   public long getIntSize()     { return intSize;     }
  89   public long getShortSize()   { return shortSize;   }
  90   public long getLongSize()    { return longSize;    }
  91   public long getFloatSize()   { return floatSize;   }
  92   public long getDoubleSize()  { return doubleSize;  }
  93 
  94   /** an interface to filter objects while walking heap */
  95   public static interface ObjectFilter {
  96     public boolean canInclude(Oop obj);
  97   }
  98 
  99   /** The base heap iteration mechanism */
 100   public void iterate(HeapVisitor visitor) {
 101     iterateLiveRegions(collectLiveRegions(), visitor, null);
 102   }
 103 
 104   /** iterate objects satisfying a specified ObjectFilter */
 105   public void iterate(HeapVisitor visitor, ObjectFilter of) {
 106     iterateLiveRegions(collectLiveRegions(), visitor, of);
 107   }
 108 
 109   /** iterate objects of given Klass. param 'includeSubtypes' tells whether to
 110    *  include objects of subtypes or not */
 111   public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k, boolean includeSubtypes) {
 112     if (includeSubtypes) {
 113       if (k.isFinal()) {
 114         // do the simpler "exact" klass loop
 115         iterateExact(visitor, k);
 116       } else {
 117         iterateSubtypes(visitor, k);
 118       }
 119     } else {
 120       // there can no object of abstract classes and interfaces
 121       if (!k.isAbstract() && !k.isInterface()) {
 122         iterateExact(visitor, k);
 123       }
 124     }
 125   }
 126 
 127   /** iterate objects of given Klass (objects of subtypes included) */
 128   public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k) {
 129     iterateObjectsOfKlass(visitor, k, true);
 130   }
 131 
 132   /** This routine can be used to iterate through the heap at an
 133       extremely low level (stepping word-by-word) to provide the
 134       ability to do very low-level debugging */
 135   public void iterateRaw(RawHeapVisitor visitor) {
 136     List liveRegions = collectLiveRegions();
 137 
 138     // Summarize size
 139     long totalSize = 0;
 140     for (int i = 0; i < liveRegions.size(); i += 2) {
 141       Address bottom = (Address) liveRegions.get(i);
 142       Address top    = (Address) liveRegions.get(i+1);
 143       totalSize += top.minus(bottom);
 144     }
 145     visitor.prologue(totalSize);
 146 
 147     for (int i = 0; i < liveRegions.size(); i += 2) {
 148       Address bottom = (Address) liveRegions.get(i);
 149       Address top    = (Address) liveRegions.get(i+1);
 150 
 151       // Traverses the space from bottom to top
 152       while (bottom.lessThan(top)) {
 153         visitor.visitAddress(bottom);
 154         bottom = bottom.addOffsetTo(VM.getVM().getAddressSize());
 155       }
 156     }
 157 
 158     visitor.epilogue();
 159   }
 160 
 161   public boolean isValidMethod(Address handle) {
 162     try {
 163       Method m = (Method)Metadata.instantiateWrapperFor(handle);
 164       return true;
 165     } catch (Exception e) {
 166       return false;
 167   }
 168   }
 169 
 170   // Creates an instance from the Oop hierarchy based based on the handle
 171   public Oop newOop(OopHandle handle) {
 172     // The only known way to detect the right type of an oop is
 173     // traversing the class chain until a well-known klass is recognized.
 174     // A more direct solution would require the klasses to expose
 175     // the C++ vtbl structure.
 176 
 177     // Handle the null reference
 178     if (handle == null) return null;
 179 
 180     // Then check if obj.klass() is one of the root objects
 181     Klass klass = Oop.getKlassForOopHandle(handle);
 182     if (klass != null) {
 183       if (klass instanceof TypeArrayKlass) return new TypeArray(handle, this);
 184       if (klass instanceof ObjArrayKlass) return new ObjArray(handle, this);
 185       if (klass instanceof InstanceKlass) return new Instance(handle, this);
 186     }
 187 
 188     if (DEBUG) {
 189       System.err.println("Unknown oop at " + handle);
 190       System.err.println("Oop's klass is " + klass);
 191     }
 192 
 193     throw new UnknownOopException();
 194   }
 195 
 196   // Print all objects in the object heap
 197   public void print() {
 198     HeapPrinter printer = new HeapPrinter(System.out);
 199     iterate(printer);
 200   }
 201 
 202   //---------------------------------------------------------------------------
 203   // Internals only below this point
 204   //
 205 
 206   private void iterateExact(HeapVisitor visitor, final Klass k) {
 207     iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() {
 208           public boolean canInclude(Oop obj) {
 209             Klass tk = obj.getKlass();
 210             // null Klass is seen sometimes!
 211             return (tk != null && tk.equals(k));
 212           }
 213         });
 214   }
 215 
 216   private void iterateSubtypes(HeapVisitor visitor, final Klass k) {
 217     iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() {
 218           public boolean canInclude(Oop obj) {
 219             Klass tk = obj.getKlass();
 220             // null Klass is seen sometimes!
 221             return (tk != null && tk.isSubtypeOf(k));
 222           }
 223         });
 224   }
 225 
 226   private void iterateLiveRegions(List liveRegions, HeapVisitor visitor, ObjectFilter of) {
 227     // Summarize size
 228     long totalSize = 0;
 229     for (int i = 0; i < liveRegions.size(); i += 2) {
 230       Address bottom = (Address) liveRegions.get(i);
 231       Address top    = (Address) liveRegions.get(i+1);
 232       totalSize += top.minus(bottom);
 233     }
 234     visitor.prologue(totalSize);
 235 
 236     CollectedHeap heap = VM.getVM().getUniverse().heap();
 237 
 238     if (heap instanceof GenCollectedHeap) {
 239       GenCollectedHeap genHeap = (GenCollectedHeap) heap;
 240       Generation genOld = genHeap.getGen(1);
 241     }
 242 
 243     for (int i = 0; i < liveRegions.size(); i += 2) {
 244       Address bottom = (Address) liveRegions.get(i);
 245       Address top    = (Address) liveRegions.get(i+1);
 246 
 247       try {
 248         // Traverses the space from bottom to top
 249         OopHandle handle = bottom.addOffsetToAsOopHandle(0);
 250 
 251         while (handle.lessThan(top)) {
 252           Oop obj = null;
 253 
 254           try {
 255             obj = newOop(handle);
 256           } catch (UnknownOopException exp) {
 257             if (DEBUG) {
 258               throw new RuntimeException(" UnknownOopException  " + exp);
 259             }
 260           }
 261           if (obj == null) {
 262               throw new UnknownOopException();
 263           }
 264           if (of == null || of.canInclude(obj)) {
 265                   if (visitor.doObj(obj)) {
 266                          // doObj() returns true to abort this loop.
 267                           break;
 268                   }
 269           }
 270 
 271           handle = handle.addOffsetToAsOopHandle(obj.getObjectSize());
 272         }
 273       }
 274       catch (AddressException e) {
 275         // This is okay at the top of these regions
 276           }
 277       catch (UnknownOopException e) {
 278         // This is okay at the top of these regions
 279       }
 280     }
 281 
 282     visitor.epilogue();
 283   }
 284 
 285   private static class LiveRegionsCollector implements LiveRegionsClosure {
 286     LiveRegionsCollector(List<Address> l) {
 287       liveRegions = l;
 288     }
 289 
 290     @Override
 291     public void doLiveRegions(LiveRegionsProvider lrp) {
 292       for (MemRegion reg : lrp.getLiveRegions()) {
 293         Address top = reg.end();
 294         Address bottom = reg.start();
 295         if (Assert.ASSERTS_ENABLED) {
 296           Assert.that(top != null, "top address in a live region should not be null");
 297         }
 298         if (Assert.ASSERTS_ENABLED) {
 299           Assert.that(bottom != null, "bottom address in a live region should not be null");
 300         }
 301         liveRegions.add(top);
 302         liveRegions.add(bottom);
 303         if (DEBUG) {
 304           System.err.println("Live region: " + lrp + ": " + bottom + ", " + top);
 305       }
 306     }
 307   }
 308 
 309      private List<Address> liveRegions;
 310   }
 311 
 312   // Returns a List<Address> where the addresses come in pairs. These
 313   // designate the live regions of the heap.
 314   private List<Address> collectLiveRegions() {
 315     // We want to iterate through all live portions of the heap, but
 316     // do not want to abort the heap traversal prematurely if we find
 317     // a problem (like an allocated but uninitialized object at the
 318     // top of a generation). To do this we enumerate all generations'
 319     // bottom and top regions, and factor in TLABs if necessary.
 320 
 321     // Addresses come in pairs.
 322     List<Address> liveRegions = new ArrayList<>();
 323     LiveRegionsCollector lrc = new LiveRegionsCollector(liveRegions);
 324 
 325     CollectedHeap heap = VM.getVM().getUniverse().heap();
 326     heap.liveRegionsIterate(lrc);
 327 
 328     // If UseTLAB is enabled, snip out regions associated with TLABs'
 329     // dead regions. Note that TLABs can be present in any generation.
 330 
 331     // FIXME: consider adding fewer boundaries to live region list.
 332     // Theoretically only need to stop at TLAB's top and resume at its
 333     // end.
 334 
 335     if (VM.getVM().getUseTLAB()) {
 336       Threads threads = VM.getVM().getThreads();
 337       for (int i = 0; i < threads.getNumberOfThreads(); i++) {
 338         JavaThread thread = threads.getJavaThreadAt(i);
 339         ThreadLocalAllocBuffer tlab = thread.tlab();
 340         if (tlab.start() != null) {
 341           if ((tlab.top() == null) || (tlab.end() == null)) {
 342             System.err.print("Warning: skipping invalid TLAB for thread ");
 343             thread.printThreadIDOn(System.err);
 344             System.err.println();
 345           } else {
 346             if (DEBUG) {
 347               System.err.print("TLAB for " + thread.getThreadName() + ", #");
 348               thread.printThreadIDOn(System.err);
 349               System.err.print(": ");
 350               tlab.printOn(System.err);
 351             }
 352             // Go from:
 353             //  - below start() to start()
 354             //  - start() to top()
 355             //  - end() and above
 356             liveRegions.add(tlab.start());
 357             liveRegions.add(tlab.start());
 358             liveRegions.add(tlab.top());
 359             liveRegions.add(tlab.hardEnd());
 360           }
 361         }
 362       }
 363     }
 364 
 365     // Now sort live regions
 366     sortLiveRegions(liveRegions);
 367 
 368     if (Assert.ASSERTS_ENABLED) {
 369       Assert.that(liveRegions.size() % 2 == 0, "Must have even number of region boundaries");
 370     }
 371 
 372     if (DEBUG) {
 373       System.err.println("liveRegions:");
 374       for (int i = 0; i < liveRegions.size(); i += 2) {
 375           Address bottom = (Address) liveRegions.get(i);
 376           Address top    = (Address) liveRegions.get(i+1);
 377           System.err.println(" " + bottom + " - " + top);
 378       }
 379     }
 380 
 381     return liveRegions;
 382   }
 383 
 384   private void sortLiveRegions(List<Address> liveRegions) {
 385     Collections.sort(liveRegions, new Comparator<Address>() {
 386         public int compare(Address a1, Address a2) {
 387           if (AddressOps.lt(a1, a2)) {
 388             return -1;
 389           } else if (AddressOps.gt(a1, a2)) {
 390             return 1;
 391           }
 392           return 0;
 393         }
 394       });
 395   }
 396 }