/**
* CopyRight by Chinamobile
*
* ObjectSizer.java
*/
package com.chinamobile.bcbsp.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import com.chinamobile.bcbsp.comm.BSPMessage;
//a reference: 4 bytes
//an Object: 8 bytes
//an Integer: 16 bytes == (8 + 4) / 8 * 8
//an int: 4 bytes
//size of array with zero elements: JRo64 = 24, Sun32 = 12
//size of reference,such as Object = null: Sun32 = 4, Sun64 = 8
//size of object without elements,such as new Object();: Sun32 = 8, Sun64 = 16
//size of byte[0]: Sun32 = 8 + 4, Sun64 = 16 + 8
//size of byte[l]: (l + 19) / 8 * 8
//size of char[l]/short[l]: (l * 2 + 19) / 8 * 8 == (l + 9) / 4 * 8
//size of String with l elements: (l + 1) * 2 + 32
//size of int[l]: (l * 4 + 19) / 8 * 8 == (l + 4) / 2 * 8
//size of long[l]: (l * 8 + 19) / 8 * 8 == (l + 2) * 8
/**
* ObjectSizer
*
* @author Bai Qiushi
* @version 1.0
*/
public class ObjectSizer {
private final byte NULL_REFERENCE_SIZE;
private final byte EMPTY_OBJECT_SIZE;
private final byte EMPTY_ARRAY_VAR_SIZE;
@SuppressWarnings("unchecked")
private List dedup = new ArrayList();
public ObjectSizer(byte nullReferenceSize, byte emptyObjectSize,
byte emptyArrayVarSize) {
this.NULL_REFERENCE_SIZE = nullReferenceSize;
this.EMPTY_OBJECT_SIZE = emptyObjectSize;
this.EMPTY_ARRAY_VAR_SIZE = emptyArrayVarSize;
}
public static ObjectSizer forSun32BitsVM() {
return new ObjectSizer((byte) 4, (byte) 8, (byte) 4);
}
public static ObjectSizer forSun64BitsVM() {
return new ObjectSizer((byte) 8, (byte) 16, (byte) 8);
}
public int sizeOf(BSPMessage msg) {
int size = this.EMPTY_OBJECT_SIZE;
size = size + sizeofPrimitiveClass(int.class); // int of dstPartition
size = size + 2 * msg.getDstVertexID().length() + 32; // String of dstVertex
if (msg.getData() != null)
size = size + msg.getData().length + 19; // byte[] of data
if (msg.getTag() != null)
size = size + msg.getTag().length + 19; // byte[] of tag
return size;
}
/**
* The size of a reference.
*
* @return
*/
public int sizeOfRef() {
return this.NULL_REFERENCE_SIZE;
}
/**
* The size of a char.
*
* @return
*/
public int sizeOfChar() {
return sizeofPrimitiveClass(char.class);
}
/**
* The size of an object.
*
* @param object
* @return
*/
public int sizeOf(Object object) {
dedup.clear();
return calculate(object);
}
private static class ref {
final Object obj;
public ref(Object obj) {
this.obj = obj;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof ref) && ((ref) obj).obj == this.obj;
}
@Override
public int hashCode() {
return obj.hashCode();
}
}
@SuppressWarnings("unchecked")
private int calculate(Object object) {
if (object == null)
return 0;
ref r = new ref(object);
if (dedup.contains(r))
return 0;
dedup.add(r);
int varSize = 0;
int objSize = 0;
for (Class clazz = object.getClass(); clazz != Object.class; clazz = clazz
.getSuperclass()) {
if (clazz.isArray()) {
varSize += EMPTY_ARRAY_VAR_SIZE;
Class<?> componentType = clazz.getComponentType();
if (componentType.isPrimitive()) {
varSize += lengthOfPrimitiveArray(object)
* sizeofPrimitiveClass(componentType);
return OccupationSize(EMPTY_OBJECT_SIZE, varSize, 0);
}
Object[] array = (Object[]) object;
varSize += NULL_REFERENCE_SIZE * array.length;
for (Object o : array)
objSize += calculate(o);
return OccupationSize(EMPTY_OBJECT_SIZE, varSize, objSize);
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()))
continue;
if (clazz != field.getDeclaringClass())
continue;
Class<?> type = field.getType();
if (type.isPrimitive()) {
varSize += sizeofPrimitiveClass(type);
} else {
varSize += NULL_REFERENCE_SIZE;
try {
field.setAccessible(true);
objSize += calculate(field.get(object));
} catch (Exception e) {
objSize += occupyofConstructor(object, field);
}
}
}
}
return OccupationSize(EMPTY_OBJECT_SIZE, varSize, objSize);
}
private static int occupyofConstructor(Object object, Field field) {
throw new UnsupportedOperationException(
"field type Constructor not accessible: " + object.getClass()
+ " field:" + field);
}
private static int OccupationSize(int size) {
return (size + 7) / 8 * 8;
}
private static int OccupationSize(int selfSize, int varsSize, int objsSize) {
return OccupationSize(selfSize) + OccupationSize(varsSize) + objsSize;
}
@SuppressWarnings("unchecked")
private static int sizeofPrimitiveClass(Class clazz) {
return clazz == boolean.class || clazz == byte.class ? 1
: clazz == char.class || clazz == short.class ? 2
: clazz == int.class || clazz == float.class ? 4 : 8;
}
private static int lengthOfPrimitiveArray(Object object) {
Class<?> clazz = object.getClass();
return clazz == boolean[].class ? ((boolean[]) object).length
: clazz == byte[].class ? ((byte[]) object).length
: clazz == char[].class ? ((char[]) object).length
: clazz == short[].class ? ((short[]) object).length
: clazz == int[].class ? ((int[]) object).length
: clazz == float[].class ? ((float[]) object).length
: clazz == long[].class ? ((long[]) object).length
: ((double[]) object).length;
}
}