什么是类的加载
将类的.class文件中的二进制数据流读入内存中,放在运行时方法区,再在堆中创建一个class对象,封装方法区内的数据结构。
一个类的生命周期
加载--连接--初始化--使用--卸载
其中连接又分为:验证--准备--解析
加载:
- 通过类的全限定名获取二进制字节流
- 将二进制字节流转化为方法区的运行时数据结构
- 在堆中生成class对象,作为访问这个方法区的入口
连接:
验证:
确保类被正确加载
确保读入二进制字节流时这些信息不会危害危害虚拟机自身安全
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
准备:
为类的静态static变量分配内存,初始化默认值
为类的静态变量赋默认值、分配内存。这个内存指的是运行时方法区的内存。
注意:这里只针对静态变量,实例变量会在对象初始化时在堆中分配。初始化默认值指的是为类初始化某一特定类型数据的初始值比如(0,null,false,etc.)
但是如果一个变量同时被static final修饰,name在准备阶段会直接赋指定的值,并不是0值
解析:
符号引用--》直接引用
符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任意形式的字面量,只要使用时能无歧义的定位到目标即可。符号与虚拟机实现的内存布局无关,引用目标并不一定加载到内存中(方法区)。比如一个类引用另一个类,或者调用另一个类的变量或方法,在编译为class文件的时候,jvm并不知道其所引用的类的具体的地址,也可能这个类还没有被加载,这时候只能通过符号先进行引用
直接引用:指向目标的指针、句柄。根据虚拟机的不同可能为指针也可能为句柄。但是这个引用都是直接可以定位到被引用对象的
初始化:
为类的静态变量赋值,对类变量进行初始化。为变量指定初始值有两种可能
1.初始化变量的初始值
2.使用静态代码块为类变量指定初始值
jvm初始化步骤
- 如果该类还未被加载连接,则先加载连接该类
- 如果该类父类未被初始化,先初始化父类
- 如果类中有初始化语句,则依次执行初始化语句
类初始化时机
- new一个对象的时候
- 访问类的静态变量
- 调用类的静态方法
- 反射
- 初始化某个类的子类,父类也会被初始化
- 虚拟机启动的时候指定初始化某个类
类生命周期的结束
- 执行了
System.exit()
- 程序运行结束
- 遇到异常结束
- jvm进程结束
双亲委派模型
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
- 系统类防止内存中出现多份同样的字节码
- 保证Java程序安全稳定运行