类加载的时机
一个类从被加载到虚拟机内存中开始,到被卸载出内存为止,他的整个生命周期会经历:
加载 -》 连接 -》 初始化 -》 使用 -》 卸载
6种情况下必须立即对类进行初始化,初始化只会执行一次
- 遇到new,getstatic,putstatic,invokestatic四个字节码指令时,如果没有执行过初始化则先触发其初始化
- new对象
- 读/写一个类的静态字段(除开final修饰的)
- 调静态方法
- 反射,没有初始化,必须先触发其初始化
- 初始化类时,父类没初始化,必须先触发父类初始化
- 执行主类,先触发主类初始化
- 接口默认方法,如果有 这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
类加载过程
加载
在加载阶段,JVM完成三件事:
- 通过类的全限定类名获取该类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区运行时的数据结构
- 在内存中生成代表这个类的class对象,作为方法区这个类的各种数据的访问入口
类加载器
完成了通过类的全限定类名获取该类的二进制字节流这项工作
对于 任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每 一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相 等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个 Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
双亲委派模型
站在Java虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现[1],是虚拟机自身的一部分;另外一种就是其他所有 的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader
- 启动类加载器;负责加载java_home/lib下
- 扩展类加载器:负责加载java_home/lib/ext下
- 应用程序类加载器:负责加载classpath下
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加 载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的 加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请 求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。保证一个类只会被一个类加载器加载一次
连接
验证
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
准备
为类变量赋初始值,为实例变量赋零值
final变量编译时已经初始化完成
解析
符号引用转为直接引用
初始化
执行init<>,为实例变量初始化