1.1类加载的生命周期七个阶段:
加载: classpath、jar包、网络、某个磁盘位置下的类的class二进制字节流读进来,在内存中生成一个代表这个类的java.lang.Class对象放入元空间,此阶段我们程序员可以干预,我们可以自定义类加载器来实现类的加载;
验证:验证Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证虚拟机的安全;
准备:类变量赋默认初始值,int为0,long为OL,boolean为false,引用类型为null;常量赋正式值
解析:把符号引用翻译为直接引用;
初始化:当我们new一个类的对象,访问一个类的静态属性,修改一个类的静态属性,调用一个类的静态方法,用反射API对一个类进行调用,初始化当前类,其父类也会被初始化......那么这些都会触发类的初始化;
使用:使用这个类;
卸载:
1.该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例;
2.加载该类的ClassLoader已经被GC;
3.该类的java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法;
1.2继承时父子类的初始化顺序是怎样的?
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
1.3究竟什么是类加载器
在类“加载”阶段,通过一个类的全限定名来获取描述该类的二进制字节流的这个动作的“代码”被称为“类加载器”(Class Loader),这个动作是可以自定义实现的;
1.4 JVM有哪些类加载器?
站在Java虚拟机的角度来看,只存在两种不同的类加载器:
1、启动类加载器(Bootstrap ClassLoader),使用C++语言实现,是虚拟机自身的一部分;
2、其他所有的类加载器,由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader;
站在Java开发者的角度来看,自JDK 1.2开始,Java一直保持着三层类加载器架构;
BootstrapClassLoader(启动类加载器//根的类加载器)
ExtClassLoader(扩展类加载器)
AppClassLoader(应用程序类加载器)
!!!三个不是继承关系!!!
1.5 JVM中不同的类加载器加载哪些文件?
BootstrapClassLoader(启动类加载器//根的类加载器)
<JAVA_HOME>\jre\lib\rt.jar,resources.jar、charsets.jar
被-Kbootclasspath参数所指定的路径中存放的类库;
ExtClassLoader(扩展类加载器)
sun. misc.Launcher$ExtClassLoader,
<JAVA_HOME>\jre\lib\ext,
被java.ext.dirs系统变量所指定的路径中所有的类库;
AppClassLoader(应用程序类加载器)
sun.misc.Launcher$AppClassLoader
加载用户类路径((ClassPath)上所有的类库;
1.6你了解JVM类加载的双亲委派模型吗?
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当上一层类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到这个类)时,下一层类加载器才会尝试自己去加载;
两句话:自底向上检查是否加载成功,自顶向下尝试加载
1.7JDK为什么要设计双亲委派模型,有什么好处?
1、确保安全,避免Java核心类库被修改;
2、避免重复加载;
3、保证类的唯一性;
1.8可以打破JVM双亲委派模型吗?如何打破JVM双亲委派模型?
可以;
想要打破这种模型,那么就自定义一个类加载器,重写其中的loadClass方法,使其不进行双亲委派即可;
1.9如何自定义自己的类加载器?
1、继承 ClassLoader
2、覆盖findClass(String name)方法或者loadClass方法
1.10 ClassLoader中的loadClass()、findClass()、defineClass()区别?
loadClass()就是主要进行类加载的方法,默认的双亲委派机制就实现在这个方法中;
findClass()根据名称或位置加载.class字节码;
definclass()把字节码转化为java.lang.Class;
1.11加载一个类采用Class.forName(和 ClassLoader有什么区别?
字节码->类加载-->链接(验证-->准备-->解析)-->初始化-->使用-->卸载
Class.forName得到的class是已经初始化完成的;
Classloader.loaderClass得到的class是还没有初始化的;
1.12你了解Tomcat的类加载机制吗
可以看到,在原来的Java的类加载机制基础上,Tomcat新增了3个基础类加载器和每个
Web应用的类加载器+JSP类加载器;
3个基础类加载器在conf/catalina.properties 中进行配置:
Tomcat自定义了WebAppClassLoader类加载器,打破了双亲委派的机制,即如果收到类加载的请求,首先会尝试自己去加载,如果找不到再交给父加载器去加载
目的就是为了优先加载Web应用自己定义的类,我们知道ClassLoader默认的loadClass方法是以双亲委派的模型进行加载类的。那么Tomcat打破了这个规则,重写了loadClass方法,我们可以看到WebAppClassLoader类中重写了loadClass方法;
1.13为什么Tomcat要破坏双亲委派模型?
Tomcat是web容器,那么一个web容器可能需要部署多个应用程序;
1、部署在同一个Tomcat上的两个Web应用所使用的Java类库要相互隔离;
2、部署在同一个Tomcat上的两个Web应用所使用的Java类库要互相共享;
3、保证Tomcat服务器自身的安全不受部署的Web应用程序影响;
4、需要支持JSP页面的热部署和热加载;
1.14有没有听说过热加载和热部署,如何自己实现一个热加载?
热加载是指可以在不重启服务的情况下让更改的代码生效,热加载可以显著的提升开发以及调试的效率,它是基于Java的类加载器实现的,但是由于热加载的不安全性,一般不会用于正式的生产环境;
热部署是指可以在不重启服务的情况下重新部署整个项目,比如Tomcat热部署就是在程序运行时,如果我们修改了War包中的内容,那么Tomcat就会删除之前的War包解压的文件夹,重新解压新的War包生成新的文件夹;
1、热加载是在运行时重新加载class,后台会启动一个线程不断检测你的class是否发生改变;
2、热部署是在运行时重新部署整个项目,耗时相对较高;
如何实现热加载呢?
在程序代码更改且重新编译后,让运行的进程可以实时获取到新编译后的class文件,然后重新进行加载;
1、实现自己的类加载器;
2、从自己的类加载器中加载要热加载的类;
3、不断轮训要热加载的类class文件是否有更新,如果有更新,重新加载;