面试题整理(5)

每天五道面试题!只记录答案,不标出源码,有什么不对的地方欢迎评论,共同进步。

1. 单例模式的双重检查锁为什么要加volatile?

  因为对象创建的过程不是一个原子性操作。

  对象创建过程分为三个步骤:1、申请内存。2、执行构造方法,给成员变量赋值。3、将创建的对象地址给引用变量。

  其中步骤2,3是可能发生指令重排序的,如果发生3在2之前,就会出现检查发现引用变量不为null,之后直接返回实例的情况,这种情况下的实例中的成员变量只进行了初始化,还没有执行构造方法赋值,可能出现空指针异常。

  加上volatile可以防止指令重排序,从而到达双重检查锁单例模式的安全实现。

2. 说一说对象在内存中的存储布局

面试题整理(5)_第1张图片

  对于HotSpot(JVM的一种实现,是使用范围最广的JVM),分为四个部分:

  1、对象头(markword),其中包含hashcode、锁的信息(synchronized锁升级信息)、GC信息(对象的年龄,4位,所以最大年龄就是2^4-1 = 15),如果是数组,会记录数组的长度。

  2、类型指针(class pointer),指向类型,比如一个cat对象会指向Cat.class。

  3、实例数据(instance data),对象中的成员变量。

  4、对齐(padding),如果前三者加起来不是八字节的倍数,会补齐到八字节的倍数。

3. 对象是如何定位的

  1、直接指针,访问快,是HotSpot的实现,引用指向堆中的对象,对象中存着类型信息。比如我们创建一个对象Cat cat = new Cat();,cat这个引用指向堆中cat对象,这个对象中又存着指向Cat.class的指针,JDK1.8之后,类型信息存放在元数据区。

  2、句柄池(指针池),也叫间接指针,节省内存空间,其他JVM的实现,引用指向一个对象池,对象池分两部分,这两部分不一定是连在一起的,一部分指向对象,另一部分指向类型信息。

4. 对象的分配过程

  按以下五个步骤分配:

  1、栈上分配(默认打开),需要做逃逸分析,如果没有逃逸会进行一系列优化,比如栈上分配、同步消除(比如StringBuffer只在当前方法内使用,就不会加锁同步)、标量替换(比如一个对象只有int,long这种基本数据类型的成员变量,会将该对象成员变量分解若干个被这个方法使用的成员变量所代替。这些代替的成员变量在栈帧或寄存器上分配空间)

  2、TLAB(Thread Local Allocation Buffer),每个线程都有一块私有空间,占用Eden区1%,一些很小的对象会分配到这里。

  3、如果对象很大,会直接分配到Old区。

  4、分配到Eden区。

  5、到一定年龄的对象会转到Old区。

5. Object o = new Object()占多少个字节(HotSpot)?

  1、对new Object(),markword占8字节,class pointer占4字节(开启压缩情况下),instance data占0字节(因为里面没有成员变量),padding占4字节(因为8+4不是8的倍数,要补齐到8的倍数,所以占4字节)。

  2、对Object o,4字节(开压缩情况下)。

  所以整个句子来说,占20字节。

  PS:JVM参数可以指定压缩或非压缩,默认是开启压缩的,但是目前一些server大于某个数就不开压缩了。不开压缩的话class loader占8字节(padding会变成0字节),Object o占8字节,总共就是24字节。

你可能感兴趣的:(面试,java,jvm)