1 /*
  2  * Copyright (c) 2000, 2026, 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 FlatArrayKlass) return new FlatArray(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(handle.toString());
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<Address> 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 = liveRegions.get(i);
231       Address top    = liveRegions.get(i+1);
232       totalSize += top.minus(bottom);
233     }
234     visitor.prologue(totalSize);
235 
236     for (int i = 0; i < liveRegions.size(); i += 2) {
237       Address bottom = liveRegions.get(i);
238       Address top    = liveRegions.get(i+1);
239 
240       try {
241         // Traverses the space from bottom to top
242         OopHandle handle = bottom.addOffsetToAsOopHandle(0);
243 
244         while (handle.lessThan(top)) {
245           Oop obj = null;
246 
247           obj = newOop(handle);
248           if (obj == null) {
249               throw new UnknownOopException();
250           }
251           if (of == null || of.canInclude(obj)) {
252                   if (visitor.doObj(obj)) {
253                          // doObj() returns true to abort this loop.
254                           break;
255                   }
256           }
257 
258           handle = handle.addOffsetToAsOopHandle(obj.getObjectSize());
259         }
260       } catch (AddressException | UnknownOopException | WrongTypeException e) {
261         // This is okay at the top of these regions
262       }
263     }
264 
265     visitor.epilogue();
266   }
267 
268   private static class LiveRegionsCollector implements LiveRegionsClosure {
269     LiveRegionsCollector(List<Address> l) {
270       liveRegions = l;
271     }
272 
273     @Override
274     public void doLiveRegions(LiveRegionsProvider lrp) {
275       for (MemRegion reg : lrp.getLiveRegions()) {
276         Address top = reg.end();
277         Address bottom = reg.start();
278         if (Assert.ASSERTS_ENABLED) {
279           Assert.that(top != null, "top address in a live region should not be null");
280         }
281         if (Assert.ASSERTS_ENABLED) {
282           Assert.that(bottom != null, "bottom address in a live region should not be null");
283         }
284         liveRegions.add(top);
285         liveRegions.add(bottom);
286         if (DEBUG) {
287           System.err.println("Live region: " + lrp + ": " + bottom + ", " + top);
288       }
289     }
290   }
291 
292      private List<Address> liveRegions;
293   }
294 
295   // Returns a List<Address> where the addresses come in pairs. These
296   // designate the live regions of the heap.
297   private List<Address> collectLiveRegions() {
298     // We want to iterate through all live portions of the heap, but
299     // do not want to abort the heap traversal prematurely if we find
300     // a problem (like an allocated but uninitialized object at the
301     // top of a generation). To do this we enumerate all generations'
302     // bottom and top regions, and factor in TLABs if necessary.
303 
304     // Addresses come in pairs.
305     List<Address> liveRegions = new ArrayList<>();
306     LiveRegionsCollector lrc = new LiveRegionsCollector(liveRegions);
307 
308     CollectedHeap heap = VM.getVM().getUniverse().heap();
309     heap.liveRegionsIterate(lrc);
310 
311     // If UseTLAB is enabled, snip out regions associated with TLABs'
312     // dead regions. Note that TLABs can be present in any generation.
313 
314     // FIXME: consider adding fewer boundaries to live region list.
315     // Theoretically only need to stop at TLAB's top and resume at its
316     // end.
317 
318     if (VM.getVM().getUseTLAB()) {
319       Threads threads = VM.getVM().getThreads();
320       for (int i = 0; i < threads.getNumberOfThreads(); i++) {
321         JavaThread thread = threads.getJavaThreadAt(i);
322         ThreadLocalAllocBuffer tlab = thread.tlab();
323         if (tlab.start() != null) {
324           if ((tlab.top() == null) || (tlab.end() == null)) {
325             System.err.print("Warning: skipping invalid TLAB for thread ");
326             thread.printThreadIDOn(System.err);
327             System.err.println();
328           } else {
329             if (DEBUG) {
330               System.err.print("TLAB for " + thread.getThreadName() + ", #");
331               thread.printThreadIDOn(System.err);
332               System.err.print(": ");
333               tlab.printOn(System.err);
334             }
335             // Go from:
336             //  - below start() to start()
337             //  - start() to top()
338             //  - end() and above
339             liveRegions.add(tlab.start());
340             liveRegions.add(tlab.start());
341             liveRegions.add(tlab.top());
342             liveRegions.add(tlab.hardEnd());
343           }
344         }
345       }
346     }
347 
348     // Now sort live regions
349     sortLiveRegions(liveRegions);
350 
351     if (Assert.ASSERTS_ENABLED) {
352       Assert.that(liveRegions.size() % 2 == 0, "Must have even number of region boundaries");
353     }
354 
355     if (DEBUG) {
356       System.err.println("liveRegions:");
357       for (int i = 0; i < liveRegions.size(); i += 2) {
358           Address bottom = liveRegions.get(i);
359           Address top    = liveRegions.get(i+1);
360           System.err.println(" " + bottom + " - " + top);
361       }
362     }
363 
364     return liveRegions;
365   }
366 
367   private void sortLiveRegions(List<Address> liveRegions) {
368     liveRegions.sort(new Comparator<Address>() {
369         public int compare(Address a1, Address a2) {
370           if (AddressOps.lt(a1, a2)) {
371             return -1;
372           } else if (AddressOps.gt(a1, a2)) {
373             return 1;
374           }
375           return 0;
376         }
377       });
378   }
379 }