参考《深入理解JVM》这本书,总结一下面试中常备问到的问题
21.类加载器、双亲委派模型
对于任意一个类都需要由加载它的类加载器和这个类本身一同确立其在jvm中的唯一性(即比较两个类是否相等先要比较这两个类是否由同一个ClassLoader加载)
jvm的角度来看有两种不同的类加载器:
1.启动类加载器,用C++实现,是虚拟机的一部分,负责启动<java_home>\lib目录中,或者用-Xbootclasspath参数指定的类
2.所有其他类的加载器(可以细分为扩展类加载器、应用程序类加载器),由java实现,独立于虚拟机,继承于java.lang.ClassLoader
扩展类加载器用来加载<java_home>\lib\ext目录中的
双亲委派模型:当一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,二十把这个请求委派给父类加载器去完成,只有当父加载器反馈无法加载这个请求(如:它的搜索范围中没有找到所需要的类),子类才会去加载(好处就是java类随着加载器一起构成了一个优先级关系,比如我同样定义了一个java.lang.Object类最终加载出来的还是系统自带的那个。。。也有可能编译通过,拒绝加载)
启动类加载器
|_扩展类加载器
|_应用程序类加载器
|_自定义类加载器
破坏双亲委派模型:基础类需要回调用户的代码(即要先加载用户类 比如 JNDI服务)
采用 线程上下文类加载器(父类请求子类的加载器去加载这个类)
22.运行时栈帧结构
栈帧是用来支持虚拟机进行方法调用和方法执行的数据结构,jvm运行时数据区中虚拟机栈的栈元素。栈帧中存储了:局部变量表、操作数栈、动态链接、返回地址。每个方法从调用到执行完成对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。只有栈顶的当前栈帧是有效的
23.局部变量表
1.是一组变量值存储空间。用来存储方法参数和方法内部定义的局部变量。在.class中已经由code属性的max_locals项决定了局部变量表的最大容量。
2.局部变量表容量以变量槽(Slot)为最小单位(32/64bit)
3.局部变量表中第0为存储的是:方法所属对象的实例引用,即 this~~
4.slot可以重用,即局部变量表的空间可能会比实际所有方法中变量需要占用的空间要小,有时候把不使用的对象手动赋null,可能会有助于GC
5.类变量(成员变量)(class内 方法外)是默认有初值的(在类准备阶段被赋0和nul了),而方法内的变量,即局部变量是没有初值的,不能直接调用
6.long和double占两个slot,可以用volatile关键字修饰,防止在多线程环境下的字撕裂。
24.操作数栈
1.操作数栈中每一个元素可以是任意的java类型
2.操作数栈中的元素数据类型必须与字节码执行指令序列严格匹配(eg:当前指令时iadd~两个int相加的指令,那么操作数栈顶必须是两个int类型!)
25.方法返回地址
方法返回地址八成存的是上一个方法PC计数器的下一条指令。方法执行后有两种方式退出方法。
1.正常退出
2.异常退出(不会给上层调用者提供返回值)
正常退出的情况下,需要把当前栈帧出栈、恢复上层方法的局部变量表和操作数栈、把返回值(如果有)压入栈顶。调整PC计数器让它指向方法调用指令的后一条指令