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 }