/*******************************************************************************
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.kato.hprof.java;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import javax.tools.diagnostics.image.CorruptDataException;
import javax.tools.diagnostics.image.ImageAddressSpace;
import javax.tools.diagnostics.image.ImagePointer;
import javax.tools.diagnostics.runtime.java.JavaClass;
import javax.tools.diagnostics.runtime.java.JavaObject;
import org.apache.kato.common.BasicType;
import org.apache.kato.common.IteratorBackedList;
import org.apache.kato.common.ObjectMapList;
import org.apache.kato.hprof.HProfView;
import org.apache.kato.hprof.IJavaClass;
import org.apache.kato.hprof.IJavaClassLoader;
import org.apache.kato.hprof.datalayer.IGCInstanceHeapDumpRecord;
import org.apache.kato.hprof.datalayer.IGCObjectArrayHeapDumpRecord;
import org.apache.kato.hprof.datalayer.IGCPrimitiveArrayHeapDumpRecord;
import org.apache.kato.hprof.datalayer.IHProfRecord;
import org.apache.kato.hprof.datalayer.IHeapDumpHProfRecord;
import org.apache.kato.hprof.datalayer.IHeapObject;
/**
* Contains the contents of the heap - JavaClasses and JavaClassLoaders and object
* of all sorts.
*
*
*/
public class JavaHeapImpl implements IJavaHeapInternal {
private HProfView view=null;
long heapRecordId=0;
private ImageAddressSpace addressSpace;
public JavaHeapImpl(HProfView view,long loc, ImageAddressSpace addressSpace) {
if(loc<1) throw new IllegalArgumentException("heap record id ["+loc+"] is less than 1");
this.view=view;
this.heapRecordId=loc;
this.addressSpace = addressSpace;
}
@Override
public String getName() {
return ""+heapRecordId;
}
List heapList = null;
@Override
public List getObjects() {
final IHeapDumpHProfRecord record=view.getHeapRecord();
if(record==null) return new LinkedList();
if (heapList == null) {
Iterator iter = new Iterator(){
int latestRecord=0;
private JavaObject nextObject=null;
@Override
public boolean hasNext() {
if(nextObject!=null) return true;
while(true) {
IHProfRecord subRecord=record.getSubRecord(latestRecord);
if(subRecord==null) {
return false;
}
latestRecord++;
// is this an object record?
if(subRecord instanceof IGCInstanceHeapDumpRecord) {
IGCInstanceHeapDumpRecord instance = (IGCInstanceHeapDumpRecord) subRecord;
nextObject = new JavaObjectInstanceImpl(JavaHeapImpl.this, instance);
return true;
} else if (subRecord instanceof IGCObjectArrayHeapDumpRecord) {
IGCObjectArrayHeapDumpRecord objArray = (IGCObjectArrayHeapDumpRecord) subRecord;
nextObject = new JavaObjectArrayImpl(JavaHeapImpl.this, objArray);
return true;
} else if (subRecord instanceof IGCPrimitiveArrayHeapDumpRecord) {
IGCPrimitiveArrayHeapDumpRecord primArray = (IGCPrimitiveArrayHeapDumpRecord) subRecord;
nextObject = new JavaPrimitiveArrayImpl(JavaHeapImpl.this, primArray);
return true;
}
}
}
@Override
public Object next() {
if (hasNext()) {
// We'll never get a new object if nextObject is never null...
Object returnObject = nextObject;
nextObject = null;
return returnObject;
} else {
throw new NoSuchElementException("JavaHeap.getObjects() reached past end of iterator.");
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("JavaHeap.getObject() Iterator.remove() method not supported.");
}};
iter.hasNext(); // initialize iterator.
heapList = new IteratorBackedList(iter);
}
return heapList;
}
@Override
public List getSections() {
return new LinkedList();
}
/**
* Retrieves an object by it's ID.
*
* @param ID
* @return
*/
@Override
public JavaObject getObjectByID(Long ID) {
if (ID.longValue() == 0L) {
return null;
}
IHeapObject obj = view.getJavaObjectByID(ID);
if(obj == null) {
return null;
}
if (obj instanceof IGCInstanceHeapDumpRecord) {
return new JavaObjectInstanceImpl(this, (IGCInstanceHeapDumpRecord)obj);
} else if (obj instanceof IGCObjectArrayHeapDumpRecord) {
return new JavaObjectArrayImpl(this, (IGCObjectArrayHeapDumpRecord) obj);
} else if (obj instanceof IGCPrimitiveArrayHeapDumpRecord) {
return new JavaPrimitiveArrayImpl(this, (IGCPrimitiveArrayHeapDumpRecord) obj);
}
return null;
}
private HashMap<Long,JavaClassImpl> classCache = new HashMap<Long,JavaClassImpl>();
/**
* Provides a single point for getting classes.
* All class instantiations should go through here.
*
* @param ID ID of a class.
* @return JavaClassImpl
*/
public JavaClassImpl getJavaClassByID(long ID) {
JavaClassImpl javaClass = classCache.get(ID);
if (javaClass == null) {
IJavaClass clazz = view.getJavaClassByID(ID);
javaClass = new JavaClassImpl(this, clazz);
classCache.put(ID, javaClass);
}
return javaClass;
}
final static private String primitiveArrayClassNames[] = {"[Z","[C","[F","[D","[B","[S","[I","[J"};
private JavaClass[] primitiveArrayClasses = new JavaClass[8];
/**
* Gets the primitive array of the passed basic type.
*
* @see javax.tools.diagnostics.common.BasicType
* @param type a value from 4 to 11
* @return JavaClassImpl of a primitive array
*/
public JavaClass getPrimitiveArrayClass(short type) {
if (type <4 || type > 11) {
throw new IllegalArgumentException("Passed invalid basic type id "+type);
}
JavaClass clazz = primitiveArrayClasses[type-4];
if(clazz == null) {
IJavaClass viewClass = view.getPrimitiveArrayClass(type);
if (viewClass != null) {
long ID = viewClass.getClassObjectID();
clazz = getJavaClassByID(ID);
} else {
// create synthetic class.
clazz = new JavaSyntheticPrimitiveArrayImpl(getPrimitiveClass(type), primitiveArrayClassNames[type-4],
this.getJavaClassLoaderByID(0));
}
primitiveArrayClasses[type-4] = clazz;
}
return clazz;
}
private JavaPrimitiveClassImpl[] primitiveClasses = new JavaPrimitiveClassImpl[8];
public JavaPrimitiveClassImpl getPrimitiveClass(int type) {
if (type < 4 || type > 11) {
throw new IllegalArgumentException("Passed invalid basic type id "+type);
}
JavaPrimitiveClassImpl clazz = primitiveClasses[type-4];
if (clazz == null) {
String name;
switch(type) {
case BasicType.BOOLEAN:
name = "boolean";
break;
case BasicType.BYTE:
name = "byte";
break;
case BasicType.SHORT:
name = "short";
break;
case BasicType.CHAR:
name = "char";
break;
case BasicType.INT:
name = "int";
break;
case BasicType.LONG:
name = "long";
break;
case BasicType.FLOAT:
name = "float";
break;
case BasicType.DOUBLE:
name = "double";
break;
default:
name ="<invalid primitive class type "+type+">";
}
clazz = primitiveClasses[type-4] = new JavaPrimitiveClassImpl(name, getImagePointer(type),
this.getJavaClassLoaderByID(0)); // The system class loader is 0
}
return clazz;
}
/**
* Map from JavaClassLoader IDs to JavaClassLoaders.
*/
private ObjectMapList<Long,JavaClassLoaderImpl> javaClassLoaders;
/**
* Create a map of JavaClassLoader object ID's to JavaClassLoaderImpl's.
*
* JavaClassLoaders are expected to be sufficiently low in number to warrant
* keeping a definitive collection to avoid duplication by JavaClasses.
*/
private void createJavaClassLoaders() {
if (javaClassLoaders == null) {
javaClassLoaders = new ObjectMapList<Long,JavaClassLoaderImpl>();
for (IJavaClassLoader loader : view.getJavaClassLoaders()) {
javaClassLoaders.put(loader.getID(),new JavaClassLoaderImpl(this, loader));
}
// We also want to get the Classloaders that have no class instances.
Iterator objects = getObjects().iterator();
while (objects.hasNext()) {
Object next = objects.next();
if (next instanceof JavaObjectInstanceImpl) {
try {
JavaClass clazz = ((JavaObject) next).getJavaClass();
while(clazz != null) {
if ("java/lang/ClassLoader".equals(clazz.getName()) ) {
JavaObjectInstanceImpl obj = (JavaObjectInstanceImpl) next;
long id = obj.getObjectID();
if (javaClassLoaders.get(id) == null) {
javaClassLoaders.put(id, new JavaClassLoaderImpl(this, new EmptyClassLoaderImpl(id)));
}
}
clazz = clazz.getSuperclass();
}
} catch (CorruptDataException e) {
e.printStackTrace();
}
}
}
}
}
@Override
/**
* Returns the java class loaders
* @return
*/
public List<JavaClassLoaderImpl> getJavaClassLoaders() {
createJavaClassLoaders();
return javaClassLoaders.values();
}
/**
* Retrieve a JavaClassLoaderImpl by it's ID.
* Used by JavaClassImpl.
*
* @param ID
* @return
*/
@Override
public JavaClassLoaderImpl getJavaClassLoaderByID(long ID) {
createJavaClassLoaders();
return javaClassLoaders.get(ID);
}
@Override
public String getUTF8StringByID(long ID) {
return view.getUTF8String(ID);
}
@Override
public ImagePointer getImagePointer(long address) {
return addressSpace.getPointer(address);
}
}