微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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次。

  1. 首先尝试在栈上分配,栈上分配有两个标准需要满足:①逃逸分析 ②标量替换,栈只有256个字节,所以还需要小对象,大对象之间进入老年代,然后被全栈回收(FGC)。栈上分配的对象会随着方法的结束栈帧弹出而消亡,无需GC处理。
  2. 如果不满足栈上分配也不满足大对象,优先使用线程本地分配缓冲区(Thread Local Allocation Buff)。在Eden区中,由于空间是线程共享,会导致多个线程同时去竞争Eden区中位置而降低效率。为了减少这种情况,每个线程会在Eden区中获取一块私有空间,线程上的私有小对象会优先分配到这里,避免多个线程同时竞争一个位置,提高效率。TLAB也是位于Eden区中。
  3. 线程本地分配缓冲区失败就会分配到新生代中的Eden区中
  4. 之后如果被GC清理掉,则对象消亡,如果没有清理掉,则进入S1区,再次经过一次垃圾回收,清理掉则对象消亡,未清理掉则对象年龄足够进入老年代之后全栈回收,年龄不够则进入S2区如此循环往复。

6、简单解释对象的创建过程?(半初始化)

  1. 一个Java对象的创建过程往往包括类初始化和类实例化两个阶段

  2. 简单分为三个步骤:

    实例变量初始化: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对象在堆。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐