1 /*
2  * Copyright (c) 2000, 2020, 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 
ObjectHeap(TypeDataBase db)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 */
equal(Oop o1, Oop o2)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 
getOopSize()84   public long getOopSize()     { return oopSize;     }
getByteSize()85   public long getByteSize()    { return byteSize;    }
getCharSize()86   public long getCharSize()    { return charSize;    }
getBooleanSize()87   public long getBooleanSize() { return booleanSize; }
getIntSize()88   public long getIntSize()     { return intSize;     }
getShortSize()89   public long getShortSize()   { return shortSize;   }
getLongSize()90   public long getLongSize()    { return longSize;    }
getFloatSize()91   public long getFloatSize()   { return floatSize;   }
getDoubleSize()92   public long getDoubleSize()  { return doubleSize;  }
93 
94   /** an interface to filter objects while walking heap */
95   public static interface ObjectFilter {
canInclude(Oop obj)96     public boolean canInclude(Oop obj);
97   }
98 
99   /** The base heap iteration mechanism */
iterate(HeapVisitor visitor)100   public void iterate(HeapVisitor visitor) {
101     iterateLiveRegions(collectLiveRegions(), visitor, null);
102   }
103 
104   /** iterate objects satisfying a specified ObjectFilter */
iterate(HeapVisitor visitor, ObjectFilter of)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 */
iterateObjectsOfKlass(HeapVisitor visitor, final Klass k, boolean includeSubtypes)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) */
iterateObjectsOfKlass(HeapVisitor visitor, final Klass k)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 */
iterateRaw(RawHeapVisitor visitor)135   public void iterateRaw(RawHeapVisitor visitor) {
136     List<Address> 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 
isValidMethod(Address handle)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
newOop(OopHandle 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
print()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 
iterateExact(HeapVisitor visitor, final Klass k)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 
iterateSubtypes(HeapVisitor visitor, final Klass k)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 
iterateLiveRegions(List<Address> liveRegions, HeapVisitor visitor, ObjectFilter of)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 = (Address) liveRegions.get(i);
231       Address top    = (Address) 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 = (Address) liveRegions.get(i);
238       Address top    = (Address) 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 {
LiveRegionsCollector(List<Address> l)269     LiveRegionsCollector(List<Address> l) {
270       liveRegions = l;
271     }
272 
273     @Override
doLiveRegions(LiveRegionsProvider lrp)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.
collectLiveRegions()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 = (Address) liveRegions.get(i);
359           Address top    = (Address) liveRegions.get(i+1);
360           System.err.println(" " + bottom + " - " + top);
361       }
362     }
363 
364     return liveRegions;
365   }
366 
sortLiveRegions(List<Address> liveRegions)367   private void sortLiveRegions(List<Address> liveRegions) {
368     Collections.sort(liveRegions, 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 }
380