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 }