Java对象内存结构
没什么可说的,可查看下方的相关文档Java对象的内存布局。
只是需要多补充一个知识点,数组对象头(object header) 在64位虚拟机中是要求8字节对齐的,而不仅仅是object整个对象要求8字节对齐。但是这个限制在32位虚拟机中是没有的
虚拟机信息常量
- JRE_IS_64BIT 是否是64位虚拟机
- NUM_BYTES_OBJECT_HEADER 对象(非数组对象)头占用的大小,不考虑内部属性以及对齐情况
- NUM_BYTES_ARRAY_HEADER 数组对象头的大小(无内容,但考虑对齐),相比上面,多了些内存来描述数组长度
- NUM_BYTES_OBJECT_ALIGNMENT 对象的对齐基线,一般是8字节
- COMPRESSED_REFS_ENABLED 是否开启指针压缩,jdk8后默认开启
- NUM_BYTES_OBJECT_REF 引用对象指针需要的字节个数
shallow size & retained size
大小测量分为三种:
-
浅层大小(Shallow Size) 不包括引用数组和实例的大小,而只包括相应指针的大小。 对于将所有对象都放在堆上的第一个对象集,浅层大小等于保留大小和深层大小。
-
保留大小(Retained Size)计算为浅层大小加上如果要删除当前对象集, 将被垃圾回收的所有对象的总大小。这个大小告诉您在一个对象集后面实际有多少内存。
-
深层大小(Deep Size)计算为浅层大小加上所有引用对象的总大小。 在极端情况下,这个值可能占整个堆的很大百分比。
官方文档介绍
lucene
中关于 shallow size的计算主要有以下几个方法
/** Returns the shallow size in bytes of the Object[] object. */
// Use this method instead of #shallowSizeOf(Object) to avoid costly reflection
// 获取对象(非基本类型)数组的shallowSize, 避免了反射,消耗更小
public static long shallowSizeOf(Object[] arr) {
// NUM_BYTES_ARRAY_HEADER 数组的对象头要求8字节对齐
// NUM_BYTES_OBJECT_REF 引用对象指针需要4字节
return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) NUM_BYTES_OBJECT_REF * arr.length);
}
---------------------
/**
* Return shallow size of any <code>array</code>.
*/
private static long shallowSizeOfArray(Object array) {
long size = NUM_BYTES_ARRAY_HEADER;
final int len = Array.getLength(array);
if (len > 0) {
Class<?> arrayElementClazz = array.getClass().getComponentType();
if (arrayElementClazz.isPrimitive()) {
// 基本类型
size += (long) len * primitiveSizes.get(arrayElementClazz);
} else {
// 对象指针
size += (long) NUM_BYTES_OBJECT_REF * len;
}
}
return alignObjectSize(size);
}
------------------------------------
/**
* Returns the shallow instance size in bytes an instance of the given class would occupy.
* This works with all conventional classes and primitive types, but not with arrays
* (the size then depends on the number of elements and varies from object to object).
* 1. 不处理数组,只处理对象(包括基本类型)
* 2. 计算对象shallow size时只处理成员变量,不考虑类变量, 还有考虑到类继承关系
* 3. 最后的结果要8字节对齐
* @see #shallowSizeOf(Object)
* @throws IllegalArgumentException if {@code clazz} is an array class.
*/
public static long shallowSizeOfInstance(Class<?> clazz) {
if (clazz.isArray())
throw new IllegalArgumentException("This method does not work with array classes.");
if (clazz.isPrimitive())
return primitiveSizes.get(clazz);
// maskword 在64位虚拟机下占用8字节,klazz 指针占用4字节,故对象头12字节
long size = NUM_BYTES_OBJECT_HEADER;
// Walk type hierarchy
for (;clazz != null; clazz = clazz.getSuperclass()) {
final Class<?> target = clazz;
final Field[] fields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() {
@Override
// 获取公开、私有属性都可以,但是排除继承属性
public Field[] run() {
return target.getDeclaredFields();
}
});
for (Field f : fields) {
if (!Modifier.isStatic(f.getModifiers())) { // 忽略静态属性
size = adjustForField(size, f);
}
}
}
return alignObjectSize(size);
}