JVM相关面试题
1.对象的创建过程
- class loading :将class文件加载进内存;
- class linking:(verification(验证),preparation(准备),Resolution(解析)) :
1.验证(Verification):校验加载进来的class文件是否符合class文件的标准;
2.准备(Preparation):为class文件的静态成员变量赋默认值;
3.解析(Resolution):将class文件的符号引用转换成直接引用; - class initialization:为静态成员变量赋初始值;
- 申请对象内存;
- 成员变量赋默认值;
- 调用构造方法
:
1.成员变量顺序赋初始值;
2.执行构造方法语句,首先会调用父类构造方法;
2.对象在内存中存储布局
在64位机器下:
- 普通对象:
(1).对象头:在HotSpot下称之为:markword 8字节(64位)
(2).ClassPointer指针:指向当前对象所对应的Class, -XX:+UseCompressedClassPointers 为4字节 不开启为8字节
(3).实例数据:引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节 ,Oops Ordinary Object Pointer
(4).Padding对齐:保证对象大小是8的倍数 - 数组对象:
(1).对象头:在HotSpot下称之为:markword 8字节(64位)
(2).ClassPointer指针:指向数据中对应对象的class
(3).数组长度:4字节
(4).数组数据
(5).Padding对齐:保证对象大小是8的倍数
对象头具体包括什么
- 要了解markword对象结构就需要去查看HotSpot的源码markOop.hpp文件,内部定义了markword对象头在32位和64位下不同位在不同锁状态下所表示的内容;对象头markword中的信息主要用于标记对象锁信息和GC信息以及hashcode,然后根据锁标志位的不同对象头中其他位表示的信息也会有所有不同;1.结尾两位:01表示无锁状态,00表示轻量级锁状态,10表示重量级锁状态,11表示被GC标记,然后在锁状态位01的时候,倒数第三位会标记锁是否为偏向锁,0表示无锁,1表示偏向锁;
- 注意:当一个对象计算过identityHashCode之后,不能进入偏向锁状态,因为计算过Hashcode值之后,这个值会被存在对象头前31位,而且偏向锁的线程ID需要这些位来记录,所以就不能再进入偏向锁状态了;

对象的定位
理解:由对象的引用定位到Java堆中的具体位置
在周志明老师的深入虚拟机一书中介绍了下面这两种主流方式来定位对象
- 句柄 : 对象引用指向的是一个句柄,这个句柄记录的是这个对象的实例数据的指针和对象类型数据的指针,然后由这两个指针能够定位到实例数据和类型数据;
- 直接指针:对象引用直接指向了实例数据和对象类型指针;HotSpot使用的是这种方式;
- 句柄的好处:对于对象移动比较友好,因为对象引用指向的是句柄,当对象发生移动时,只需要改变句柄的对象实例数据地址指针就以可以了,垃圾收集时移动对象是非常普遍的行为,所以使用句柄的方式对于GC来说更加友好;
- 使用直接指针方式最大的好处就是速度更快,他节省了一次指针定位的时间开销。
对象的分配
- 根据对象是否逃逸出当前方法,来判断能否栈上分配;栈帧弹出,对象销毁;
- 如果对象太大,Eden区无法容纳下直接分配到老年代;
- Eden区能容纳,则先进行线程本地(TLAB)分配,TLAB默认大小为Eden区的%1,判断能否分配下,能够分配则分配在TLAB中,不能则直接分配在Eden区中;
- YGC后,将对象分配到S0或者S1,对象过大分配不下,直接进入老年代,对象年龄到了也会进入老年代;
- 注意这里的年龄:
动态年龄:年龄从小到大进行累加,当加入某个年龄段后,累加和超过survivor区域TargetSurvivorRatio的时候,就从这个年龄段往上的年龄的对象进行晋升。
-XX:TargetSurvivorRatio目标存活率,默认为50%
Object对象在内存占用多少个字节?
16个字节
(1).对象头:在HotSpot下称之为:markword 8字节(64位)
(2).ClassPointer指针:指向当前对象所对应的Class, 开启-XX:+UseCompressedClassPointers 为4字节 不开启为8字节
(3).Padding对齐:保证对象大小是8的倍数
JVM 主要组成部分
类加载器(ClassLoader)
运行时数据区(Runtime Data Area)
执行引擎(Execution Engine)
本地库接口(Native Interface)
组件的作用
首先,类加载器将class字节码内容加载到内存中,并将这些静态数据转换成方法区运行时数据结构,然后在堆中形成代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口;
其次,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行;
于是,需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行;
最后,此过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。