JVM 面试题【初级】

异常和错误

有的面试官会挖这个坑:你说说遇到的错误java

  • Exception - 是异常,是代码里经常写的
  • Error - 是错误,像内粗溢出 OOM 这样的就是

坏就坏在你可能没注意说成另外一个了,的确有这样坏心眼的家伙(╯‵□′)╯︵┴─┴面试


怎么判断2个类是同一个类

  • 类名+包名相同
  • 加载这2个类的类加载器对象必须是同一个

类主动使用方式

类的使用能够分主动使用、被动使用,区别是被动使用类时,不会致使类的初始化,这会致使不少难以查找的 Bug 的算法

  • 经过new关键字、反射、clone、反序列化机制实例化对象
  • 调用类的静态方法时
  • 使用类的静态字段或对其赋值时
  • 经过反射调用类的方法时
  • 初始化该类的子类时(初始化子类前其父类必须已经被初始化)
  • JVM启动时被标记为启动类的类(简单理解为具备main方法的类)

建立对象的几种方式

  • new用new语句bai建立对象,这是最常du用的建立对象的方式。
  • Class.newInstance(),JDK9 之后过期了,只能使用public的无参构造方法
  • Construtcor.newInstance(xx) 有参无参均可以,也没有访问限制,这就是反射的好处
  • clone()
  • 反序列化手段: 好比IO,ObjectInputStream 的readObject() 方法,从本地文件,或者网络上获取
  • 第三方库Objenesis 利用字节码技术,动态生成class信息

这个你们了解,知道有这么回事就行,注意反射的2种方式差异,实际场景会用上数据库


从字节码的角度看对象的建立

public class Max {
    public void run() {
        String name = new String();
    }
}
复制代码

字节码:网络

public void run();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: invokespecial #3                  // Method java/lang/String."<init>":()V
         7: astore_1
         8: return
      LineNumberTable:
        line 11: 0
        line 12: 8
复制代码
  • 先走了一个new指令,会判断这个类加载没加载,加载完后去堆内存申请一块内存空间并返回该地址到操做数栈栈顶
  • dup 指令会把刚刚申请到的内存地址复制一份并保存到栈帧中操做数栈栈顶,这样以后操做数栈实际上就上下有2个相同的内存地址了
  • 执行String对象的初始化方法,这须要地址以便找到对象去执行方法,因此这里会消耗调一个内存地址的操做数
  • 而后把这个堆内存地址赋值给局部变量表里的name属性,因此才会有dup指令把内存地址复制一边

一般这个问题是和 volitale 保证有序性、单例模式的双判断写法为何还要加上 volitale 关键字这个问题联系在一块儿问的并发


对象建立的步奏

  1. 判断对象对应的类是否被加载、连接、初始化了 也就是new指令会去方法区常量池中定位到类的符号引用,检查这个符号引用是否被加载了
  2. 给对象分配内存空间,计算对象须要占用多大的内存空间
    • 堆内存规整:指针碰撞
    • 堆内存不规整:JVM要维护一个空闲列表,记录哪些内存块可用的,哪块是用过的,碎片化的问题,对应的是标记清除算法,伊甸园区回收完垃圾以后不作规整整理就这样,好比早期的CMS垃圾回收器
  3. 并发处理 堆内存是共享区域,就会有并发问题
    • CAS 失败重试,区域加锁保证原子性
    • 堆内存给每一个线程预先分配一块TLAB空间,也就是针对每一个线程预先给一块专属的内存空间,以防止并发问题,问题是这块空间不大,因此还须要上面CAS的配合,能够经过-XX:UseTLAB来设置
  4. 初始化分配到的内存空间(默认初始化) 也就是给属性赋一个初始的默认值,即使该属性在代码里设置值了,在这一步也会先给一个默认值,在以后的步奏里再赋指定值
  5. 设置对象头 对象头保存有对象的hashcode,GC信息,锁信息,所属类(方法区元数据地址)
  6. 显示初始化,执行init方法并初始化 具体能够看下一个问题的解答,init就是类的构造器

实际到代码spa

Dog dog = new Dog();
复制代码

new 关键字能够理解为1-5步,Dog() 至关于第6步线程

指针碰撞:

用过的内存在一边,没用过的内存在另外一边,中间临界点有一个指针。要是须要新分配内存空间了,只须要把指针移动执定大小便可。不过JVM是否采用这种分配方式,的看采用的垃圾回收器选择的是否是 Serial、ParNew 这样的压缩算法(标记整理)了。核心就是垃圾回收以后新生代伊甸园区的是规整过的才能这么玩 3d


对象属性赋值过程

  1. 属性的默认初始化
    给对象中的属性赋一个默认值,好比 int age = 100,在这一步 age=0
  2. 显示初始化/代码块初始化
    仍是拿 age 说事,到这里 age=100 了
  3. 构造器中初始化

上个问题6里面的init方法对应的就是这里的二、3部指针


对象访问的2种方式

有:句柄式、直接指针 2种

1. 句柄式

在JVM堆内存中,维护一个列表,存储每一个对象对应的内存地址,比如咱们在数据库搞一个索引表出来,你们要理解句柄的含义,就是索引,句柄是C++的内容,可是在java里好多地方还有她的身影

句柄的好处在这个索引表的内存地址是不变的,不论是对象的地址怎么变,对于别人来讲其实都是不变的

缺点是真用内存,访问效率低一些

2. 直接指针

这是目前 htospot 虚拟机访问对象的方式,不须要索引表,我直接记录你的内存位置,缺点是地址变了须要通知使用者

这道题仍是知道的好,还真就有人问。具体就是涉及到 GC 的标记清除算法上的优缺点,句柄式的能够并发的GC垃圾回收,由于句柄是不变的,我恩始终能够找到对象。直接指针GC咱们就必须中止其余线程,由于引用地址会变


空对象占内存多少

这是一道常问的面试题,咱们只考虑通常对象,对象实体是空的,Mark Word 占64位8个字节

类型指针默认是8个字节的,可是在开启指针压缩时会变成4个字节,JDK8 是默认开启的

参考对齐方式,对象的大小必须能被8整除。因此一个空对象默认占用内存大小是12个字节,算上对齐方法的化是16个字节

相关文章
相关标签/搜索