参考javaguide(guide哥的原文链接)
类加载的生命周期
类加载过程?
加载->连接->初始化->使用->卸载
加载
- 通过全类名获取定义该类的二进制字节流
- 将字节流所代表的静态存储结构转换为方法区的运行时结构
- 在内存生成一个代表该类的class对象,作为方法区这些数据的方法入口
验证
验证字节码文件各种信息
- 文件格式,版本号,常量池是否支持
- 元数据验证:字节码描述信息,类等各种继承信息
- 字节码验证:字节码验证
- 符号引用验证
准备
分配内存,并且设定初始值
- 分配类变量但是不包括实例变量(在java堆中会处理)
- 静态变量(jdk7之后把静态变量和字符串常量池都放到了堆)和class对象被放到堆中
- 赋值给类变量为0除非是final+static那么就是赋值常量的值
解析
- 相当于即使把符号引用解析为直接引用
- 比如调用一个方法,但是符号引用是不知道那个位置的,系统需要知道位置在哪就会制造一张表存储类的所有方法位置,那么符号引用可以直接解析到方法在什么位置。比如有的类在其他文件你无法看到,那么只能是符号引用,只有通过解析,把类的位置注册到表中,那么就能够知道类的位置给符号引用赋值。
初始化
cinit实际上就是给静态变量赋值
new、 getstatic、putstatic、invokestatic
- new指令会创建一个对象初始化类
- getstatic初始化一个类,可以访问静态变量
- putstatic初始化类给类的静态变量赋值
- invokestatic初始化类调用静态方法
- Class.forName和newInstance反射可以初始化类
- MethodHandle和varHandle轻量级反射机制,需要先调用findStaticVarHandle来初始化类
- 默认方法也会初始化类
最后就是卸载类
- 类的对象都被gc了
- 该类没有在其他地方被引用
- 该类的类加载器实例已经被gc
双亲委派的好处?
- 避免类重复加载,被不同的类加载器加载就是不同的类
- 保证核心api不被修改
如果不想打破双亲委派?
-
重写findClass
-
如果想要打破那么就重写loadClass,因为loadClass源码就是在不断往上找父类的classLoader先加载其它类
-
如果想要打破那么就重写loadClass,因为loadClass源码就是在不断往上找父类的classLoader先加载其它类