JVM类加载面试题
面试官一般会问JVM类加载相关的问题,面试题如下:
说说Java类加载机制?
如果加了final,是在类加载的什么时候分配的?
Java类加载器有哪些,为什么使用双亲委派模型?
什么时候会加载一个类?

Java类加载机制思维导图
什么是类加载机制?
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制

类加载的时机
类从被加载到虚拟机内存中到卸载出内存为止的生命周期7个阶段:加载、连接(验证,准备,解析)、初始化、使用、卸载

其中 加载、验证、准备、解析、卸载的顺序是确定的。初始化的顺序是不确定的,那什么情况下一个类会进行初始化呢?
根据虚拟机规范规定,有5种情况需要对类立即初始化:
遇到 new getstatic putstatic 或invokestatic这4条指令码的时候
使用java.lang.reflect包的方法对类进行反射调用的时候
初始化一个类的时候,如果发现他的父类还没有初始化,那么先要初始化他的父类
虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类
当使用jdk的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic 、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要触发其初始化
以上5种行为也被统称为主动引用。有主动引用就有被动引用,被动引用就是除了以上5种行为以外所有引用类的地方都不出触发初始化。
示例:
通过子类引用父类的静态字段,不会导致子类初始化
通过数组定义类引用类,不会触发此类的初始化
常量在编译阶段会存入调用类的常量池种,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
接口初始化与类是存在区别的,区别就是上边5种行为种中的第3种,区别如下:
类:当一个类初始化的时候要求其父类全部初始化
接口:接口初始化时并不要求其父类全部完成初始化,仅当真正使用到父接口的时候(如引用接口中定义的常量)才会初始化
类加载的过程
前边已经说过类加载生命周期的7个阶段,其中加载、连接(验证,准备,解析),初始化是在运行期进行的。
运行期特性:

加载阶段:
加载完成后,二进制流按虚拟机格式存储在方法区
验证阶段:
确保Class文件的字节流中的信息符合虚拟机的要求
不会危害虚拟机自身安全
本阶段会大致完成下面4个阶段的验证动作:

准备阶段:
为类变量(被static修饰的变量)分配内存,并设置初始值
被final修饰的类变量初始值设置为指定的值
解析阶段:
虚拟机将常量池中的符号引用替换为直接引用的过程
初始化阶段:
类加载过程最后一步,前边已经说过类加载的时机,需要掌握
类加载器和双亲委派模型
类加载器有四种:

启动类加载器:
虚拟机本身的,c++语言实现的
加载java_home/lib下的类
加载 -Xbootclasspath指定路径下的类
系统属性sun.boot.class.path指定目录下的jar包
扩展类加载器(Extension ClassLoader):
加载java_home/lib/ext下的类
加载被java.ext.dirs系统变量所指定路径下的类
应用程序类加载器(Application ClassLoader):
由 Sun的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径java -classpath或-Djava.class.path变量所指的目录下的类库加载到内存中。开发者可以直接使用系统类加载器。
双亲委派模型:

以上这种层次关系被称为双亲委派模型
注:非强制约束模型,只是一种推荐模型
工作过程:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类
总结
以上就是与Java类加载机制相关知识点的笔记,最后留个问题:扩展类加载器的父加载器是启动类加载器吗?




