Class加载过程&类加载器
1.Class cycle
- loading:
把class文件load进内存中去,它本来是class文件中一个个的二进制字节,装载完成就进入Linking阶段 - Linking:
- Verification:校验装进来的class文件是否符合class文件的标准,假如你装进去的不是CA FE BA BE,在这步就会被拒绝掉
- Preparation:是把class文件静态变量赋值,不是赋初始值,例如类int i类型的数字赋值5时。会先把值赋值为0,再由Initlalizing将5赋值给当前变量
- Resolution:是把常量池中用到的符号引用,要把他转化为直接的内存地址,直接可以访问到的内容
- Initlalizing:
将静态变量的的初始值这个时候才赋值,例如上面的 i在此时由0赋值为5
Loading过程
-
load过程中类加载器的双亲委派机制,如下图
-
双亲委派机制
- 这里的父加载器指的不是当前加载器的父类加载器,也不是当前加载器的加载器。指的是当前类的中的一个父加载器类型的成员变量
- 双亲委派过程是
- 在当前类加载器的缓存检查是否存在,如果没存在的话就向器父加载器中进检查是否已经加载过。以此类推知道到顶层的bootstrap都没有的话,就反向从上往下委托其子加载器加载,一层一层加载,如果最后没有加载出来就抛出ClassNotfound。
- 为什么要双亲委派机制
- 主要是为了安全,如果没有改机制,那么任何一个自定义加载器都可以讲任何类加载到内存中
- 假如我们自定义的加载器对String类进行加载,覆盖之前的加载,那么就会十分的危险。存在泄密的风险
- 是有了双亲加载就会不一样,我们的在加载java.lang.string的类时就会,在当前加载器中查找,没有的话就直接向上检查(不允许直接将其加载)。结果我们就直接在最上层的bootstrap中找到,所以就直接返回并不会在进行二次加载。
打破双亲委派
- 如何打破:重写loadClass()方法
- 什么时候打破过
- JDK1.2之前,自定义ClassLoader都必须重写loadClass()
- ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader执行
- 模块化的时候使用热启动和热部署
自定义类加载器
- 继承ClassLoad
- 重写模板方法findClass
- 调用defineClass
ClassLoader源码解析
- 类加载的过程就是,上来先找,我有没有加载过,这个catch里有没有,如果有就直接返回,没有就去父类加载,父加载器上来也是在catch中找。一直到顶层都没有,再向下层层委托加载,最终是当前加载器进行加载。
Lazyloading
- 懒加载,jvm并没有规定何时加载,jvm虚拟机的实现都是用的懒加载。懒加载就是我什么时候使用,才去加载这个类
- 但是虽然没有规定加载,却严格规定了初始化
- New getstatic putstatic invokestatic指令,访问final变量除外
- Java.lang.reflect对类进行反射调用时
- 初始化子类的时候父类首先初始化
- 虚拟机启动时,被执行的主类必须出啥化
- 动态语言支持java.lang.invoke.MethodHandle解析结果为REF_getstatic REF_putstatic REF_invokestatic方法句柄时,该类必须初始化