|
1 /* |
|
2 * Copyright (c) 2000, 2015, 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.cms.*; |
|
36 import sun.jvm.hotspot.gc.shared.*; |
|
37 import sun.jvm.hotspot.gc.g1.*; |
|
38 import sun.jvm.hotspot.gc.parallel.*; |
|
39 import sun.jvm.hotspot.memory.*; |
|
40 import sun.jvm.hotspot.runtime.*; |
|
41 import sun.jvm.hotspot.types.*; |
|
42 import sun.jvm.hotspot.utilities.*; |
|
43 |
|
44 public class ObjectHeap { |
|
45 |
|
46 private static final boolean DEBUG; |
|
47 |
|
48 static { |
|
49 DEBUG = System.getProperty("sun.jvm.hotspot.oops.ObjectHeap.DEBUG") != null; |
|
50 } |
|
51 |
|
52 private Address boolArrayKlassHandle; |
|
53 private Address byteArrayKlassHandle; |
|
54 private Address charArrayKlassHandle; |
|
55 private Address intArrayKlassHandle; |
|
56 private Address shortArrayKlassHandle; |
|
57 private Address longArrayKlassHandle; |
|
58 private Address singleArrayKlassHandle; |
|
59 private Address doubleArrayKlassHandle; |
|
60 |
|
61 private TypeArrayKlass boolArrayKlassObj; |
|
62 private TypeArrayKlass byteArrayKlassObj; |
|
63 private TypeArrayKlass charArrayKlassObj; |
|
64 private TypeArrayKlass intArrayKlassObj; |
|
65 private TypeArrayKlass shortArrayKlassObj; |
|
66 private TypeArrayKlass longArrayKlassObj; |
|
67 private TypeArrayKlass singleArrayKlassObj; |
|
68 private TypeArrayKlass doubleArrayKlassObj; |
|
69 |
|
70 public void initialize(TypeDataBase db) throws WrongTypeException { |
|
71 // Lookup the roots in the object hierarchy. |
|
72 Type universeType = db.lookupType("Universe"); |
|
73 |
|
74 boolArrayKlassHandle = universeType.getAddressField("_boolArrayKlassObj").getValue(); |
|
75 boolArrayKlassObj = new TypeArrayKlass(boolArrayKlassHandle); |
|
76 |
|
77 byteArrayKlassHandle = universeType.getAddressField("_byteArrayKlassObj").getValue(); |
|
78 byteArrayKlassObj = new TypeArrayKlass(byteArrayKlassHandle); |
|
79 |
|
80 charArrayKlassHandle = universeType.getAddressField("_charArrayKlassObj").getValue(); |
|
81 charArrayKlassObj = new TypeArrayKlass(charArrayKlassHandle); |
|
82 |
|
83 intArrayKlassHandle = universeType.getAddressField("_intArrayKlassObj").getValue(); |
|
84 intArrayKlassObj = new TypeArrayKlass(intArrayKlassHandle); |
|
85 |
|
86 shortArrayKlassHandle = universeType.getAddressField("_shortArrayKlassObj").getValue(); |
|
87 shortArrayKlassObj = new TypeArrayKlass(shortArrayKlassHandle); |
|
88 |
|
89 longArrayKlassHandle = universeType.getAddressField("_longArrayKlassObj").getValue(); |
|
90 longArrayKlassObj = new TypeArrayKlass(longArrayKlassHandle); |
|
91 |
|
92 singleArrayKlassHandle = universeType.getAddressField("_singleArrayKlassObj").getValue(); |
|
93 singleArrayKlassObj = new TypeArrayKlass(singleArrayKlassHandle); |
|
94 |
|
95 doubleArrayKlassHandle = universeType.getAddressField("_doubleArrayKlassObj").getValue(); |
|
96 doubleArrayKlassObj = new TypeArrayKlass(doubleArrayKlassHandle); |
|
97 } |
|
98 |
|
99 public ObjectHeap(TypeDataBase db) throws WrongTypeException { |
|
100 // Get commonly used sizes of basic types |
|
101 oopSize = VM.getVM().getOopSize(); |
|
102 byteSize = db.getJByteType().getSize(); |
|
103 charSize = db.getJCharType().getSize(); |
|
104 booleanSize = db.getJBooleanType().getSize(); |
|
105 intSize = db.getJIntType().getSize(); |
|
106 shortSize = db.getJShortType().getSize(); |
|
107 longSize = db.getJLongType().getSize(); |
|
108 floatSize = db.getJFloatType().getSize(); |
|
109 doubleSize = db.getJDoubleType().getSize(); |
|
110 |
|
111 initialize(db); |
|
112 } |
|
113 |
|
114 /** Comparison operation for oops, either or both of which may be null */ |
|
115 public boolean equal(Oop o1, Oop o2) { |
|
116 if (o1 != null) return o1.equals(o2); |
|
117 return (o2 == null); |
|
118 } |
|
119 |
|
120 // Cached sizes of basic types |
|
121 private long oopSize; |
|
122 private long byteSize; |
|
123 private long charSize; |
|
124 private long booleanSize; |
|
125 private long intSize; |
|
126 private long shortSize; |
|
127 private long longSize; |
|
128 private long floatSize; |
|
129 private long doubleSize; |
|
130 |
|
131 public long getOopSize() { return oopSize; } |
|
132 public long getByteSize() { return byteSize; } |
|
133 public long getCharSize() { return charSize; } |
|
134 public long getBooleanSize() { return booleanSize; } |
|
135 public long getIntSize() { return intSize; } |
|
136 public long getShortSize() { return shortSize; } |
|
137 public long getLongSize() { return longSize; } |
|
138 public long getFloatSize() { return floatSize; } |
|
139 public long getDoubleSize() { return doubleSize; } |
|
140 |
|
141 // Accessors for well-known system classes (from Universe) |
|
142 public TypeArrayKlass getBoolArrayKlassObj() { return boolArrayKlassObj; } |
|
143 public TypeArrayKlass getByteArrayKlassObj() { return byteArrayKlassObj; } |
|
144 public TypeArrayKlass getCharArrayKlassObj() { return charArrayKlassObj; } |
|
145 public TypeArrayKlass getIntArrayKlassObj() { return intArrayKlassObj; } |
|
146 public TypeArrayKlass getShortArrayKlassObj() { return shortArrayKlassObj; } |
|
147 public TypeArrayKlass getLongArrayKlassObj() { return longArrayKlassObj; } |
|
148 public TypeArrayKlass getSingleArrayKlassObj() { return singleArrayKlassObj; } |
|
149 public TypeArrayKlass getDoubleArrayKlassObj() { return doubleArrayKlassObj; } |
|
150 |
|
151 /** Takes a BasicType and returns the corresponding primitive array |
|
152 klass */ |
|
153 public Klass typeArrayKlassObj(int t) { |
|
154 if (t == BasicType.getTBoolean()) return getBoolArrayKlassObj(); |
|
155 if (t == BasicType.getTChar()) return getCharArrayKlassObj(); |
|
156 if (t == BasicType.getTFloat()) return getSingleArrayKlassObj(); |
|
157 if (t == BasicType.getTDouble()) return getDoubleArrayKlassObj(); |
|
158 if (t == BasicType.getTByte()) return getByteArrayKlassObj(); |
|
159 if (t == BasicType.getTShort()) return getShortArrayKlassObj(); |
|
160 if (t == BasicType.getTInt()) return getIntArrayKlassObj(); |
|
161 if (t == BasicType.getTLong()) return getLongArrayKlassObj(); |
|
162 throw new RuntimeException("Illegal basic type " + t); |
|
163 } |
|
164 |
|
165 /** an interface to filter objects while walking heap */ |
|
166 public static interface ObjectFilter { |
|
167 public boolean canInclude(Oop obj); |
|
168 } |
|
169 |
|
170 /** The base heap iteration mechanism */ |
|
171 public void iterate(HeapVisitor visitor) { |
|
172 iterateLiveRegions(collectLiveRegions(), visitor, null); |
|
173 } |
|
174 |
|
175 /** iterate objects satisfying a specified ObjectFilter */ |
|
176 public void iterate(HeapVisitor visitor, ObjectFilter of) { |
|
177 iterateLiveRegions(collectLiveRegions(), visitor, of); |
|
178 } |
|
179 |
|
180 /** iterate objects of given Klass. param 'includeSubtypes' tells whether to |
|
181 * include objects of subtypes or not */ |
|
182 public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k, boolean includeSubtypes) { |
|
183 if (includeSubtypes) { |
|
184 if (k.isFinal()) { |
|
185 // do the simpler "exact" klass loop |
|
186 iterateExact(visitor, k); |
|
187 } else { |
|
188 iterateSubtypes(visitor, k); |
|
189 } |
|
190 } else { |
|
191 // there can no object of abstract classes and interfaces |
|
192 if (!k.isAbstract() && !k.isInterface()) { |
|
193 iterateExact(visitor, k); |
|
194 } |
|
195 } |
|
196 } |
|
197 |
|
198 /** iterate objects of given Klass (objects of subtypes included) */ |
|
199 public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k) { |
|
200 iterateObjectsOfKlass(visitor, k, true); |
|
201 } |
|
202 |
|
203 /** This routine can be used to iterate through the heap at an |
|
204 extremely low level (stepping word-by-word) to provide the |
|
205 ability to do very low-level debugging */ |
|
206 public void iterateRaw(RawHeapVisitor visitor) { |
|
207 List liveRegions = collectLiveRegions(); |
|
208 |
|
209 // Summarize size |
|
210 long totalSize = 0; |
|
211 for (int i = 0; i < liveRegions.size(); i += 2) { |
|
212 Address bottom = (Address) liveRegions.get(i); |
|
213 Address top = (Address) liveRegions.get(i+1); |
|
214 totalSize += top.minus(bottom); |
|
215 } |
|
216 visitor.prologue(totalSize); |
|
217 |
|
218 for (int i = 0; i < liveRegions.size(); i += 2) { |
|
219 Address bottom = (Address) liveRegions.get(i); |
|
220 Address top = (Address) liveRegions.get(i+1); |
|
221 |
|
222 // Traverses the space from bottom to top |
|
223 while (bottom.lessThan(top)) { |
|
224 visitor.visitAddress(bottom); |
|
225 bottom = bottom.addOffsetTo(VM.getVM().getAddressSize()); |
|
226 } |
|
227 } |
|
228 |
|
229 visitor.epilogue(); |
|
230 } |
|
231 |
|
232 public boolean isValidMethod(Address handle) { |
|
233 try { |
|
234 Method m = (Method)Metadata.instantiateWrapperFor(handle); |
|
235 return true; |
|
236 } catch (Exception e) { |
|
237 return false; |
|
238 } |
|
239 } |
|
240 |
|
241 // Creates an instance from the Oop hierarchy based based on the handle |
|
242 public Oop newOop(OopHandle handle) { |
|
243 // The only known way to detect the right type of an oop is |
|
244 // traversing the class chain until a well-known klass is recognized. |
|
245 // A more direct solution would require the klasses to expose |
|
246 // the C++ vtbl structure. |
|
247 |
|
248 // Handle the null reference |
|
249 if (handle == null) return null; |
|
250 |
|
251 // Then check if obj.klass() is one of the root objects |
|
252 Klass klass = Oop.getKlassForOopHandle(handle); |
|
253 if (klass != null) { |
|
254 if (klass instanceof TypeArrayKlass) return new TypeArray(handle, this); |
|
255 if (klass instanceof ObjArrayKlass) return new ObjArray(handle, this); |
|
256 if (klass instanceof InstanceKlass) return new Instance(handle, this); |
|
257 } |
|
258 |
|
259 if (DEBUG) { |
|
260 System.err.println("Unknown oop at " + handle); |
|
261 System.err.println("Oop's klass is " + klass); |
|
262 } |
|
263 |
|
264 throw new UnknownOopException(); |
|
265 } |
|
266 |
|
267 // Print all objects in the object heap |
|
268 public void print() { |
|
269 HeapPrinter printer = new HeapPrinter(System.out); |
|
270 iterate(printer); |
|
271 } |
|
272 |
|
273 //--------------------------------------------------------------------------- |
|
274 // Internals only below this point |
|
275 // |
|
276 |
|
277 private void iterateExact(HeapVisitor visitor, final Klass k) { |
|
278 iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() { |
|
279 public boolean canInclude(Oop obj) { |
|
280 Klass tk = obj.getKlass(); |
|
281 // null Klass is seen sometimes! |
|
282 return (tk != null && tk.equals(k)); |
|
283 } |
|
284 }); |
|
285 } |
|
286 |
|
287 private void iterateSubtypes(HeapVisitor visitor, final Klass k) { |
|
288 iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() { |
|
289 public boolean canInclude(Oop obj) { |
|
290 Klass tk = obj.getKlass(); |
|
291 // null Klass is seen sometimes! |
|
292 return (tk != null && tk.isSubtypeOf(k)); |
|
293 } |
|
294 }); |
|
295 } |
|
296 |
|
297 private void iterateLiveRegions(List liveRegions, HeapVisitor visitor, ObjectFilter of) { |
|
298 // Summarize size |
|
299 long totalSize = 0; |
|
300 for (int i = 0; i < liveRegions.size(); i += 2) { |
|
301 Address bottom = (Address) liveRegions.get(i); |
|
302 Address top = (Address) liveRegions.get(i+1); |
|
303 totalSize += top.minus(bottom); |
|
304 } |
|
305 visitor.prologue(totalSize); |
|
306 |
|
307 CompactibleFreeListSpace cmsSpaceOld = null; |
|
308 CollectedHeap heap = VM.getVM().getUniverse().heap(); |
|
309 |
|
310 if (heap instanceof GenCollectedHeap) { |
|
311 GenCollectedHeap genHeap = (GenCollectedHeap) heap; |
|
312 Generation genOld = genHeap.getGen(1); |
|
313 if (genOld instanceof ConcurrentMarkSweepGeneration) { |
|
314 ConcurrentMarkSweepGeneration concGen = (ConcurrentMarkSweepGeneration)genOld; |
|
315 cmsSpaceOld = concGen.cmsSpace(); |
|
316 } |
|
317 } |
|
318 |
|
319 for (int i = 0; i < liveRegions.size(); i += 2) { |
|
320 Address bottom = (Address) liveRegions.get(i); |
|
321 Address top = (Address) liveRegions.get(i+1); |
|
322 |
|
323 try { |
|
324 // Traverses the space from bottom to top |
|
325 OopHandle handle = bottom.addOffsetToAsOopHandle(0); |
|
326 |
|
327 while (handle.lessThan(top)) { |
|
328 Oop obj = null; |
|
329 |
|
330 try { |
|
331 obj = newOop(handle); |
|
332 } catch (UnknownOopException exp) { |
|
333 if (DEBUG) { |
|
334 throw new RuntimeException(" UnknownOopException " + exp); |
|
335 } |
|
336 } |
|
337 if (obj == null) { |
|
338 //Find the object size using Printezis bits and skip over |
|
339 long size = 0; |
|
340 |
|
341 if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle) ){ |
|
342 size = cmsSpaceOld.collector().blockSizeUsingPrintezisBits(handle); |
|
343 } |
|
344 |
|
345 if (size <= 0) { |
|
346 //Either Printezis bits not set or handle is not in cms space. |
|
347 throw new UnknownOopException(); |
|
348 } |
|
349 |
|
350 handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(size)); |
|
351 continue; |
|
352 } |
|
353 if (of == null || of.canInclude(obj)) { |
|
354 if (visitor.doObj(obj)) { |
|
355 // doObj() returns true to abort this loop. |
|
356 break; |
|
357 } |
|
358 } |
|
359 if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle)) { |
|
360 handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(obj.getObjectSize()) ); |
|
361 } else { |
|
362 handle = handle.addOffsetToAsOopHandle(obj.getObjectSize()); |
|
363 } |
|
364 } |
|
365 } |
|
366 catch (AddressException e) { |
|
367 // This is okay at the top of these regions |
|
368 } |
|
369 catch (UnknownOopException e) { |
|
370 // This is okay at the top of these regions |
|
371 } |
|
372 } |
|
373 |
|
374 visitor.epilogue(); |
|
375 } |
|
376 |
|
377 private void addLiveRegions(String name, List input, List output) { |
|
378 for (Iterator itr = input.iterator(); itr.hasNext();) { |
|
379 MemRegion reg = (MemRegion) itr.next(); |
|
380 Address top = reg.end(); |
|
381 Address bottom = reg.start(); |
|
382 if (Assert.ASSERTS_ENABLED) { |
|
383 Assert.that(top != null, "top address in a live region should not be null"); |
|
384 } |
|
385 if (Assert.ASSERTS_ENABLED) { |
|
386 Assert.that(bottom != null, "bottom address in a live region should not be null"); |
|
387 } |
|
388 output.add(top); |
|
389 output.add(bottom); |
|
390 if (DEBUG) { |
|
391 System.err.println("Live region: " + name + ": " + bottom + ", " + top); |
|
392 } |
|
393 } |
|
394 } |
|
395 |
|
396 private class LiveRegionsCollector implements SpaceClosure { |
|
397 LiveRegionsCollector(List l) { |
|
398 liveRegions = l; |
|
399 } |
|
400 |
|
401 public void doSpace(Space s) { |
|
402 addLiveRegions(s.toString(), s.getLiveRegions(), liveRegions); |
|
403 } |
|
404 private List liveRegions; |
|
405 } |
|
406 |
|
407 // Returns a List<Address> where the addresses come in pairs. These |
|
408 // designate the live regions of the heap. |
|
409 private List collectLiveRegions() { |
|
410 // We want to iterate through all live portions of the heap, but |
|
411 // do not want to abort the heap traversal prematurely if we find |
|
412 // a problem (like an allocated but uninitialized object at the |
|
413 // top of a generation). To do this we enumerate all generations' |
|
414 // bottom and top regions, and factor in TLABs if necessary. |
|
415 |
|
416 // List<Address>. Addresses come in pairs. |
|
417 List liveRegions = new ArrayList(); |
|
418 LiveRegionsCollector lrc = new LiveRegionsCollector(liveRegions); |
|
419 |
|
420 CollectedHeap heap = VM.getVM().getUniverse().heap(); |
|
421 |
|
422 if (heap instanceof GenCollectedHeap) { |
|
423 GenCollectedHeap genHeap = (GenCollectedHeap) heap; |
|
424 // Run through all generations, obtaining bottom-top pairs. |
|
425 for (int i = 0; i < genHeap.nGens(); i++) { |
|
426 Generation gen = genHeap.getGen(i); |
|
427 gen.spaceIterate(lrc, true); |
|
428 } |
|
429 } else if (heap instanceof ParallelScavengeHeap) { |
|
430 ParallelScavengeHeap psh = (ParallelScavengeHeap) heap; |
|
431 PSYoungGen youngGen = psh.youngGen(); |
|
432 // Add eden space |
|
433 addLiveRegions("eden", youngGen.edenSpace().getLiveRegions(), liveRegions); |
|
434 // Add from-space but not to-space |
|
435 addLiveRegions("from", youngGen.fromSpace().getLiveRegions(), liveRegions); |
|
436 PSOldGen oldGen = psh.oldGen(); |
|
437 addLiveRegions("old ", oldGen.objectSpace().getLiveRegions(), liveRegions); |
|
438 } else if (heap instanceof G1CollectedHeap) { |
|
439 G1CollectedHeap g1h = (G1CollectedHeap) heap; |
|
440 g1h.heapRegionIterate(lrc); |
|
441 } else { |
|
442 if (Assert.ASSERTS_ENABLED) { |
|
443 Assert.that(false, "Expecting GenCollectedHeap, G1CollectedHeap, " + |
|
444 "or ParallelScavengeHeap, but got " + |
|
445 heap.getClass().getName()); |
|
446 } |
|
447 } |
|
448 |
|
449 // If UseTLAB is enabled, snip out regions associated with TLABs' |
|
450 // dead regions. Note that TLABs can be present in any generation. |
|
451 |
|
452 // FIXME: consider adding fewer boundaries to live region list. |
|
453 // Theoretically only need to stop at TLAB's top and resume at its |
|
454 // end. |
|
455 |
|
456 if (VM.getVM().getUseTLAB()) { |
|
457 for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) { |
|
458 ThreadLocalAllocBuffer tlab = thread.tlab(); |
|
459 if (tlab.start() != null) { |
|
460 if ((tlab.top() == null) || (tlab.end() == null)) { |
|
461 System.err.print("Warning: skipping invalid TLAB for thread "); |
|
462 thread.printThreadIDOn(System.err); |
|
463 System.err.println(); |
|
464 } else { |
|
465 if (DEBUG) { |
|
466 System.err.print("TLAB for " + thread.getThreadName() + ", #"); |
|
467 thread.printThreadIDOn(System.err); |
|
468 System.err.print(": "); |
|
469 tlab.printOn(System.err); |
|
470 } |
|
471 // Go from: |
|
472 // - below start() to start() |
|
473 // - start() to top() |
|
474 // - end() and above |
|
475 liveRegions.add(tlab.start()); |
|
476 liveRegions.add(tlab.start()); |
|
477 liveRegions.add(tlab.top()); |
|
478 liveRegions.add(tlab.hardEnd()); |
|
479 } |
|
480 } |
|
481 } |
|
482 } |
|
483 |
|
484 // Now sort live regions |
|
485 sortLiveRegions(liveRegions); |
|
486 |
|
487 if (Assert.ASSERTS_ENABLED) { |
|
488 Assert.that(liveRegions.size() % 2 == 0, "Must have even number of region boundaries"); |
|
489 } |
|
490 |
|
491 if (DEBUG) { |
|
492 System.err.println("liveRegions:"); |
|
493 for (int i = 0; i < liveRegions.size(); i += 2) { |
|
494 Address bottom = (Address) liveRegions.get(i); |
|
495 Address top = (Address) liveRegions.get(i+1); |
|
496 System.err.println(" " + bottom + " - " + top); |
|
497 } |
|
498 } |
|
499 |
|
500 return liveRegions; |
|
501 } |
|
502 |
|
503 private void sortLiveRegions(List liveRegions) { |
|
504 Collections.sort(liveRegions, new Comparator() { |
|
505 public int compare(Object o1, Object o2) { |
|
506 Address a1 = (Address) o1; |
|
507 Address a2 = (Address) o2; |
|
508 if (AddressOps.lt(a1, a2)) { |
|
509 return -1; |
|
510 } else if (AddressOps.gt(a1, a2)) { |
|
511 return 1; |
|
512 } |
|
513 return 0; |
|
514 } |
|
515 }); |
|
516 } |
|
517 } |