Skip to content

Instantly share code, notes, and snippets.

@semperos
Forked from bnyeggen/SizeOf.java
Last active December 30, 2015 19:49
Show Gist options
  • Save semperos/7876843 to your computer and use it in GitHub Desktop.
Save semperos/7876843 to your computer and use it in GitHub Desktop.
;; Just a start
(defn size-of
"Estimate size of a data structure"
[x]
(letfn [(boxed-size [^Class c]
(condp = c
(Class/forName "java.lang.Boolean") 1
(Class/forName "java.lang.Byte") 1
(Class/forName "java.lang.Character") 2
(Class/forName "java.lang.Short") 2
(Class/forName "java.lang.Integer") 4
(Class/forName "java.lang.Long") 8
(Class/forName "java.lang.Float") 4
(Class/forName "java.lang.Double") 8
arch-word-size))
(size-prim [^Class c]
(condp = c
Boolean/TYPE 1
Byte/TYPE 1
Character/TYPE 2
Short/TYPE 2
Integer/TYPE 4
Long/TYPE 8
Float/TYPE 4
Double/TYPE 8
Void/TYPE 0
arch-word-size))
(round-up-size [^long s]
(/ (+ s (dec arch-word-size)) (* arch-word-size arch-word-size)))
(size-inst [^Class c]
(let [fields (.getDeclaredFields c)
interfaces (.getInterfaces c)
size (reduce (fn [state field]
(if (and (not (.isInterface c))
(not= (bit-and (.getModifiers field)
java.lang.reflect.Modifier/STATIC)
0))
(+ state (size-prim (type field)))
state))
0 fields)
size (if-not (nil? (.getSuperclass c))
(+ (size-inst (.getSuperclass c)))
size)
size (reduce (fn [size interface]
(+ size (size-inst interface)))
size interfaces)]
size))
(deep-size-array [o]
(let [length (Array/getLength o)]))]
(size-inst (class x))))
import java.lang.reflect.*;
//This is still basically an estimate, but should be reasonably accurate.
public class SizeOf {
//4 bytes on 32 bit JVM
public static final int REFERENCE_SIZE = 8;
public static int deepSize(boolean b) { return 1; }
public static int deepSize(byte b) { return 1; }
public static int deepSize(char c) { return 2; }
public static int deepSize(short s) { return 2; }
public static int deepSize(int i) { return 4; }
public static int deepSize(long l) { return 8; }
public static int deepSize(float f) { return 4; }
public static int deepSize(double d) { return 8; }
public static int shallowSize(boolean b) { return 1; }
public static int shallowSize(byte b) { return 1; }
public static int shallowSize(char c) { return 2; }
public static int shallowSize(short s) { return 2; }
public static int shallowSize(int i) { return 4; }
public static int shallowSize(long l) { return 8; }
public static int shallowSize(float f) { return 4; }
public static int shallowSize(double d) { return 8; }
//Size of underlying wrapped primitive type
public static int boxedSize(Class c){
if(c == Boolean.class) return 1;
if(c == Byte.class) return 1;
if(c == Character.class) return 2;
if(c == Short.class) return 2;
if(c == Integer.class) return 4;
if(c == Long.class) return 8;
if(c == Float.class) return 4;
if(c == Double.class) return 8;
return REFERENCE_SIZE;
}
//JVM isn't "guaranteed" to align to boundaries like this, but it's usually
//a good guess for big long-lived nested on-heap datastructures.
public static int roundUpSize(int s) {
return (s + (REFERENCE_SIZE - 1)) / REFERENCE_SIZE * REFERENCE_SIZE;
}
//Size of one instance, excluding any data it holds a reference to
private static int sizeInst(Class c) {
final Field[] flds = c.getDeclaredFields();
int sz = REFERENCE_SIZE;
for (int i = 0; i < flds.length; i++) {
final Field f = flds[i];
if (!c.isInterface() &&
(f.getModifiers() & Modifier.STATIC) != 0)
continue;
sz += sizePrim(f.getType());
}
if (c.getSuperclass() != null)
sz += sizeInst(c.getSuperclass());
final Class[] cv = c.getInterfaces();
for (int i = 0; i < cv.length; i++)
sz += sizeInst(cv[i]);
return roundUpSize(sz);
}
private static int sizePrim(Class t) {
if (t == Boolean.TYPE) return 1;
else if (t == Byte.TYPE) return 1;
else if (t == Character.TYPE) return 2;
else if (t == Short.TYPE) return 2;
else if (t == Integer.TYPE) return 4;
else if (t == Long.TYPE) return 8;
else if (t == Float.TYPE) return 4;
else if (t == Double.TYPE) return 8;
else if (t == Void.TYPE) return 0;
else return REFERENCE_SIZE;
}
private static int shallowSizeArr(Object obj, Class c) {
final Class ct = c.getComponentType();
final int len = Array.getLength(obj);
if (ct.isPrimitive()) {
return len * sizePrim(ct) + REFERENCE_SIZE + 4;
}
else {
int sz = REFERENCE_SIZE + 4;
for (int i = 0; i < len; i++) {
sz += REFERENCE_SIZE;
final Object obj2 = Array.get(obj, i);
if (obj2 == null) continue;
final Class c2 = obj2.getClass();
if (c2.isArray()) sz += shallowSizeArr(obj2, c2);
}
return roundUpSize(sz);
}
}
private static int deepSizeArr(Object obj){
final int len = Array.getLength(obj);
if (obj.getClass().getComponentType().isPrimitive())
return roundUpSize(len * sizePrim(obj.getClass().getComponentType()) + REFERENCE_SIZE + 4);
int sz = REFERENCE_SIZE + 4;
for(int i=0;i<len;i++){
sz += deepSize(Array.get(obj, i), false) + REFERENCE_SIZE;
}
return roundUpSize(sz);
}
public static int shallowSize(Object obj) {
if (obj == null) return 0;
final Class c = obj.getClass();
if (c.isArray()) return shallowSizeArr(obj, c);
else return sizeInst(c);
}
//Lazy sequences in Clojure use methods to calculate additional elements,
//so using them with this doesn't fully traverse - it just tells you how
//much has been actually stored
public static int deepSize(Object obj){
return deepSize(obj, false);
}
//Retained size of fields for the object contained in its superclasses
//This logic could be just rolled into deepSize, which would then takes an
//explicit class
private static int deepSuperClassSize(Object o, Class c){
if (o==null || c==null) return 0;
final Class superclass = c.getSuperclass();
if (superclass == null) return 0;
final Field[] fields = superclass.getDeclaredFields();
int sz = 0;
for (final Field f: fields) {
if ((f.getModifiers() & Modifier.STATIC) != 0)
continue;
final Class fieldClass = f.getType();
try {
f.setAccessible(true);
sz += REFERENCE_SIZE + deepSize(f.get(o), fieldClass.isPrimitive());
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return sz + deepSuperClassSize(o, superclass);
}
//Flag tells you when you had to box something
public static int deepSize(Object obj, boolean primitive) {
if (obj == null) return 0;
final Class c = obj.getClass();
if (primitive) return boxedSize(c);
if (c.isArray()) return deepSizeArr(obj);
//This excludes inherited fields, which we need to check separately
//Interfaces don't have fields, so we can ignore them
final Field[] flds = c.getDeclaredFields();
int sz = 0;
for (final Field f:flds) {
//Do not count static fields
if ((f.getModifiers() & Modifier.STATIC) != 0){
continue;
}
final Class fieldClass = f.getType();
try {
//Only need to do this on private fields, but easier to do it
//for everything than try-catch-modify-try-catch-throw
f.setAccessible(true);
//f.get(obj) returns an Object (boxing if needed), so we
//manually flag it to avoid recurring forever
sz += REFERENCE_SIZE + deepSize(f.get(obj), fieldClass.isPrimitive());
} catch (IllegalAccessException e){
throw new RuntimeException(e);
}
}
return roundUpSize(sz + deepSuperClassSize(obj, c));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment