jvm内部图
class文件就是字节码文件,从字节码文件之后的操作都是通过jvm实现的
jvm简图
![]()
详细图
类加载器
![]()
运行时数据区
方法区只有Hotspot才有
![]()
执行引擎
![]()
类加载的过程
分为三个阶段,加载,连接,初始化
其中链接阶段又分为验证,准备,解析
![]()
类加载作用
- 类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识。
- 类加载器只负责class文件的加载,至于它是否可以运行,则由执行引擎决定。
- 加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是class文件中常量池部分的内存映射)
classLoader扮演搬运者角色
![]()
在此图中Car.class是我们要加载的字节码文件,存储于磁盘上,作为类信息可以看做是一个产品的模板,通过实例化实例出来的car1,car2,car3可以看做是该模板的实例;Car.class被加载到JVM中成为Car Class后,Car class被称为DNA元数据模板,此时存储在方法区中。
在这个过程中负责将.class文件搬运到jvm中的角色就叫做类加载器ClassLoader
HelloLoader类加载过程
如果jvm中还没有加载过HelloLoader类,那么会通过ClassLoader将其加载进去
![]()
类加载详细过程
分为三个阶段,加载,连接,初始化
其中链接阶段又分为验证,准备,解析
1.加载
1.通过一个类的全限定名(全类名)获取定义此类的二进制字节流
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3.在内存中生成一个代表这个类的java.lang.class对象(类对象),作为方法区这个类的各种数据的访问入口加载.class文件的方式
- 从本地系统中直接加载
- 通过网络获取,典型场景: web Applet
- 从zip压缩包中读取,成为日后jar、war格式的基础·运行时计算生成,使用最多的是:动态代理技术工·由其他文件生成,典型场景:JSP应用
- 从专有数据库中提取.class文件,比较少见
- 从加密文件中获取,典型的防class文件被反编译的保护措施
2.类的链接
2.1.验证
目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。
主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
class文件的标识头:咖啡baby
2.2.准备
如图就算a被赋值为了1但是在准备阶段还是会被赋值为0,在最后的初始化阶段才会被重新赋值为1
字节码文件中就会被分配值
2.3解析
- 将常量池内的符号引用转换为直接引用的过程。
- 事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行。
- 符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《java虚拟机规范》的class文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。|I
- 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的
- CONSTANT class info、CONSTANT Fieldref info、CONSTANT Methodref info等。
3.初始化
初始化阶段主要任务是执行类构造器方法()的过程
类构造器方法并不是构造器的方法,主要任务是对静态代码重新赋值,才是此类的构造器
在先前的准备阶段中类中的静态变量都被赋值为默认值:(int是0,string是null),在中根据我们定义的值重新对其赋值,如下列代码中,中会对num和number赋值因为在准备阶段num和number已经被创建并赋值0值了,所以赋值结果根据语句在文件中出现的顺序执行,num先被赋值1再被赋值2,number先被赋值20再被赋值10
package com.atguigu.java; /** * @author shkstart * @create 2020 下午 6:01 */ public class ClassInitTest { private static int num = 1; static{ num = 2; number = 20; System.out.println(num); //System.out.println(number);//报错:非法的前向引用。 } private static int number = 10; //linking之prepare: number = 0 --> initial: 20 --> 10 public static void main(String[] args) { System.out.println(ClassInitTest.num);//2 System.out.println(ClassInitTest.number);//10 } }
方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。
()不同于类的构造器。(关联:构造器是虚拟机视角下的())若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕。
初始化子类静态B之前先初始化父类静态A
虚拟机必须保证一个类的 ()方法在多线程下被同步加锁。