面试题如下:
- 请解释一下对象的创建过程?
- 对象在内存中的存储布局?
- 对象头具体包括什么?
- 对象怎么定位?
- 对象怎么分配?
- Object o = new Object()在内存中占用多少字节?
================================================
对象的创建过程:
- 类加载:
a.class loading;
b.class linking:verification、preparation、resolution;
c.class initializing(类的静态变量初始值,执行静态代码块); - 类创建:
a.申请对象内存;
b.成员变量赋默认值;
c.调用构造方法(成员变量顺序赋初始值、执行构造方法语句)
对象的创建过程比较简单,第一步就是把要创建对象的类load到内存。第二步就是linking,这步分三个小步骤(verification、preparation、resolution),verification验证class文件,preparation把class文件中的静态变量赋默认值,resolution把class文件常量池的符号引用转换为内存地址。第三步申请内存空间创建对象,成员变量赋默认值,在字节码层面调用构造方法初始化,把成员变量设为初始值,然后执行构造方法,先执行super调用父类。
================================================
对象的内存存储布局:
分两种:普通对象、数组对象
普通对象:
- 对象头:在HotSpot里面MarkWord是8个字节。
- ClassPointer:开启类指针压缩(-XX:+UseCompressedClassPointers)时是4个字节,不开启是8个字节。
- 实例数据:根据实例中的成员变量决定。如果开启引用变量指针压缩(-XX:UseCompressedOops)则引用类型变量为4个字节,不开启为8个字节。
- Padding对齐:64位机器是按块读取,每块是8的倍数,对齐的作用就是保证对象的大小是8的倍数。
数组对象:
- 对象头:8个字节
- ClassPointer:同上
- 数组长度:4个字节
- 数组数据
- Padding对齐:同上
================================================
Object o = new Object()在内存中占用多少字节?
根据上面的内存存储布局,可以知道MarkWord8个字节,如果类指针压缩开启时类指针为4个字节,8+4=12,还有补齐,所以是16个字节。如果类指针压缩关闭时,MarkWord8个字节,类指针为8个字节,不需要补齐,所以是16个字节。
如果是一个new int[] 数组呢?
- 类指针压缩开启时:MarkWord8字节,类指针4字节,数组长度4字节,不需要补齐,一共16个字节。
- 类指针压缩关闭时:MarkWord8字节,类指针8字节,数组长度4字节,需要补齐4个自己,一共24个字节。
================================================
对象头具体包括什么?
这个图从markOop.hpp截取的:
根据markOop.hpp的描述,简单整理出下面的表格:
MarkWord中主要包括了锁的信息和GC信息。其中2位标识对象是否被锁,4位表示被回收了多少次。
================================================
对象怎么定位?
两种方式:
- 句柄池
- 直接指针
可以参考这篇文章:
https://blog.csdn.net/clover_lily/article/details/80095580
================================================
对象怎么分配?
简单整理了如下一个图
new一个对象的时候,先看一下栈上是否能分配的下,如果可以在栈上分配,那么分配到栈上,然后使用,栈内弹出,结束。如果栈上分配不下,先看一下这个对象是不是特别大,如果特别大,直接分配到堆内存,老年代。如果不是特比大,那么看是否能在TLAB上分配的下,如果分配的下,在TLAB上分配,如果分配不下,那么在伊甸区分配(TLAB也是伊甸区的一部分),然后进行GC回收,如果第一次没被回收,进入S1或S2,然后等待下一次GC,多次GC还未被回收的,达到一定年龄后,进入老年代,FGC结束。