/*
* Copyright 2014 Edward Aftandilian. All Rights Reserved.
*
* 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 edu.tufts.eaftan.hprofparser.handler.examples.statisticscollectinghandler;
import com.google.common.primitives.Ints;
import edu.tufts.eaftan.hprofparser.handler.NullRecordHandler;
import edu.tufts.eaftan.hprofparser.parser.datastructures.Constant;
import edu.tufts.eaftan.hprofparser.parser.datastructures.InstanceField;
import edu.tufts.eaftan.hprofparser.parser.datastructures.Static;
import edu.tufts.eaftan.hprofparser.parser.datastructures.Type;
import edu.tufts.eaftan.hprofparser.parser.datastructures.Value;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Computes basic statistics about a heap dump. For example, the number of instances of each
* type and the total bytes used by instances of that type.
*
* <p>Note that this is nowhere near accurate. We intentionally ignore array and object header
* sizes because they are VM dependent. We also don't know how the fields were laid out, what the
* alignment was, whether OOPs were compressed, etc. There's no way to get this right without
* knowing details about the VM & options that produced it.
*/
public class StatisticsCollectingHandler extends NullRecordHandler {
private Map<Long, TypeInfo> classMap = new HashMap<>();
private Map<Long, String> stringMap = new HashMap<>();
private Map<String, TypeInfo> arrayInfoMap = new HashMap<>();
@Override
public void stringInUTF8(long id, String data) {
stringMap.put(id, data);
}
@Override
public void loadClass(int classSerialNum, long classObjId, int stackTraceSerialNum,
long classNameStringId) {
ClassInfo classInfo = new ClassInfo();
classInfo.className = stringMap.get(classNameStringId);
classMap.put(classObjId, classInfo);
}
@Override
public void classDump(long classObjId, int stackTraceSerialNum, long superClassObjId,
long classLoaderObjId, long signersObjId, long protectionDomainObjId, long reserved1,
long reserved2, int instanceSize, Constant[] constants, Static[] statics,
InstanceField[] instanceFields) {
ClassInfo classInfo = (ClassInfo) classMap.get(classObjId);
classInfo.instanceSize = instanceSize;
}
@Override
public void instanceDump(long objId, int stackTraceSerialNum, long classObjId,
Value<?>[] instanceFieldValues) {
ClassInfo classInfo = (ClassInfo) classMap.get(classObjId);
classInfo.instanceCount++;
}
@Override
public void objArrayDump(long objId, int stackTraceSerialNum, long elemClassObjId, long[] elems) {
String typeDescriptor = "[" + classMap.get(elemClassObjId).className;
int length = elems != null ? elems.length : 0;
recordArrayInstance(typeDescriptor, length * Type.OBJ.sizeInBytes());
}
@Override
public void primArrayDump(long objId, int stackTraceSerialNum, byte hprofElemType, Value<?>[] elems) {
Type elemType = Type.hprofTypeToEnum(hprofElemType);
String typeDescriptor = "[" + elemType.toString();
int length = elems != null ? elems.length : 0;
recordArrayInstance(typeDescriptor, length * elemType.sizeInBytes());
}
private void recordArrayInstance(String typeDescriptor, int bytes) {
ArrayInfo arrayInfo = (ArrayInfo) arrayInfoMap.get(typeDescriptor);
if (arrayInfo == null) {
arrayInfo = new ArrayInfo();
arrayInfo.className = typeDescriptor;
arrayInfoMap.put(typeDescriptor, arrayInfo);
}
arrayInfo.instanceCount++;
arrayInfo.totalSize += bytes;
}
@Override
public void finished() {
Comparator<TypeInfo> totalSizeComparator = new Comparator<TypeInfo>() {
@Override
public int compare(TypeInfo cls1, TypeInfo cls2) {
return Ints.checkedCast(cls2.totalSize() - cls1.totalSize());
}
};
List<TypeInfo> typeInfoList = new ArrayList<>(classMap.values());
typeInfoList.addAll(arrayInfoMap.values());
Collections.sort(typeInfoList, totalSizeComparator);
for (TypeInfo typeInfo : typeInfoList) {
if (typeInfo.instanceCount == 0) {
continue;
}
System.out.println(typeInfo.toString());
}
}
}