Java计算一个对象占用内存的大小

来源:互联网 发布:2组数据相关性分析 编辑:程序博客网 时间:2024/05/17 22:45

在C/C++中计算某一个基本类型或者对象占用内存大小的方法很简单,只要调用库里面的sizeof()操作符即可,但是在Java的API里面并没有给我们提供类似的方法。那么我们可不可以自己实现一个Java中的sizeof()方法呢?答案是肯定的。为了计算一个Java对象占用内存的大小,首先你得对Java对象的内存结构有所了解。如果你还不了解,请先阅读Java内存结构。

首先介绍一下sun.misc.Unsafe类,该类是Java中很神奇的一个类,这个类是用于执行低级别、不安全操作的方法集合。尽管这个类和所有的方法都是公开的(public),但是这个类的使用仍然受限,你无法在自己Java程序中直接使用该类,因为它的构造函数是私有的(private)。但是我们仍然能获得它的实例,方法如下:

Unsafe unsafe;try {        Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");        unsafeField.setAccessible(true);        unsafe = (Unsafe)unsafeField.get(null);    } catch (Throwable t) {        unsafe = null;    }
Unsafe类的更多介绍和用法可以参照http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

我们将使用Unsafe类的如下方法来实现Java中的sizeof()

//获得对象中一个非静态字段的偏移量public native long objectFieldOffset(java.lang.reflect.Field field);//获得数组对象第一个元素的偏移量public native int arrayBaseOffset(java.lang.Class aClass);//获得数组每一个元素所占内存大小public native int arrayIndexScale(java.lang.Class aClass);

sizeof()方法代码如下:

注意:不同的Java虚拟机,参数会各不一样,以下代码只适用于32位的HotSpot虚拟机。

package test;import java.lang.reflect.Array;import java.lang.reflect.Field;import java.lang.reflect.Modifier;import sun.misc.Unsafe;public class JUtil {/**对象头部的大小 */private static final int OBJECT_HEADER_SIZE = 8;/**对象占用内存的最小值*/private static final int MINIMUM_OBJECT_SIZE = 8;/**对象按多少字节的粒度进行对齐*/private static final int OBJECT_ALIGNMENT = 8;public static long sizeOf(Object obj) {//获得Unsafe实例Unsafe unsafe;        try {            Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");            unsafeField.setAccessible(true);            unsafe = (Unsafe)unsafeField.get(null);        } catch (Throwable t) {            unsafe = null;        }                //判断对象是否为数组        if (obj.getClass().isArray()) {            Class<?> klazz = obj.getClass();            int base = unsafe.arrayBaseOffset(klazz);            int scale = unsafe.arrayIndexScale(klazz);            long size = base + (scale * Array.getLength(obj));            if ((size % OBJECT_ALIGNMENT) != 0) {                size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);            }            return Math.max(MINIMUM_OBJECT_SIZE, size);        } else {        //如果数组对象则迭代遍历该对象的父类,找到最后一个非静态字段的偏移量            for (Class<?> klazz = obj.getClass(); klazz != null; klazz = klazz.getSuperclass()) {                long lastFieldOffset = -1;                for (Field f : klazz.getDeclaredFields()) {                    if (!Modifier.isStatic(f.getModifiers())) {                        lastFieldOffset = Math.max(lastFieldOffset, unsafe.objectFieldOffset(f));                    }                }                if (lastFieldOffset > 0) {                    lastFieldOffset += 1;                    if ((lastFieldOffset % OBJECT_ALIGNMENT) != 0) {                        lastFieldOffset += OBJECT_ALIGNMENT - (lastFieldOffset % OBJECT_ALIGNMENT);                    }                    return Math.max(MINIMUM_OBJECT_SIZE, lastFieldOffset);                }            }            //该对象没有任何属性            long size = OBJECT_HEADER_SIZE;            if ((size % OBJECT_ALIGNMENT) != 0) {                size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);            }            return Math.max(MINIMUM_OBJECT_SIZE, size);        }    }public static void main(String[] args) throws InterruptedException {System.out.println(JUtil.sizeOf(new MyClass()));//输出32Thread.sleep(100000);//阻塞线程,为了使用jmap工具}}
下面测试所用的类:

package test;public class MyClass {private byte a;private int c;private boolean d;private long e;private Object f; }
最后我们使用jmap工具来检查计算的结果是否正确


上图中的各项含义分别是:类的编号,实例的个数,占用内存的大小,类的名字。

可以看出sizeof()方法计算出来的结果是完全正确的。

0 0
原创粉丝点击