深入理解JVM之二
javac 编译
加载
将源码编译成字节码
字节码加载过程加载
将字节码加载到方法区
由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部
并存储在运行时内存区的方法区
(整个代码当成模版放在方法代码、方法数据、静态变量中)
类加载器
双亲委派机制:
主要是沙箱安全机制
防止核心类库被修改
避免类的重复加载
保证被加载类的唯一性
加载阶段把数据存放在方法区
类元信息包括
类型信息
类型的常量池
字段信息
方法信息
类加载器的引用
Class实例引用
方法表
常量池包括
字面量
文本字符串
final常量值
基本数据类型
其他符号引用
类和结构的全限定名
字段名称和描述符
方法名称和描述符
加载阶段实例化对象
将加载到的数据转换为一个与目标类型对应的java.lang.Class对象实例(对象存放在堆中,对象和方法区之间有一个指针引用)验证
这个Class对象在日后就会作为方法区中该类的各种数据访问入口
运行final是否合规准备
类型是否正确
静态变量是否合法等校验操作
为静态变量分配内存并初始化默认值解析
解析类的方法确保类与类之间的相互引用正确性,完成内存结构布局初始化
静态变量初始化
编译之后的数据存放在类的clinit方法中
该方法的作用就是初始化一个类中的变量
使用用户指定的值覆盖之前在准备阶段设定的初始值
任何invoke之类的字节码都无法调用<clinit>方法
因为该方法只能在类加载的过程中由JVM调用
父类优先初始化
如果父类还没有被初始化,那么优先初始化父类,但在<clinit>方法内部不会显示调用父类的<clinit>方法,由JVM负责保证一个类的<clinit>方法执行之前,它的父类<clinit>方法已经被执行
JVM保证初始化时的数据安全
JVM必须确保一个类在初始化的过程中使用
如果多线程需要同时初始化它
仅仅只能允许其中一个线程对其执行初始化操作
其余线程必须等待
只有在活动线程执行完对类的初始化操作之后
才会通知正在等待的其他线程
使用的过程就是在方法区和新生代本地方法栈中执行代码的过程卸载
GC垃圾回收本地方法栈
当前线程执行main方法,main方法中调用subfun方法局部变量表
在当前线程所在的本地方法栈中 有2个栈桢
sunfunc的方法出口指向了main的栈桢 说明执行完subfunc方法
就会回到main方法继续执行其他
程序计数器中存放的是线程执行方法代码的位置
用于存boolean、char、short、int、float、long、double等类型的数据操作数栈
以变量槽为最小单位 long和double需要两个slot 所以线程不安全
基本数据类型会直接存储值 引用数据类型会存放对象的引用
用于计算时的临时数据存储区、使用load执行将数据加载到此处动态链接
动态链接(多态,编译器没有指明 运行时才会指明)指向常量池中的方法引用方法出口
记录出栈地址即方法返回地址或异常地址运行时数据区
堆区分为新生代和老年代 空间比例1:2垃圾回收
新生代分为 Eden和S0和S1 空间比例 8:1:1
Eden区存放的都是早生夕死的对象
经过一次垃圾回收如果该对象和跟对象是可达的
那么就不会被回收 就转移到S0或S1区
S0和S1同一时刻只有有一个是有数据的
另外一个是空的
对象的动态年龄如果达到15就会转移到老年代
版权声明
本文仅代表作者观点,不代表博信信息网立场。