加载.class文件的方式:
1.从本地系统直接加载 本地自带的
2.通过网络下载.class文件 Web Applet 小程序
3.从Zip、Jar等归档文件中加载.class文件 Jar/War包
4.从专有数据库中提取.class文件 JSP应用从专有数据库中提取.class文件
5.将Java源文件动态编译为.class文件 动态代理
6.从加密文件中获取 被反编译的保护措施
链接
验证
装载 准备 初始化
解析
虚拟机把Class文件加载到内存,并对数据进行校验、转换、解析和初始化,形成虚拟机可以直接使用的Java类型 即 java.lang.class
装载
查找和导入class文件
1.通过一个类的全限定名获取定义此类的二进制字节流
2.将这个字节流所代表的的静态存储结构转换为方法区的运行时数据结构
3.在java堆中生产一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口
装载完成之后内存有哪些信息
方法区:类的信息,静态变量,常量
堆:代表被加载类的java.lang.Class对象
即时编译之后的热点代码并在这个阶段进入方法区
链接
验证
确保Class文件中的字节流包含的信息完全符合当前虚拟机的要求,并且还要求我们的信息不会危害到虚拟机自身安全,导致虚拟机崩溃;
1.文件格式验证
处理字节流,确保当前字节流可以被当前虚拟机处理,该验证的主要目的是保证输入的字节流能正确的解析并存储到方法区内,这阶段的验证是基于二进制字节流进行的,只有经过该阶段的验证之后,字节流才会进入到内存的方法区中进行存储,后面验证都是基于方法区的存储结构进行的
2.元数据验证
对类的元数据信息进行语义校验,就是对Java语法校验,保证不存在不符合Java语法规范的元数据信息
3.字节码验证
进行数据流和控制流分析,确定程序语义是合法的,符合逻辑的,对类的方法进行校验分析保证被校验的类的方法在运行时不会做出危害虚拟机安全的行为,获取类的二进制字节流的节点是我们Java程序员最关注的阶段,也是操控性最强的阶段,因为这个阶段我们可以对我们的类加载器进行操作,比如我们想自定义类加载器进行操作用以完成加载,又或者我们想通过JAVA Agent来完成我们的字节码增强操作;
运行检查/跳转指令指向合理的位置,比如栈数据类型和操作码操作参数吻合
4.符号引用验证
这个是最好一个阶段的验证,它发生在虚拟机将符号引用转化为直接引用的时候(解析阶段),可以看作是对类自身以外的信息(常量池中的各种符号引用)进行匹配性的校验,符号引用验证的目的就是确保解析操作能正常执行;
准备
为类的静态变量分配内存,并将其初始化为默认值
这里不包含用final修饰的static,因为final在编译的时候就被分配了,准备节点会显示初始化
这里不会为示例变量,也就是没加static分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中、
这里如果有静态实例变量,这里只是在内存中分配了空间,并没有运行Java代码,这个静态实例变量是在初始化阶段才被赋值的;
解析
把类中的符号引用转换为直接引用:符号引用就是一组符号来描述目标,可以是任何自变量,直接引用就是直接指向目标的指针,相对偏移量或者一个间接定位到目标的句柄;
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类的接口、字段、类方法、接口方法、方法类型、方法句柄和调用字符七类符号引用进行
直接引用与虚拟机内存布局实现相关,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用是不一样的,如果有了直接引用,那引用的目标必定存在内存中;
初始化
初始化阶段是执行类构造器方法的过程 class中的init方法
1,声明类变量的指定初始值,就是在之前准备阶段初始值之后进行赋值操作
2.使用静态代码块为类变量指定初始值
Java初始化步骤
假如这个类还没有被加载和连接,则程序先进行加载并链接该类
假如该类的直接父类还没有被初始化,则先初始化父类
假如类中有初始化语句,则系统依次执行这些初始化语句
类加载器的层次
BootstrapClassLoader 启动类加载器
ExtClassLoader 扩展类加载器
AppClassLoader classPath路径下的类加载器
CustomClassLoader 自定义的类加载器,继承ClassLoder的子类并实现
双亲委派机制(父类委托机制)
向上查找类是否已经被类加载器加载,向下委托类加载器加载类
以类的全限定路径名为参数传入
直接去找AppClassLoader,查看当前类是否被加载过,如果被加载过就不进行二次加载,如果没有就查看ExtClassLoader是否加载过当前类,如果被加载过就不进行二次加载,如果没有就查看BootstrapClassLoader是否加载过当前类,如果被加载过就不进行二次加载,如果没有就查看BootstrapClassLoader是否可以加载当前类,如果可以就直接加载,如果不可以就去查看ExtClassLoader是否可以加载当前类,如果可以就直接加载,如果不可以就去查看AppClassLoader是否可以加载当前类,如果可以就直接加载,如果不可以就抛出ClassNotFound的异常;
打破双亲委派的方式
1.重写类加载器
2.SPI机制 Service Provider Interface
3.OSGI 热部署 热更新