Java - JVM - 类的生命周期
-
概述
- 简述 JVM 里 类的生命周期
- 上次写了 30%, 居然丢了
- 难受, 又要重新写
- 回过头来看了一看, 感觉好些地方还是需要 调整
- 重写预定
-
类的生命周期
- 加载
- 使用
- 卸载
1. 加载
-
概述
- 类型的加载
-
大体流程
- 装载
- 连接
- 验证
- 准备
- 解析(可选的)
- 初始化
-
问题: 为啥解析是可选的
- 原因
- JVM 规范没有强制规定类型解析的时机
- 不是不做, 是说可以在其他的时候做
- 原因
-
问题: 这么关键的机制, 如何触发呢
- 时机
- 主动实例化
- 这个 ps 有讲
- 主动实例化
- 时机
-
特性: 类加载 与 依赖
- 子类 与 父类
- 如果子类加载, 则它的所有父类, 必须加载完毕
- 接口
- 接口没有这种规定
- 子类 与 父类
1. 装载
-
概述
- 由一个二进制流, 得到 方法区 类数据, 和 堆区 的 Class 实例
-
基本动作
- 获取 二进制流
- 结果
- 读入完整的二进制流
- 结果
- 解析二进制流, 并填入 方法区
- 结果
- 解析二进制流为 方法区 格式
- 把类型信息, 存在 方法区
- 结果
- 创建 Class 类实例
- 结果
- 在 堆 里创建引用
- 指向 方法区 类型信息
- 结果
- 获取 二进制流
-
问题1: 二进制流
-
概述
- 承载类型信息的二进制数据流
- 例如我们常见的 class 文件
- 承载类型信息的二进制数据流
-
来源
- class 文件
- 本地文件系统的 class 文件
- classpath, 这个我也不大懂
- 网络来源
- 压缩文件
- 数据库
- 动态编译
- 运行时编译 java 源文件
- 动态生成
- 运行时生成类的 class 文件
- 本地文件系统的 class 文件
- 其他
- class 文件
-
-
问题2: 解析二进制流
-
概述
- 将 class 文件转化为 方法区 的格式, 然后存在里面
-
解析
- 通常是按照 class 文件格式解析的
- 特殊时候, 也可以按别的格式解析
-
方法区
- 方法区有自己的结构
- 解析后的内容, 按方法区的结构存进去
-
-
问题3: 创建 Class 实例
-
概述
- 在 堆 里创建 类型对应的 Class 实例
-
Class 实例
- 指向 方法区 的类型信息
-
-
类加载器
-
启动类加载器
- 时机
- jvm 启动
- 作用
- 加载必须启动类
- 时机
-
自定义类加载器
- 时机
- 运行时
- 主动初始化
- 作用
- 加载其他类
- 关系
- 是 启动类加载器 的子类
- 时机
-
-
机制: 预加载
-
概述
- 类加载预料某个类型将要使用, 提前加载
-
区别
- 不是 完整加载
-
报错
- 如果装载出现了问题
- 会在 正式使用时, 才会报错
- 如果装载出现了问题
-
2. 连接
1. 验证
-
概述
- 验证 类型信息 是否符合 Java 语义, 以及 安全 等
- 这块不是重点, 我就不多讲了
- 其实是不懂
-
验证内容
-
格式
- 文件格式
- 长度确认
- 魔数
-
超类
- 除了 Object , 谁都有超类
-
符号引用
- 略
-
其他兼容
- final 类不能有子类
- final 方法不能被覆盖
- 确保类型和超类没有不兼容
-
其他
-
-
问题: 验证时如果需要其他类, 会触发 类加载吗?
- 结果
- 不会
- 不是主动使用
- 不会
- 结果
2. 准备
-
概述
- 类型信息通过验证, 分配内存
-
前提
- 主动使用
- 这个时候, 通常需要实例
- 所以说, 这个是 实例化 前的准备
- 主动使用
-
分配内存
-
变量
- 类变量会分配到对应的空间
- 并被赋予 默认值
- 各种 0, false, null
-
方法
- 可能会有方发表, 指向每个方法
- 特别是 继承的方法, 这样不用搜索超类
- 提高效率
- 特别是 继承的方法, 这样不用搜索超类
- 可能会有方发表, 指向每个方法
-
3. 解析
-
概述
- 将 符号引用 换成 直接引用
-
略
- 因为不懂
3. 初始化
-
概述
- 初始化
-
初始值
-
赋值
- 通常是 static 值
- 类的值
-
通过
- 类变量初始化语句
- static 变量的 赋值语句
- 静态初始化语句
- 静态代码块
- 类变量初始化语句
-
clinit 方法
-
来源
- 自动生成
-
内容
- static 复制语句
- 静态代码块
-
没有?
- 如果类型没有 static 内容
- 那就没有这个方法
- static final 被当做常量
- 也没有
- 如果类型没有 static 内容
-
-
初始化步骤
-
如果有超类
- 初始化超类
-
执行 clinit 方法
-
其他
- 接口不需要执行超类的 clinit 方法
-
-
多线程
-
场景
- 多个线程同时触发 类加载
-
结果
- 第一个执行初始化
- 其他全部等待
-
-
2. 使用
-
概述
- 其实就是对象的生命周期
-
内容
-
实例化
- 时机
- 类加载完成
- 方法
- 构造函数
- 时机
-
垃圾回收
- 这个以后单独说
-
3. 卸载
-
概述
- 类型生命周期的最后
-
对象
- 自定义装载器装载的类
-
时机
- 类型 不可触及
- 判断
- 没有明确引用
- 没有 class 类 实例
- 判断
- 类型 不可触及
ps
-
ref
- 深入 Java 虚拟机
- 一夜搞懂 | JVM 类加载机制
-
区别: 初始化 与 实例化
- 初始化
- 类加载的最后一步
- 实例化
- 类加载完成之后
- 结果是一个 类型的实例
- 初始化
-
主动使用 和 被动使用
-
主动使用
- 创建新实例
- 调用某个类的 静态方法
- 使用某个 类 或者 接口 的静态字段
- 除了 final 字段
- 反射方法
- 初始化某个类的子类
- 启动类
- 比如 main
- 启动时就必须使用
-
被动使用
-
-
classpath
- 寻址用, 但是我不大懂
-
对象生命周期
- 这个以后可以说说
- 实例化
- 垃圾收集
- 这个以后可以说说
-
类加载器
- 这个以后也可以说说
尽量尝试解释清楚; 自己校对能力有限, 如果有错误欢迎指出