一 类加载机制
一、类加载顺序:
1、加载
加载二进制字节流,在内存中生成java.lang.class对象,二进制字节流可以从多个方面获取,如 网络 、数据库、zip等
2、验证
确保class字节流是满足虚拟机规范,并不会威胁虚拟机自身安全 这个阶段包括文件格式验证(比如 魔数 版本等),元数据验证 字节码验证
3、准备
正式为类变量分配内存并设置类变量初始值的阶段,这些变量使用的内存都将在方法区中进行分配,这里进行的内存分配只包括被static修饰的变量,(实例变量将在类实例化后分配内存),在准备阶段只进行初始化并不进行赋值(比如 static int a =123准备阶段初始值为0,而不是123),被final修饰的除外
4、解析
虚拟机将常量池中的符号引用变成直接引用的过程
5、初始化
执行类中定义的JAVA 代码,如:static代码块 static修饰的变量赋值 在以下几中情况下会进行类的初始化(初始化之前的几个阶段是必须新进行的 )
a、遇到 new getstatic putstatic invokestatic 这四条字节码指令时,如果类没有进行初始化需要先触发初始化
b、使用反射包的方法对类进行反射调用时,如果类没有初始化需要先触发其初始化
c、初始化一个类时如果其父类还没进行初始化
d、虚拟机启动时指定的main的类如果没有初始化需要触发初始化
e、动态语言支持,如果一个java.lang.invoke.MethodHandle实例最后解析结果REF-getStatic ,REF-putStatic,REF-invokeStatic的方法句柄所对应的类没有初始化 需要将其初始化
6、使用
7、卸载
其中加载 验证 准备 初始化 卸载 这五个阶段顺序确定,必须按照这种顺序按部就班的开始,解析在某些情况下可以在初始化之后进行
二、类加载器
1、 类加载器模型: 双亲委派
BootStapClassLoader <-- ExtensionClassLoader <-- ApplicationClassLoader <-- UserClassLoader
2、 双亲委派模型的
优点 :
a、安全
为啥安全 如系统库中的class只能被顶级类加载器(BootStrapClassLoder)加载,这样就避免了用户自己如果写了危害系统的和系统库相同名称的类 加载后危害系统
b、防止一个类多次加载
一个类可能从zip war 或者网络而来。防止混乱
缺点 :
a、效率较低 每次都得往上找父加载器,找不到父加载器才会用自己的加载器
3、打破双亲委派模型
a、使用线程上下文类加载器(setContentClassLoder)修改
package review.thread.lock; public class TestClassLoder { public static void main(String[] args) { Thread thread = Thread.currentThread() ; ClassLoader loder = thread.getContextClassLoader(); System.out.println(loder.toString()); thread.setContextClassLoader(new MyClassLoader()); loder = thread.getContextClassLoader(); System.out.println(loder); } }
b、复写loadeCLass
loadeClass方法就是先找父找不到再找子,再找不到才会用自己的类加载器,通过修改里面的逻辑就可以使用自己的加载器,
比方说简单的代码加密会修改class文件的字节就行修改,就可以通过loadeClass进行解密前提是你知道加密规则
自定义类加载器 : 建议实现findClass()方法