如何计算一个对象在内存中占多少位(bit)

570 阅读2分钟

http接口返回一堆数据,从数据库内查询出一堆数据,程序接收,并封装成对象。那么问题来了,这些对象在内存内占多少空间?

有时候看一个http接口返回5M的数据就觉的吓人,总有一种调用次数多了服务器要爆了的感觉。当然这是开玩笑的,服务器怎么会爆,充其量宕机。

之前一直不知道怎么计算对象在内存上的大小。其实是思维太死,一个java对象,说到底就是由field与method组成的。method我先不管,field,这个就很好理解了,基础类型,包装类型,或者类类型。不过真的仔细追踪下去,其实不管什么类类型,到头来都是由基础类型组成的,君不见数据库也就存存数字、字符串…… ……
因此,以model类为例,这玩意就像一个套娃,不停的把基础类型包来包去。 而基础类型有多大,这是有规定的:

boolean类型 占 1 bit
byte类型 占8bit(1字节)
char类型 占16 bit(2字节)
short类型 占16 bit(2字节)
int类型 占32bit(4字节)
long类型 占64bit(8字节)
float 占32bit(4字节)
double类型 占64bit(8字节)

所以理论上讲,通过递归加反射是可以计算对象在内存中的占多少字节的。
下面是一个demo,可以做简单计算,但是碰到包含集合之类的对象的怕是不行,感兴趣的朋友可以一起实现:

github.com/J0-J0/demo/…

public class ObjectMemorySizeUtil {

    /**
     * 获取某对象在内存中占用的bit数
     *
     * @param object
     * @return
     * @throws IllegalAccessException
     */
    public static final int getObjectMemorySize(Object object) throws IllegalAccessException {
        if (object == null || object instanceof Class<?>) {
            return 0;
        }

        Class<?> objectClass = object.getClass();
        if (isPrimitive(objectClass)) {
            return getPrimitiveMemorySize(objectClass);
        }

        if (objectClass.isArray()) {
            return getArrayMemorySize(object);
        }

        Field[] fieldArr = objectClass.getDeclaredFields();
        if (ArrayUtils.isEmpty(fieldArr)) {
            return 0;
        }

        int result = 0;
        for (Field field : fieldArr) {
            field.setAccessible(true);
            result += getObjectMemorySize(field.get(object));
        }

        return result;
    }

    /**
     * 获取数据类型占用bit数
     *
     * @param object
     * @return
     * @throws IllegalAccessException
     */
    private static int getArrayMemorySize(Object object) throws IllegalAccessException {
        int length = Array.getLength(object);
        if (length == 0) {
            return 0;
        }

        int result = 0;
        for (int i = 0; i < length; i++) {
            Object obj = Array.get(object, i);
            result += getObjectMemorySize(obj);
        }

        return result;
    }

    /**
     * 是否原生类型
     *
     * @param objectClass
     * @return
     */
    private static boolean isPrimitive(Class<?> objectClass) {
        String classSimpleName = objectClass.getSimpleName();
        String[] wrapperClassArr = {"Boolean", "Byte", "Character", "Short", "Integer", "Long", "Float", "Double"};
        return objectClass.isPrimitive() || StringUtils.equalsAny(classSimpleName, wrapperClassArr);
    }

    /**
     * 获取原生类型占用bit数
     *
     * @param objectClass
     * @return
     */
    private static int getPrimitiveMemorySize(Class<?> objectClass) {
        String classSimpleName = objectClass.getSimpleName();
        if (StringUtils.equalsIgnoreCase(classSimpleName, "boolean")) {
            return 1;
        } else if (StringUtils.equalsIgnoreCase(classSimpleName, "byte")) {
            return 8;
        } else if (StringUtils.equalsAny(classSimpleName, "Character", "char")) {
            return 16;
        } else if (StringUtils.equalsIgnoreCase(classSimpleName, "short")) {
            return 16;
        } else if (StringUtils.equalsAny(classSimpleName, "Integer", "int")) {
            return 32;
        } else if (StringUtils.equalsIgnoreCase(classSimpleName, "long")) {
            return 64;
        } else if (StringUtils.equalsIgnoreCase(classSimpleName, "float")) {
            return 32;
        } else if (StringUtils.equalsIgnoreCase(classSimpleName, "double")) {
            return 64;
        }
        return 0;
    }

    public static void main(String[] args) throws IllegalAccessException {
        ObjectMemorySizeUtil obj = new ObjectMemorySizeUtil();

        System.out.println(getObjectMemorySize(obj));
    }

    private int a;
    private char b;
    private int[] cArr = new int[4];
}