-
-
Save semperos/7876843 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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)))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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