blob: d550a935fa38dbabb394c690e9c298c31b0dbe14 [file] [log] [blame]
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
//
// The ObjectHeap is an abstraction over all generations in the VM
// It gives access to all present objects and classes.
//
package sun.jvm.hotspot.oops;
import java.util.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.gc_interface.*;
import sun.jvm.hotspot.gc_implementation.g1.*;
import sun.jvm.hotspot.gc_implementation.parallelScavenge.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.utilities.*;
public class ObjectHeap {
private static final boolean DEBUG;
static {
DEBUG = System.getProperty("sun.jvm.hotspot.oops.ObjectHeap.DEBUG") != null;
}
private Address boolArrayKlassHandle;
private Address byteArrayKlassHandle;
private Address charArrayKlassHandle;
private Address intArrayKlassHandle;
private Address shortArrayKlassHandle;
private Address longArrayKlassHandle;
private Address singleArrayKlassHandle;
private Address doubleArrayKlassHandle;
private TypeArrayKlass boolArrayKlassObj;
private TypeArrayKlass byteArrayKlassObj;
private TypeArrayKlass charArrayKlassObj;
private TypeArrayKlass intArrayKlassObj;
private TypeArrayKlass shortArrayKlassObj;
private TypeArrayKlass longArrayKlassObj;
private TypeArrayKlass singleArrayKlassObj;
private TypeArrayKlass doubleArrayKlassObj;
public void initialize(TypeDataBase db) throws WrongTypeException {
// Lookup the roots in the object hierarchy.
Type universeType = db.lookupType("Universe");
boolArrayKlassHandle = universeType.getAddressField("_boolArrayKlassObj").getValue();
boolArrayKlassObj = new TypeArrayKlass(boolArrayKlassHandle);
byteArrayKlassHandle = universeType.getAddressField("_byteArrayKlassObj").getValue();
byteArrayKlassObj = new TypeArrayKlass(byteArrayKlassHandle);
charArrayKlassHandle = universeType.getAddressField("_charArrayKlassObj").getValue();
charArrayKlassObj = new TypeArrayKlass(charArrayKlassHandle);
intArrayKlassHandle = universeType.getAddressField("_intArrayKlassObj").getValue();
intArrayKlassObj = new TypeArrayKlass(intArrayKlassHandle);
shortArrayKlassHandle = universeType.getAddressField("_shortArrayKlassObj").getValue();
shortArrayKlassObj = new TypeArrayKlass(shortArrayKlassHandle);
longArrayKlassHandle = universeType.getAddressField("_longArrayKlassObj").getValue();
longArrayKlassObj = new TypeArrayKlass(longArrayKlassHandle);
singleArrayKlassHandle = universeType.getAddressField("_singleArrayKlassObj").getValue();
singleArrayKlassObj = new TypeArrayKlass(singleArrayKlassHandle);
doubleArrayKlassHandle = universeType.getAddressField("_doubleArrayKlassObj").getValue();
doubleArrayKlassObj = new TypeArrayKlass(doubleArrayKlassHandle);
}
public ObjectHeap(TypeDataBase db) throws WrongTypeException {
// Get commonly used sizes of basic types
oopSize = VM.getVM().getOopSize();
byteSize = db.getJByteType().getSize();
charSize = db.getJCharType().getSize();
booleanSize = db.getJBooleanType().getSize();
intSize = db.getJIntType().getSize();
shortSize = db.getJShortType().getSize();
longSize = db.getJLongType().getSize();
floatSize = db.getJFloatType().getSize();
doubleSize = db.getJDoubleType().getSize();
initialize(db);
}
/** Comparison operation for oops, either or both of which may be null */
public boolean equal(Oop o1, Oop o2) {
if (o1 != null) return o1.equals(o2);
return (o2 == null);
}
// Cached sizes of basic types
private long oopSize;
private long byteSize;
private long charSize;
private long booleanSize;
private long intSize;
private long shortSize;
private long longSize;
private long floatSize;
private long doubleSize;
public long getOopSize() { return oopSize; }
public long getByteSize() { return byteSize; }
public long getCharSize() { return charSize; }
public long getBooleanSize() { return booleanSize; }
public long getIntSize() { return intSize; }
public long getShortSize() { return shortSize; }
public long getLongSize() { return longSize; }
public long getFloatSize() { return floatSize; }
public long getDoubleSize() { return doubleSize; }
// Accessors for well-known system classes (from Universe)
public TypeArrayKlass getBoolArrayKlassObj() { return boolArrayKlassObj; }
public TypeArrayKlass getByteArrayKlassObj() { return byteArrayKlassObj; }
public TypeArrayKlass getCharArrayKlassObj() { return charArrayKlassObj; }
public TypeArrayKlass getIntArrayKlassObj() { return intArrayKlassObj; }
public TypeArrayKlass getShortArrayKlassObj() { return shortArrayKlassObj; }
public TypeArrayKlass getLongArrayKlassObj() { return longArrayKlassObj; }
public TypeArrayKlass getSingleArrayKlassObj() { return singleArrayKlassObj; }
public TypeArrayKlass getDoubleArrayKlassObj() { return doubleArrayKlassObj; }
/** Takes a BasicType and returns the corresponding primitive array
klass */
public Klass typeArrayKlassObj(int t) {
if (t == BasicType.getTBoolean()) return getBoolArrayKlassObj();
if (t == BasicType.getTChar()) return getCharArrayKlassObj();
if (t == BasicType.getTFloat()) return getSingleArrayKlassObj();
if (t == BasicType.getTDouble()) return getDoubleArrayKlassObj();
if (t == BasicType.getTByte()) return getByteArrayKlassObj();
if (t == BasicType.getTShort()) return getShortArrayKlassObj();
if (t == BasicType.getTInt()) return getIntArrayKlassObj();
if (t == BasicType.getTLong()) return getLongArrayKlassObj();
throw new RuntimeException("Illegal basic type " + t);
}
/** an interface to filter objects while walking heap */
public static interface ObjectFilter {
public boolean canInclude(Oop obj);
}
/** The base heap iteration mechanism */
public void iterate(HeapVisitor visitor) {
iterateLiveRegions(collectLiveRegions(), visitor, null);
}
/** iterate objects satisfying a specified ObjectFilter */
public void iterate(HeapVisitor visitor, ObjectFilter of) {
iterateLiveRegions(collectLiveRegions(), visitor, of);
}
/** iterate objects of given Klass. param 'includeSubtypes' tells whether to
* include objects of subtypes or not */
public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k, boolean includeSubtypes) {
if (includeSubtypes) {
if (k.isFinal()) {
// do the simpler "exact" klass loop
iterateExact(visitor, k);
} else {
iterateSubtypes(visitor, k);
}
} else {
// there can no object of abstract classes and interfaces
if (!k.isAbstract() && !k.isInterface()) {
iterateExact(visitor, k);
}
}
}
/** iterate objects of given Klass (objects of subtypes included) */
public void iterateObjectsOfKlass(HeapVisitor visitor, final Klass k) {
iterateObjectsOfKlass(visitor, k, true);
}
/** This routine can be used to iterate through the heap at an
extremely low level (stepping word-by-word) to provide the
ability to do very low-level debugging */
public void iterateRaw(RawHeapVisitor visitor) {
List liveRegions = collectLiveRegions();
// Summarize size
long totalSize = 0;
for (int i = 0; i < liveRegions.size(); i += 2) {
Address bottom = (Address) liveRegions.get(i);
Address top = (Address) liveRegions.get(i+1);
totalSize += top.minus(bottom);
}
visitor.prologue(totalSize);
for (int i = 0; i < liveRegions.size(); i += 2) {
Address bottom = (Address) liveRegions.get(i);
Address top = (Address) liveRegions.get(i+1);
// Traverses the space from bottom to top
while (bottom.lessThan(top)) {
visitor.visitAddress(bottom);
bottom = bottom.addOffsetTo(VM.getVM().getAddressSize());
}
}
visitor.epilogue();
}
public boolean isValidMethod(Address handle) {
try {
Method m = (Method)Metadata.instantiateWrapperFor(handle);
return true;
} catch (Exception e) {
return false;
}
}
// Creates an instance from the Oop hierarchy based based on the handle
public Oop newOop(OopHandle handle) {
// The only known way to detect the right type of an oop is
// traversing the class chain until a well-known klass is recognized.
// A more direct solution would require the klasses to expose
// the C++ vtbl structure.
// Handle the null reference
if (handle == null) return null;
// Then check if obj.klass() is one of the root objects
Klass klass = Oop.getKlassForOopHandle(handle);
if (klass != null) {
if (klass instanceof TypeArrayKlass) return new TypeArray(handle, this);
if (klass instanceof ObjArrayKlass) return new ObjArray(handle, this);
if (klass instanceof InstanceKlass) return new Instance(handle, this);
}
if (DEBUG) {
System.err.println("Unknown oop at " + handle);
System.err.println("Oop's klass is " + klass);
}
throw new UnknownOopException();
}
// Print all objects in the object heap
public void print() {
HeapPrinter printer = new HeapPrinter(System.out);
iterate(printer);
}
//---------------------------------------------------------------------------
// Internals only below this point
//
private void iterateExact(HeapVisitor visitor, final Klass k) {
iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() {
public boolean canInclude(Oop obj) {
Klass tk = obj.getKlass();
// null Klass is seen sometimes!
return (tk != null && tk.equals(k));
}
});
}
private void iterateSubtypes(HeapVisitor visitor, final Klass k) {
iterateLiveRegions(collectLiveRegions(), visitor, new ObjectFilter() {
public boolean canInclude(Oop obj) {
Klass tk = obj.getKlass();
// null Klass is seen sometimes!
return (tk != null && tk.isSubtypeOf(k));
}
});
}
private void iterateLiveRegions(List liveRegions, HeapVisitor visitor, ObjectFilter of) {
// Summarize size
long totalSize = 0;
for (int i = 0; i < liveRegions.size(); i += 2) {
Address bottom = (Address) liveRegions.get(i);
Address top = (Address) liveRegions.get(i+1);
totalSize += top.minus(bottom);
}
visitor.prologue(totalSize);
CompactibleFreeListSpace cmsSpaceOld = null;
CollectedHeap heap = VM.getVM().getUniverse().heap();
if (heap instanceof GenCollectedHeap) {
GenCollectedHeap genHeap = (GenCollectedHeap) heap;
Generation genOld = genHeap.getGen(1);
if (genOld instanceof ConcurrentMarkSweepGeneration) {
ConcurrentMarkSweepGeneration concGen = (ConcurrentMarkSweepGeneration)genOld;
cmsSpaceOld = concGen.cmsSpace();
}
}
for (int i = 0; i < liveRegions.size(); i += 2) {
Address bottom = (Address) liveRegions.get(i);
Address top = (Address) liveRegions.get(i+1);
try {
// Traverses the space from bottom to top
OopHandle handle = bottom.addOffsetToAsOopHandle(0);
while (handle.lessThan(top)) {
Oop obj = null;
try {
obj = newOop(handle);
} catch (UnknownOopException exp) {
if (DEBUG) {
throw new RuntimeException(" UnknownOopException " + exp);
}
}
if (obj == null) {
//Find the object size using Printezis bits and skip over
long size = 0;
if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle) ){
size = cmsSpaceOld.collector().blockSizeUsingPrintezisBits(handle);
}
if (size <= 0) {
//Either Printezis bits not set or handle is not in cms space.
throw new UnknownOopException();
}
handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(size));
continue;
}
if (of == null || of.canInclude(obj)) {
if (visitor.doObj(obj)) {
// doObj() returns true to abort this loop.
break;
}
}
if ( (cmsSpaceOld != null) && cmsSpaceOld.contains(handle)) {
handle = handle.addOffsetToAsOopHandle(CompactibleFreeListSpace.adjustObjectSizeInBytes(obj.getObjectSize()) );
} else {
handle = handle.addOffsetToAsOopHandle(obj.getObjectSize());
}
}
}
catch (AddressException e) {
// This is okay at the top of these regions
}
catch (UnknownOopException e) {
// This is okay at the top of these regions
}
}
visitor.epilogue();
}
private void addLiveRegions(String name, List input, List output) {
for (Iterator itr = input.iterator(); itr.hasNext();) {
MemRegion reg = (MemRegion) itr.next();
Address top = reg.end();
Address bottom = reg.start();
if (Assert.ASSERTS_ENABLED) {
Assert.that(top != null, "top address in a live region should not be null");
}
if (Assert.ASSERTS_ENABLED) {
Assert.that(bottom != null, "bottom address in a live region should not be null");
}
output.add(top);
output.add(bottom);
if (DEBUG) {
System.err.println("Live region: " + name + ": " + bottom + ", " + top);
}
}
}
private class LiveRegionsCollector implements SpaceClosure {
LiveRegionsCollector(List l) {
liveRegions = l;
}
public void doSpace(Space s) {
addLiveRegions(s.toString(), s.getLiveRegions(), liveRegions);
}
private List liveRegions;
}
// Returns a List<Address> where the addresses come in pairs. These
// designate the live regions of the heap.
private List collectLiveRegions() {
// We want to iterate through all live portions of the heap, but
// do not want to abort the heap traversal prematurely if we find
// a problem (like an allocated but uninitialized object at the
// top of a generation). To do this we enumerate all generations'
// bottom and top regions, and factor in TLABs if necessary.
// List<Address>. Addresses come in pairs.
List liveRegions = new ArrayList();
LiveRegionsCollector lrc = new LiveRegionsCollector(liveRegions);
CollectedHeap heap = VM.getVM().getUniverse().heap();
if (heap instanceof GenCollectedHeap) {
GenCollectedHeap genHeap = (GenCollectedHeap) heap;
// Run through all generations, obtaining bottom-top pairs.
for (int i = 0; i < genHeap.nGens(); i++) {
Generation gen = genHeap.getGen(i);
gen.spaceIterate(lrc, true);
}
} else if (heap instanceof ParallelScavengeHeap) {
ParallelScavengeHeap psh = (ParallelScavengeHeap) heap;
PSYoungGen youngGen = psh.youngGen();
// Add eden space
addLiveRegions("eden", youngGen.edenSpace().getLiveRegions(), liveRegions);
// Add from-space but not to-space
addLiveRegions("from", youngGen.fromSpace().getLiveRegions(), liveRegions);
PSOldGen oldGen = psh.oldGen();
addLiveRegions("old ", oldGen.objectSpace().getLiveRegions(), liveRegions);
} else if (heap instanceof G1CollectedHeap) {
G1CollectedHeap g1h = (G1CollectedHeap) heap;
g1h.heapRegionIterate(lrc);
} else {
if (Assert.ASSERTS_ENABLED) {
Assert.that(false, "Expecting GenCollectedHeap, G1CollectedHeap, " +
"or ParallelScavengeHeap, but got " +
heap.getClass().getName());
}
}
// If UseTLAB is enabled, snip out regions associated with TLABs'
// dead regions. Note that TLABs can be present in any generation.
// FIXME: consider adding fewer boundaries to live region list.
// Theoretically only need to stop at TLAB's top and resume at its
// end.
if (VM.getVM().getUseTLAB()) {
for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) {
ThreadLocalAllocBuffer tlab = thread.tlab();
if (tlab.start() != null) {
if ((tlab.top() == null) || (tlab.end() == null)) {
System.err.print("Warning: skipping invalid TLAB for thread ");
thread.printThreadIDOn(System.err);
System.err.println();
} else {
if (DEBUG) {
System.err.print("TLAB for " + thread.getThreadName() + ", #");
thread.printThreadIDOn(System.err);
System.err.print(": ");
tlab.printOn(System.err);
}
// Go from:
// - below start() to start()
// - start() to top()
// - end() and above
liveRegions.add(tlab.start());
liveRegions.add(tlab.start());
liveRegions.add(tlab.top());
liveRegions.add(tlab.hardEnd());
}
}
}
}
// Now sort live regions
sortLiveRegions(liveRegions);
if (Assert.ASSERTS_ENABLED) {
Assert.that(liveRegions.size() % 2 == 0, "Must have even number of region boundaries");
}
if (DEBUG) {
System.err.println("liveRegions:");
for (int i = 0; i < liveRegions.size(); i += 2) {
Address bottom = (Address) liveRegions.get(i);
Address top = (Address) liveRegions.get(i+1);
System.err.println(" " + bottom + " - " + top);
}
}
return liveRegions;
}
private void sortLiveRegions(List liveRegions) {
Collections.sort(liveRegions, new Comparator() {
public int compare(Object o1, Object o2) {
Address a1 = (Address) o1;
Address a2 = (Address) o2;
if (AddressOps.lt(a1, a2)) {
return -1;
} else if (AddressOps.gt(a1, a2)) {
return 1;
}
return 0;
}
});
}
}