理解Java虚拟机第三版框架教案
2022-10-26 15:29:09 0 举报
AI智能生成
登录查看完整内容
理解Java虚拟机第三版框架教案
作者其他创作
大纲/内容
使用new关键字实例化对象的时候
调用一个类型的静态方法的时候
遇到下面的字节码指令的时候
对应的Java代码中的使用场景
1). 使用如下字节码指令的时候
2). 使用java.lang.reflect包的方法的对类型进行反射调用的时候
6种情况必须立即对类进行初始化
类的生命周期
1). 通过一个类的全限定名来获取定义此类的二进制字节流
2). 通过字节流代表的静态存储结构转化为方法区的运行时数据结构
该阶段用户程序可以通过自定义类加载器的方式进行局部参与<br>
注意
1. 加载
确保Class文件的字节流中包含的信息符合Java虚拟机规范的约束要求
是否以魔数开头: 0xCAFEBABE
常量池中的常量是否有不被支持的类型(检查常量的tag)
...
验证点
1). 文件格式验证
对字节码描述信息进行语义分析并对元数据信息进行语义校验
这个类是否有父类
这个类是否继承了不允许被继承的类
如果这个类是抽象类是否实现了其父类或接口中所要求的所有的抽象方法
2). 元数据验证
保证在任何时刻操作数栈的数据类型与质量代码序列都能配合工作
保证任何跳转指令都不会跳转到方法体以外的字节码指令上
3). 字节码验证
符号引用中通过自费重描述的全限定名是否能找到对应的类
在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段
4). 符号引用验证
4个阶段
2. 验证
类的静态变量(static)被分配内存并设置初始值的过程
3. 准备
Java虚拟机将常量池内的符号引用替换为直接引用的过程
4. 解析
<clinit>()是编译器自动收集类中的所有类变量赋值动作和静态语句块中的语句的合并
5. 初始化
类加载的过程
类加载器用于实现类的加载动作
类与类加载器
好处: Java中类随着他的类加载器仪器具备了一种带有优先级层次的关系
启动类加载器(Bootstrap ClassLoader)
扩展类加载器(Exension ClassLoader)
负责加载用户类路径(ClassPath)上所有的类库
应用程序类加载器(Application ClassLoader)
3类系统类加载器
双亲委派模型
类加载器
七. 虚拟机类加载机制
概念
变量槽
局部变量表
操作数栈的最大深度也在编译期就确定了最大深度 写入了Code属性的max_stacks数据项中
操作数栈
动态连接
方法返回地址
附加信息
运行时栈帧结构
解析
分派的调用过程将会揭示一些多态的最基本的体现
静态类型
变化的结果只有在运行期才可确定
实际类型
选择哪个重载版本
静态分派
在运行期间根据实际类型确定方法执行版本的分派过程称为动态分派
选择哪个重写版本
动态分派
单分派: 根据一个宗量对目标方法进行选择
多分派: 根据多于一个宗量对目标方法进行选择
单分派和多分配
分派
方法的调用
定义
动态类型语言
java.lang.invoke
引导方法
方法类型
名称
可以获得3项信息
invokedynamic指令
传统编译过程: 编写的源码程序-> 词法分析-> 语法分析-> 抽象语法树->中间代码-> 生成器-> 目标代码
解释执行过程: 编写的源码程序-> 词法分析-> 语法分析-> 抽象语法树->指令流-> 解释器-> 解释执行
字节码解释执行
八. 虚拟机字节码执行引擎
部署在同一个服务器上的两个web应用程序所使用的Java类库可实现相互隔离
部署在同一个服务器上的两个web应用程序所使用的Java类库可实现相互共享
服务器需要保证自身的安全不受部署的web应用程序的影响
支持jsp的web服务器
解决的问题
目录结构和类加载器
基于Java语言的动态模块化规范
OSGi
字节码生成技术
把高版本的JDK编写的代码放到低版本JDK环境中部署运行
Backport工具
Tomcat 正统的类家在架构
九. 类加载及子系统案例
代表性编译器产品
1). 准备阶段: 初始化初始化插入式注解处理器
2). 解析与填充符号表的过程
3). 插入式注解处理器的注解处理过程
数据流和控制流分析
解语法糖
字节码生成
4). 分析与字节码生成的过程
编译过程
Javac编译器
类型擦除
运行期间无法获取泛型的类型信息
缺陷
结论
提供"值类型"的语言层面的支持
值类型与未来的泛型
泛型
"==" 运算在遇到算术运算符时会自动拆箱
自动拆装箱
条件为常量的if语句可以实现条件编译
条件编译
Java语法糖
十. 前端编译与优化
解释器与编译器
被多次调用的方法
被多次执行的循环体
热点代码
基于采样的热点探测
基于计数器的热点探测
热点探测判定
提前编译器
即时编译器
被调用方法是否是热点代码
被调用法法是否大小合适
运行时方法是否可唯一确定
方法内联的条件
方法内联
栈上分配
标量
聚合量
标量替换
同步消除
代码优化
逃逸分析
公共子表达式消除
数组边界检查消除
编译器优化技术
代码中间层表示
字节码生成理想图: 可以按照字节码解释器的思路去理解它
生成理想图
理想图转化为机器码
理想图的操作
代码优化与生成
Graal编译器
十一. 后端编译与优化
Java内存模型规定了所有的变量都存在主内存中
主内存和工作内存
lock 锁定
unlock 解锁
read 读取
load 载入
use 使用
assign 赋值
store 存储
write 写入
针对8中操作的规则
不允许一个线程丢弃它最近的assign操作
不允许一个线程没有经过assign操作的值同步会主线程
一个新的变量只能在主内存中产生
一个变量同一个时刻值运行一条线程对其进行lock操作
内存间的交互操作
禁止指令重排序
两项特性
volatile规则
volatile型变量的特殊规则
原子性
可见性
有序性
程序次序规则
一个unlock操作先行发生于后面对同一个锁的lock操作
管程锁规则
对一个volatile变量的写操作先行发生于后面对这个变量的读操作
volatile变量规则
Thread对象的start()方法先行发生于此线程的每个动作
线程启动规则
线程中所有发生的操作都先行发生于线程的终止操作
线程终止规则
对线程的interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
线程中断规则
一个对象的初始化完成先行发生于它的finalize()方法的开始
对象的直接规则
传递性
先行发生原则
Java的每一个线程都直接映射到一个操作系统原生线程上来实现的
新建: 创建后尚未启动
新建 New
运行 Running
无限等待 Waiting
限期等待 Timed Waiting
阻塞 Blocked
已经终止的线程状态<br>
结束 Terminated
线程的状态
Java的线程
Java内存模型
十二. Java内存模型与线程
线程安全的定义
不可变
绝对线程安全
通常意义上讲的线程安全
相对线程安全
线程兼容
线程对立
共享操作数据分为5类
缺点
同步
实现同步的一种手段
互斥
被synchronized修饰的同步块对同一条线程来说是可重入的
synchrionized
等待可中断
公平锁
指一个ReentrantLock对象可以同时绑定多个Condition对象
锁绑定多个条件
ReentrantLock 可重入锁
ReentrantReadRriteLock
Lock接口
互斥同步(悲观锁)
ABA问题
非阻塞同步(乐观锁)
无同步方案
线程安全的实现方法
Java语言中的线程安全
自旋锁
锁消除
锁粗化
JVM使用CAS尝试把对象的Mark Wrod更新为锁记录的指针
加锁工作过程
轻量级锁
偏向锁
锁优化
线程安全
十三. 线程安全与锁优化
Java程序设计语言
不同硬件平台的Java虚拟机
Class文件格式
Java类库API
第三发Java类库
组成部分
Java小程序Applets小内存平台
Java Card
支持Java程序运行在移动终端上的平台
Java ME
支持桌面级应用的Java平台
Java SE
支持使用多层架构的企业应用的Java平台
Java EE
产品线
Java技术体系
分支主题
Java发展史
使用范围最广的JVM: HotSpot
面向移动和嵌入式市场: Mobile VM
BEA System 公司的 JRockit
IBM 的 IBM J9
......
Java虚拟机
Graal VM
无语言倾向
先进的即时编译器
向Native迈进
Java体系提供更多的接口
Java虚拟机不断优化增强
语言语法持续增强
展望Java技术的未来
一. 走进Java
程序计数器
会出现两类异常
Java虚拟机栈
本地方法栈
组成
Java堆
变化
方法区
运行时数据区域
在常量池中检查类的符号引用
指针碰撞
空闲列表
JVM采用CAS保证更新操作的原子性; 另一种方式: 把内存分配动作按照线程划分在不同的空间中进行
并发问题
堆中分配内存
对象头设置
此时该对象才算完全构造出来
执行实例构造函数<init>()方法
过程
对象的创建
运行时数据
通过这个指针确定该对象是哪个类的实例
指向类型元数据的指针
对象头
实例数据
对齐填充
对象的内存布局
通过栈上的reference数据来操作堆上的具体对象
优势
使用句柄访问
使用直接指针访问
访问方式
对象的访问定位
HostSpot虚拟机对象
-Xmx 最大堆内存; -Xms初始堆内存
Java堆溢出
-Xss 设置栈内存容量
虚拟机栈和本地方法栈溢出
String::intern()
字符串常量池
方法区溢出
OutOfMemoryError异常
二. 内存区域和内存溢出
哪些内存需要回收
什么时候回收
如何回收
GC要解决的问题
优点
无法解决对象之间互相引用的问题
引用计数法
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象
被同步锁持有的对象
GC Roots
可达性分析算法
强引用
软引用
弱引用
虚引用
引用的分类
任何对象的finalize()方法都只能被JVM自动调用一次
finalize()方法
该类所有的实例都被回收
加载该类的类加载器已被回收
该类对应的Class对象没有被其他地方引用
不再使用的类型
方法区的GC
哪些对象可以回收
绝大多数对象是朝生夕灭
弱分代假说
熬过多次GC过程的对象就越难消亡
强分代假说
跨代引用相对于同代引用来说仅占极少数
跨代引用假说
分代收集理论
实现简单
执行效率不稳定
内存空间碎片化问题
标记-清除算法
浪费内存空间
标记-复制算法
算法相对复杂
标记-整理算法
垃圾收集算法
并发和并行
OopMap
根节点枚举
抢先式中断
主动式中断
用户线程达到安全点并暂停的方案
安全点
安全区域
卡表: 针对"记忆集" 这种定义的一个具体实现
记忆集和卡表
为了解决 如何维护卡表元素状态
写屏障
白色: 未被收集器访问过
按照"是否被GC收集器访问过"这个条件标记为三种颜色
赋值器插入至少一条黑色对象到白色对象的引用
赋值器删除了全部从灰色对象到白色对象的直接或者间接引用
增量更新
原始快照(SATB)
并发的可达性分析
HotSpot算法细节
Serial 收集器
目前仅有他能跟CMS收集器配合
ParNew 收集器
吞吐量 = 运行用户代码时间/ (运行用户代码时间+运行垃圾收集的时间)
Parallel Scavenge 收集器
Serial Old 收集器
Parallel Old 收集器
Concurrent Mark Sweep 以获取最短回收停顿时间为目标的收集器
并发标记: 多线程并发从GC Roots的直接关联的对象遍历整个对象图的过程
并发清除: 并发清理掉已经被标记的对象
步骤
无法处理"浮动垃圾" 只能等到下次GC时被回收
CMS 收集器
跨Region引用对象的处理 : 记忆集和卡表 解决
并发标记阶段如何保证收集线程和用户线程互不干扰: 采用 原始快照算法实现
待解决的问题
4个步骤
可指定最大停顿时间
G1 收集器
目标是能在任何堆大小下都能把垃圾收集的停顿时间限制在十毫秒以内
并发清理: 清理掉那些整个区域中都不存在一个存活对象的Region
并发更新引用: 多线程并发执行 真正把堆中所有指向旧对象的引用修正到复制后的新地址
9个步骤
Shenandoah 收集器
并发重映射: 修正整个堆中指向重分配集中旧对象的所有引用
4大步骤
ZGC 收集器
垃圾收集器
运行应用的基础设施佮? 例如: 硬件规格
使用JDK的版本发行商?
收集器的权衡
< JDK9 使用 -XX:+PrintGC
>= JDK9 使用 -Xlog:gc
查看GC基本信息
< JDK9 使用 -XX:+PrintGCDetails
>= JDK9 使用 -Xlog:gc*
查看GC详细信息
< JDK9 使用 -XX:+PrintHeapAtGC
>= JDK9 使用 -Xlog:gc+heap=debuge
JVM收集器日志
如何选择合适的收集器
自动给对象分配内存
自动回收掉无用对象的内存
自动管理内存的目标
对象优先分配在Eden区域
大对象直接进入老年代
长期存活的对象进入老年代
动态对象年龄判定
空间担保
对象的生命旅程
内存分配与回售策略
三. 垃圾收集区与内存分配策略
jps: JVM进程状况工具
jstat: JVM统计信息监控工具
jinfo: Java配置信息工具
jmap: Java内存映像工具
jhat: 堆内存快照分析工具
生成当前时刻JVM的线程快照
jstack: Java堆栈跟踪工具
四. JVM性能分析工具
回收大块堆内存导致的长时间停顿
大内存必须要有64位Java虚拟机的支持
必须保证应用程序的足够稳定
相同的程序再64位虚拟机中消耗的内存比32位要大
Java虚拟机管理大内存
节点竞争全局资源
很难高效率利用某些资源池
32位Java虚拟机收到系统的内存限制
若干虚拟机独立部署应用
五. 调优案例分析域实战
无符号数
表
两种基础数据类型
每个Class文件的前4个字节称为魔数
次版本号
主版本号
魔数与Class文件的版本
WinHex 可以打开16进制的Class文件
常量池容量计数值
可以比喻为Class文件的资源仓库
字面量
被模块导出或者开放的包
类和接口的全限定名
字段的名称和描述符
方法的名称和描述符
方法句柄和方法类型
动态调用点和动态常量
符号引用
图示
项目类型
常量池
0x0001 : 是否为public类型
ACC_PUBLIC
0x0010: 是否被声明final
ACC_FINAL
ACC_SUPER
0x0200: 标识是个接口
ACC_INTERFACE
0x0400: 是否为abstract类型
ACC_ABSTRACT
0x1000: 标识这个类并非用户代码生成
ACC_SYNTHETIC
0x200: 标识这是一个注解
ACC_ACCOTATION
0x4000: 标识是个枚举
ACC_ENUM
0x8000: 标识是个模块
ACC_MODULE
访问标志
用于确定这个类的全限定名
类索引
父类索引
接口索引集合
字段表结构
字段表
用于描述类中的方法的相关描述
方发表集合
Code属性
Exceptions属性
描述Java源代码行号与字节码行号(字节码偏移量)之间的关系
LineNumberTable属性
描述栈帧中局部变量表的变量和Java源码中定义的变量之间的关系
LocalVariableTable属性
用于记录生成这个Class文件的源码文件名称
SourceFile属性
用于存储额外的代码调试信息
SourceDebugExtension属性
通知虚拟机自动给static的变量进行赋值
ConstantValue属性
记录内部类和宿主类之间的关联
InnerClasses属性
Deprecated属性
Synthetic属性
StackMapTable属性
Signature属性
Bootstrapmethods属性
MethodParameters属性
属性表集合
Class文件内容剖析
例如: iload指令用于从局部遍历表中加载int型的数据到操作数栈中
fload指令则是把float类型的数据加载到操作数栈
大多数的指令都包含其操作对应的数据类型信息
操作码助记符中都有特殊的字符
字节码与数据类型
加载和存储指令用于把数据在栈帧中的局部遍历表和操作数栈之间传递
将一个局部变量加载到操作数栈
将一个数值从操作数栈存储到局部变量表
将一个常量加载到操作数栈
wide
扩充局部变量表的访问索引指令
说明
加载和存储指令
加减乘除指令
求余取反位移
按位或
按位与
按位异或
iinc
局部变量自增
比较指令
运算符指令
可以把两个不同类型的数值类型进行互相转化
float -> double
宽化类型转换
必须显示地使用转化指令来完成
窄化处理转换
类型转换指令
new
创建类实例指令
创建数组的指令
把一个数组元素加载到操作数栈的指令
把一个操作数栈的值存储到数组元素中的指令
arraylength
取数组的长度的指令
检查类实例类型的指令
对象创建与访问指令
栈顶一个或两个元素出栈
swap
将栈顶的两个元素互换
操作数栈管理命令
有条件或无条件地从指定位置指令的已下条指令开始执行
条件分支
复合条件分支
无条件分支
控制转移指令
invokevirtual
invokeinterface
invokespecial
调用类的静态方法
invokestatic
用于在运行时动态解析出调用点限定符所引用的方法
invokedynamic
方法调用和返回指令
异常处理指令
同步指令
9大类
字节码指令
六. 类文件结构<br>
理解Java虚拟机第三版框架教案
0 条评论
回复 删除
下一页