JVM面试基础
作者:互联网
JVM基础面试题
1、对象在内存中的存储布局?(对象与数组的存储不同)
- new一个普通对象,存储布局分为:markword、类型指针(class pointer)、实例数据(instance data)、对齐(padding)。
- markword占8个字节。
- 类型指针(new出来的是哪个类)占4个字节。
- 实例数据(new出来对象的成员变量)没有成员变量就是占0个字节,int(4个字节),long(8个字节)、boolean(1个字节)。
- 对齐:如果存储占的字节不能被8整除,对齐将补齐相应字节直至被8整除。
2、Object o = new Object()在内存中占用多少个字节?
markword占8个字节,类型指针占4个字节,实例数据占0个字节,一共12个字节,不能被8整除,所以对齐补上4个字节,一共16个字节。
3、对象头具体包括什么?
- 包括8个字节的markword以及4个字节的class pointer。
- markword又包括三大信息:1.锁信息 2.hashcode 3.GC信息。
4、对象怎么定位?
定位方法分为直接定位(直接指针)以及间接定位(句柄方式),现在的JVM基本都是采用的直接指针方式。
句柄定位的优缺点:
- 优点:Java栈里的变量存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)的时候只会改变句柄中的实例数据指针,而变量本身不需要改变(对象移动,变量地址不用变)。
- 缺点:增加了一次指针定位的时间开销。
直接指针的优缺点:
- 优点:节省了一次指针定位的开销。
- 缺点:在对象被移动的时候变量本身地址需要跟着修改。
5、对象怎么分配(栈上-线程本地-Eden-Old)
对象多少次经过安全区后进入老年代?默认的使用的PN+PO是15次,CMS是6次。
- 首先尝试在栈上分配,栈上分配有两个标准需要满足:①逃逸分析 ②标量替换,栈只有256个字节,所以还需要小对象,大对象之间进入老年代,然后被全栈回收(FGC)。栈上分配的对象会随着方法的结束栈帧弹出而消亡,无需GC处理。
- 如果不满足栈上分配也不满足大对象,优先使用线程本地分配缓冲区(Thread Local Allocation Buff)。在Eden区中,由于空间是线程共享,会导致多个线程同时去竞争Eden区中位置而降低效率。为了减少这种情况,每个线程会在Eden区中获取一块私有空间,线程上的私有小对象会优先分配到这里,避免多个线程同时竞争一个位置,提高效率。TLAB也是位于Eden区中。
- 线程本地分配缓冲区失败就会分配到新生代中的Eden区中
- 之后如果被GC清理掉,则对象消亡,如果没有清理掉,则进入S1区,再次经过一次垃圾回收,清理掉则对象消亡,未清理掉则对象年龄足够进入老年代之后全栈回收,年龄不够则进入S2区如此循环往复。
6、简单解释对象的创建过程?(半初始化)
-
一个Java对象的创建过程往往包括类初始化和类实例化两个阶段
-
简单分为三个步骤:
实例变量初始化:new一个对象,如果对象有成员变量,先给成员变量赋初始值,如int就是0,boolean即使false,引用类型就是null(先clinit一下)。
invoke special:调用对象构造方法设初始值
astore_1:建立关联
7、DCL单例模式(Double Check Lock)到底需不需要volatile?
DCL是在创建一个线程安全的单例模式广泛是使用的双重检查结构。代码如下:
private static volatile INSTANCE;
private Test() {
}
public static Test getInstance() {
//双重检查
if (Objects.isNull(INSTANCE)) { // Double Check Lock
synchronized (Test.class) {
if (Objects.isNull(INSTANCE)) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Test();
}
}
}
return INSTANCE;
}
volatile的内存语义:
- 当写一个volatile变量时,JMM(Java内存模型)会把该线程对应的本地内存中的共享变量值立即刷新回主内中
- 当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量
- 所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取
DCL单例模式(Double Check Lock)到底需不需要volatile?答案是需要的,因为CPU会出现指令重排序的现象,从上述代码可以看到,如果没有加volatile关键字的话,那么在锁内代码中,可能 第一个进来的线程发生指令重排序现象,首先执行
INSTANCE = new Test();
并且与变量进行链接,那么变量INSTANCE
还未执行构造方法,并未被赋值,所以之后的线程在第一重判断中INSTANCE!=null
直接返回半初始化的对象,出现并发,违背了单例模式的概念。所以在DCL模式中需要给单例对象加上volatile关键字,它的特性之一就是禁止指令重排序。
8、为什么hotspot不适用C++对象来代表java对象?
因为C++对象有一个virtual table 这个是java对象所不需要也没有的。会占用内存。
9、Class对象是在堆还是在方法区?
C++对象在方法区,Java对象在堆。
标签:字节,对象,基础,面试,线程,内存,JVM,volatile,变量 来源: https://www.cnblogs.com/Beginner-liu/p/16386966.html