JVM的组成:
- 类加载器
- 内存模型
- 字节码执行引擎
类加载器:
- 加载
- 将class文件读取到内存中,存放在方法区中,并在堆区创建一个java.lang.Class对象,之后用该Class对象进行相关的反射操作
- 验证
- 确保类被加载的正确性
- 准备
- 为类的静态变量(在方法区)分配内存并初始化默认值
- 解析
- 将类中的符号引用转变为直接引用
- 初始化
- 为类的静态变量赋予正确的初始值
- 注意: 类的初始化只有在使用的时候才会被初始化
类加载的时机:
- 创建类的实例时
- 访问该类的实例变量或者对该类的静态变量赋值时
- 调用该类的静态方法时
- 反射使用到该类时
- jvm启动时标明了该类为启动类
类加载机制:
- 全盘负责
- 当一个Class的类加载器加载该Class时,该Class所依赖和引用的其他Class也有该类加载器负责
- 双亲委派
- 当一个类加载时,会先试图让其父类加载器加载该class,如果不能再有自己的类加载器进行加载
- 好处:能够避免类的重复加载
- 缓存机制
- 所有被加载过的class都会被缓存,下次使用到该class时,会先去缓存中查找,没有的话,再重新加载该class,这就是为什么替换了修改的class后需要重启的原因。
内存模型组成:
- 方法区(线程共享)
- 堆区(线程共享)
- 虚拟机栈(线程私有)
- 本地方法栈(线程私有)
- 程序计数器(线程私有)
虚拟机栈:
- 每个线程执行都会给他分配一小块栈内存空间
- 在这一块内存空间中,每一个方法对应一个栈帧,方法之间的调用就是一个堆栈的过程
- 栈帧
- 局部变量表
- 方法局部变量存放的地方
- 局部变量可能是对象的引用,此时变量空间中存放的就是堆中该实例的引用地址
- 操作数栈
- 变量计算的区域
- 动态链接
- 将符号引用转变为直接引用
- 方法出口
- 可以理解为方法执行完成后的出口
- 局部变量表
- 栈帧
程序计数器:
- 记录该线程所执行的位置
本地方法栈:
- java调用本地方法时需要分配内存区域,则由本地方法栈提供
java8以后的方法区:
- 存放常量
- 存放静态变量
- 存放虚拟机加载的类信息以及编译后的代码
- 存放运行时常量池
java8以后元空间替代
继承关系下类加载顺序:
父类静态代码块
子类静态代码块
父类非静态代码块
父类构造方法
子类非静态代码块
子类构造方法