怎么计算一个对象占用的内存
这应该是很多人在面试过程中遇到过的一个面试题~ 好多人在听到这么一个问题的时候都会瞬间懵逼,也有的人我记住了基本数据类型的占用内存和对象引用是4个字节大小。 这是正确的思考方向,但是不够全面。 下面就一步一步解析对象的组成有哪些。
对象分为三块存储区域
- 对象头(obect header)
对象头又包含两部分,总共占用大小12字节,如下: 对象标记(markOop)。存储对象在运行时的数据,如:哈希码、GC标记、锁信息、线程关联信息。在64位的jvm上占用 8个字节。需要补充一点的是,对象标记部分的存储格式是非固定的,具体要看jvm的实现。这样设计的目的是为了能存储 更多数据。
类元信息(klassOop)。存储对象指向的类元信息的首地址,也就是Klass的首地址。Klass存放在哪?在方法区。不知道 的可以去看看内存模型的介绍。类元信息占4个字节。
- 实例数据(Instance Data)
存储本类对象的实例成员变量和父类所有可见的成员变量
例子a:
public class BaseEntity {
private int mId;
}
复制代码
BaseEntity类只有一个私有的成员变量,所以它new出来的对象占用(4+12)字节。
例子b:
public class BaseEntity {
private int mId;
public long mLong;
protected double mDouble;
}
复制代码
public class SampleEntity extends BaseEntity {
}
复制代码
在BaseEntity内新增一个public的long成员变量和protected的double成员变量。所以它的实例对象占用(4+8+8+12)字节。
继承该BaseEntity得到子类SampleEntity。SampleEntity继承了父类的mLong和mDouble。所以实例对象占用(8+8+12)字 节。所有基本类型的占用大小本文后面会介绍。
- 对齐填充(Padding)
对象的存储空间的分配单位是8字节,当对象大小不是8的整数倍的时候需要填充对其。如上例子2,占用内存是28,不是8的整 数倍,这个时候需要填充4个字节,即32。所以实际分配的是32个字节。
9种基本数据类型
聊到这里,大概的思考流程是明了了,但是还需要补充基本数据类型占用内存的大小,以及在计算过程中实例对象该怎么处理。
什么,不是8种吗?对,8+1种。hhh~
前8种
类型 | 默认值 | 占用内存(字节) |
---|---|---|
boolean | false | 1 |
byte | (byte)0 | 1 |
char | '\u0000' | 2 |
short | (short)0 | 2 |
int | 0 | 4 |
long | 0L | 8 |
float | 0.0f | 4 |
double | 0.0d | 8 |
第9种
类型 | 默认值 | 占用内存(字节) |
---|---|---|
对象引用变量(Refrence Varible) | null | 4 |
假设对象引用变量简称refV,实际对象(Referred Object)为:refO;
refV存储引用指向的实际对象的存储地址的首地址。可以直接用==做等值判断。作为对象的引用变量,refV无论指向包装类,集合类,字符串类,自定义类,还是数组,均占用4字节。
refO最小占用12字节,即只有对象头,但是要对齐填充,所以最小分配的内存空间是16字节。
例子c:
public class RefDemo {
private short mShort;
private BaseEntity mBaseEntity;
private SampleEntity mSampleEntity = new SampleEntity();
}
复制代码
mShort占2字节;mBaseEntity未指向任何实例值为null且占用4字节;mSampleEntity指向了一个refO实例对象,注意 mSampleEntity只是个refV引用类型数据结构,表示的是refO实例对象的首地址,占用4个字节。补充一下,new 出来的 SampleEntity实例存放在堆里,new出来的RefDemo实例也是存放在堆里,RefDemo实例内的mSampleEntity引用变量指向 SampleEntity实例存储。
所以一个RefDemo实例占用(2+4+4+12)字节,填充对齐后是24字节,即实际分配了24个字节的存储空间。
结语:
计算一个对象的占用内存大小要注意:父类继承下来的基本数据类型和引用变量,一定要理解引用变量和引用变量指向的实例对象的区别。