package org.graalvm.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
public final class ObjectSizeEstimate {
private static final int UNCOMPRESSED_POINTER_SIZE = 8;
private static final int = 16;
private static final int COMPRESSED_POINTER_SIZE = 4;
private static final int = 12;
public static ObjectSizeEstimate forObject(Object root) {
return forObject(root, Integer.MAX_VALUE);
}
public static ObjectSizeEstimate forObject(Object root, int maxDepth) {
return forObjectHelper(root, maxDepth);
}
private int ;
private int pointerCount;
private int primitiveByteSize;
private ObjectSizeEstimate() {
}
public ObjectSizeEstimate add(ObjectSizeEstimate other) {
ObjectSizeEstimate result = new ObjectSizeEstimate();
result.headerCount = headerCount + other.headerCount;
result.primitiveByteSize = primitiveByteSize + other.primitiveByteSize;
result.pointerCount = pointerCount + other.pointerCount;
return result;
}
public ObjectSizeEstimate subtract(ObjectSizeEstimate other) {
ObjectSizeEstimate result = new ObjectSizeEstimate();
result.headerCount = headerCount - other.headerCount;
result.primitiveByteSize = primitiveByteSize - other.primitiveByteSize;
result.pointerCount = pointerCount - other.pointerCount;
return result;
}
public int () {
return headerCount;
}
public int getPointerCount() {
return pointerCount;
}
public int getPrimitiveByteSize() {
return primitiveByteSize;
}
@Override
public String toString() {
return String.format("(#headers=%s, #pointers=%s, #primitiveBytes=%s, totalCompressed=%s, totalNonCompressed=%s)", headerCount, pointerCount, primitiveByteSize,
getCompressedTotalBytes(), getTotalBytes());
}
public int getCompressedTotalBytes() {
return headerCount * COMPRESSED_HEADER_SIZE + pointerCount * COMPRESSED_POINTER_SIZE + primitiveByteSize;
}
public int getTotalBytes() {
return headerCount * UNCOMPRESSED_HEADER_SIZE + pointerCount * UNCOMPRESSED_POINTER_SIZE + primitiveByteSize;
}
private void () {
headerCount++;
}
private void recordPointer() {
pointerCount++;
}
private void recordPrimitiveBytes(int size) {
primitiveByteSize += size;
}
private static ObjectSizeEstimate forObjectHelper(Object object, int maxDepth) {
EconomicMap<Object, Object> identityHashMap = EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE);
ObjectSizeEstimate size = new ObjectSizeEstimate();
ArrayList<Object> stack = new ArrayList<>();
ArrayList<Integer> depthStack = new ArrayList<>();
stack.add(object);
depthStack.add(0);
identityHashMap.put(object, object);
while (!stack.isEmpty()) {
Object o = stack.remove(stack.size() - 1);
int depth = depthStack.remove(depthStack.size() - 1);
size.recordHeader();
Class<?> c = o.getClass();
if (c.isArray()) {
size.recordPrimitiveBytes(Integer.BYTES);
if (o instanceof byte[]) {
size.recordPrimitiveBytes(Byte.BYTES * ((byte[]) o).length);
} else if (o instanceof boolean[]) {
size.recordPrimitiveBytes(Byte.BYTES * ((boolean[]) o).length);
} else if (o instanceof char[]) {
size.recordPrimitiveBytes(Character.BYTES * ((char[]) o).length);
} else if (o instanceof short[]) {
size.recordPrimitiveBytes(Short.BYTES * ((short[]) o).length);
} else if (o instanceof int[]) {
size.recordPrimitiveBytes(Integer.BYTES * ((int[]) o).length);
} else if (o instanceof long[]) {
size.recordPrimitiveBytes(Long.BYTES * ((long[]) o).length);
} else if (o instanceof float[]) {
size.recordPrimitiveBytes(Float.BYTES * ((float[]) o).length);
} else if (o instanceof double[]) {
size.recordPrimitiveBytes(Byte.BYTES * ((double[]) o).length);
} else {
for (Object element : (Object[]) o) {
size.recordPointer();
if (element != null) {
if (depth < maxDepth && !identityHashMap.containsKey(element)) {
identityHashMap.put(element, null);
stack.add(element);
depthStack.add(depth + 1);
}
}
}
}
} else {
while (c != null) {
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
if (!Modifier.isStatic(f.getModifiers())) {
Class<?> type = f.getType();
if (type == Byte.TYPE) {
size.recordPrimitiveBytes(Byte.BYTES);
} else if (type == Boolean.TYPE) {
size.recordPrimitiveBytes(Byte.BYTES);
} else if (type == Character.TYPE) {
size.recordPrimitiveBytes(Character.BYTES);
} else if (type == Short.TYPE) {
size.recordPrimitiveBytes(Short.BYTES);
} else if (type == Integer.TYPE) {
size.recordPrimitiveBytes(Integer.BYTES);
} else if (type == Long.TYPE) {
size.recordPrimitiveBytes(Long.BYTES);
} else if (type == Float.TYPE) {
size.recordPrimitiveBytes(Float.BYTES);
} else if (type == Double.TYPE) {
size.recordPrimitiveBytes(Double.BYTES);
} else {
size.recordPointer();
if (maxDepth > 1) {
f.setAccessible(true);
try {
Object inner = f.get(o);
if (inner != null) {
if (depth < maxDepth && !identityHashMap.containsKey(inner)) {
identityHashMap.put(inner, null);
stack.add(inner);
depthStack.add(depth + 1);
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new UnsupportedOperationException("Must have access privileges to traverse object graph");
}
}
}
}
}
c = c.getSuperclass();
}
}
}
return size;
}
public static ObjectSizeEstimate zero() {
return new ObjectSizeEstimate();
}
@Override
public int hashCode() {
final int prime = 31;
return headerCount + prime * (pointerCount + prime * primitiveByteSize);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof ObjectSizeEstimate) {
ObjectSizeEstimate other = (ObjectSizeEstimate) obj;
return headerCount == other.headerCount && pointerCount == other.pointerCount && primitiveByteSize == other.primitiveByteSize;
}
return false;
}
}