加载-----》连接(验证、准备、解析)------》初始化------》使用-------》卸载
注意
在Java虚拟机中,方法区是用于存储类的结构信息、常量池、静态变量、静态方法等数据的区域。其中包含了类的字节码、字段和方法的描述符、运行时常量池等。
而java.lang.Class对象则表示一个类的结构信息,包含了类的名称、父类、接口、字段和方法等。在运行时可以通过它来获取和操作类的相关信息。例如创建类的实例对象、获取类的方法信息、字段信息。
Class 对象和 _java_mirror 相互持有对方的地址,堆中对象通过 instanceKlass 和元空间进行交互
简单讲解一下,一个 Java 对象内存中存储为三部分:对象头(Header)、实例数据(Instance Data)和对齐填充 (Padding),对象头包括Mark Word和Klass Word。
Mark Word:用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等
Klass Word:类型指针,指向该对象的 Class 类对象的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例;
所以 Person对象头里存储着Person.class的地址,在运行时可以通过它来获取和操作类的相关信息。例如创建类的实例对象、获取类的方法信息、字段信息。
连接被分为三个阶段验证、准备、解析。
验证内容是否满足《Java虚拟机规范》
为静态变量分配内存并赋初始值(final修饰的静态变量,会直接赋值**),**使用的是方法区的内存。
主要是将常量池中的符号引用替换为直接引用。
还是按代码自上而下的顺序执行的
以下几种方式会导致类的初始化:
clinit指令在特定情况下不会出现,比如:如下几种情况是不会进行初始化指令执行的。
类实例化过程:父类的类构造器() -> 子类的类构造器() -> 父类的成员变量和实例代码块 -> 父类的构造函数 -> 子类的成员变量和实例代码块 -> 子类的构造函数
时机:执行了 System.exit() 方法,程序正常执行结束,程序在执行过程中遇到了异常或错误而异常终止,由于操作系统出现错误而导致Java 虚拟机进程终止
卸载类即该类的 Class 对象被 GC,卸载类需要满足3个要求:
在 JVM 生命周期类,由 JVM 自带的类加载器加载的类是不会被卸载的,自定义的类加载器加载的类是可能被卸载。因为 JVM 会始终引用启动、扩展、系统类加载器,这些类加载器始终引用它们所加载的类,这些类
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载
(Loading)、验证(Veri?cation)、准备(Preparation)、解析(Resolution)、初始化
(Initialization)、使用(Using)和卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统 称为连接(Linking)。这七个阶段的发生顺序如下图所示。
一、加载
在上述七个阶段中,包括了类加载的全过程,即加载、验证、准备、解析和初始化这五个阶段。 一、加载
“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一个阶段,在加载阶段,Java虚拟机需要完成以下三件事情:
加载阶段结束后,Java虚拟机外部的二进制字节流就按照虚拟机所设定的格式存储在方法区之中了,方法区中的数据存储格式完全由虚拟机实现自行定义,《Java虚拟机规范》未规定此区域的具体数据结 构。类型数据妥善安置在方法区之后,会在Java堆内存中实例化一个java.lang.Class类的对象,这个对 象将作为程序访问方法区中的类型数据的外部接口。
二、验证
验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。验证阶段大致上会 完成下面四个阶段的检验动作:文件格式验证、元数据验证、字节码验证和符号引用验证。
第一阶段要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。
第二阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合《Java语言规范》的要求。
第三阶段是通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的。
4. 符号引用验证:
符号引用验证可以看作是对类自身以外(常量池中的各种符号引用)的各类信息进行匹配性校验, 通俗来说就是,该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。
三、准备
准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值 的阶段。从概念上讲,这些变量所使用的内存都应当在方法区中进行分配,但必须注意到方法区本身是 一个逻辑上的区域,在JDK7及之前,HotSpot使用永久代来实现方法区时,实现是完全符合这种逻辑概 念的。而在JDK 8及之后,类变量则会随着Class对象一起存放在Java堆中,这时候“类变量在方法区”就完全是一种对逻辑概念的表述了。
四、解析
解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程,符号引用在Class文件中以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现, 那解析阶段中所说的直接引用与符号引用又有什么关联呢?
五、初始化
类的初始化阶段是类加载过程的最后一个步骤,之前介绍的几个类加载的动作里,除了在加载阶段用户 应用程序可以通过自定义类加载器的方式局部参与外,其余动作都完全由Java虚拟机来主导控制。直到 初始化阶段,Java虚拟机才真正开始执行类中编写的Java程序代码,将主导权移交给应用程序。
进行准备阶段时,变量已经赋过一次系统要求的初始零值,而在初始化阶段,则会根据程序员通过程序 编码制定的主观计划去初始化类变量和其他资源。我们也可以从另外一种更直接的形式来表达:初始化 阶段就是执行类构造器() 方法的过程。() 并不是程序员在Java代码中直接编写的方法,它是Javac编译器的自动生成物。
对象实例化过程,就是执行类构造函数对应在字节码文件中的() 方法(实例构造器),()
方法由非静态变量、非静态代码块以及对应的构造器组成。
静态变量、静态代码块、普通变量、普通代码块、构造器的执行顺序如下图:
具有父类的子类的实例化顺序如下: