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