-
类的生命周期
-
加载:查找并加载类的二进制数据
- 通过一个类的全限定名来获取其定义的二进制字节流
- 将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构
- 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区中这些数据的访问入口
-
连接
-
验证:确保被加载类的正确性
-
文件格式验证
- 是否以0xCAFEBABE开头
- 主次版本号是否在虚拟机可接受范围内
- 常量池中的常量是否有不被支持的类型
- 。
- 。
-
元数据验证:对字节码描述信息进行语义分析,以保证其描述的信息复合Java语言规范
- 这个类是否有父类(除了Object以外所有的类都应该有父类)
- 这个类是否继承了不允许被继承的类
- 。
- 。
-
字节码验证:通过数据流和控制流分析,确定程序语义是合法的、复合逻辑的
-
符号引用验证:这个行为发生在虚拟机将符号引用转换为直接引用的阶段(解析阶段)用以确保解析动作能够正确执行
- 是否能通过字符串描述的全限定名,找到对应的类
- 类中是否存在复合其描述符和其简单名称的字段
- 。
- 。
-
-
准备:为类的静态变量分配内存,并将其初始化为默认值
-
解析:把类中的符号引用转换为直接引用
- 符号引用:符号引用是引用一组符号来描述所引用的目标,符号可以是任意形式的字面量,只要使用的时候能无歧义的定位到目标即可。
- 直接引用:直接引用是直接指向目标所在内存地址的指针、相对偏移量或一个可以间接定位到目标的句柄
-
-
初始化:为类变量赋予正确的初始值,其实就是执行类构造器的过程,主要是堆类变量进行初始化吗Java中堆类变量进行初始值设定有两种方式
- 声名类变量是指定的初始值 - 使用静态代码块为变量指定初值
-
JVM初始化步骤
- 假如这个类还没有被加载和连接,则程序先加载并连接该类
- 加入该类的直接父类还没有被初始化,则先初始化其直接父类
- 加入类中有初始化语句,则系统依次执行这些初始化语句
-
类初始化的时机
- 创建类的实例,new
- 访问某个类或接口的静态变量,或者对该静态变量进行赋值
- 调用该类的静态方法
- 反射
- 初始化某个类的子类,该类的父类也会被初始化
- Java虚拟机启动时被标明为启动类的类
-
-
使用
-
卸载
-
加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载必须按照这个顺序按部就班的开始(仅仅是开始),而解析阶段不一定。
-
-
类加载器、JVM类加载机制
-
-
启动类加载器
- 负责加载存放在JDK\jre\lib下的类库,启动类加载器无法被Java程序直接引用
-
扩展类加载器
- 该加载器是由sun.misc.launcher$ExtClassLoader来实现,负责加载JDK\jre\ext目录中的类
-
应用类加载器
- 该类加载器有sun.misc.launcher$AppClassLoader实现,她负责加载用户类路径所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定以自己的类加载器,那么默认使用应用类加载器
-
类加载的方式
- 命令行启动应用的时候有JVM初始化加载
- 通过Class.forName()方法动态加载
- 通过ClassLoader.loadClass()方法动态加载
-
双亲委派模型
- 如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派给他的父类加载器来完成,每一层次的类加载器都是如此,所有的类加载请求都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求,子类加载器才会尝试自己去完成加载
-
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)