第二章 JVM规范
JVM概述
- 虚拟机指的是:通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的计算机系统
JVM是通过软件来模拟Java字节码的指令集,是Java程序的运行环境
JVM规范
JVM规范为不同硬件平台提供了一种编译Java技术代码的规范
- 该规范使Java软件独立于平台,因为编译时针对作为虚拟机的“一般机器”而做
这个“一般机器”可用软件模拟并运行于各种现存的计算机系统,也可用硬件来实现
JVM规范定义的主要内容
字节码指令集(相当于CPU)
- Class文件的格式
- 数据类型和值
- 运行时的数据区
- 栈帧
- 特殊方法
- 类库(eg.对硬件)
- 异常
-
JVM规范中的虚拟机结构
如何学习JVM规范中的指令集
理解ClassFile结构
Class文件时JVM的输入,Java虚拟机规范中定义了Class文件的结构。Class文件是JVM实现平台无关、技术无关的基础
- Class文件是一组以8字节为单位的字节流,各个数据项目按顺序紧凑排列。
- 对于占用空间大于8字节的数据项,按照高位在前的方式分割成多个8字节进行存储
- 只有两种类型:无符号数、表
- 无符号数 u1
- 表 由无符号数和表复合数据类型 “_info”
- javap工具生成非正式的“虚拟机汇编语言”,格式如下
[ <>][ ]
- stack:方法执行时,操作栈的深度
- Locals:局部变量所需的存储空间,单位是slot
-
总结
根据classFile结构规范,去读每一小段,然后根据每一段的结构和索引去读出具体的内容。
可以看到虚拟机如何执行程序的,
虚拟机如何执行优化的。
可以直接操作字节码ASM
ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。
- ASM可以直接产生二进制Class文件,也可以在类被加载入虚拟机之前动态改变类行为,ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能根据要求生成新类。
目前许多框架如cglib、Hibernate、Spring都直接或间接地用ASM操作字节码
ASM编程模型
Core API
操纵字节码的功能基于ClassVisitor接口,三个,实现class生成和转换
- ClassReader解析一个类的class字节码
- ClassAdapter是ClassVIsitor实现类,实现要变换的功能
- ClassWriter 输出字节码
- 验证:确保被加载类的正确性
- 准备:分配内存
- 解析
-
类加载
双亲委派模型
破坏双亲委派模型
类连接
验证
类文件结构检查
- 元数据验证
-
准备
解析
类的初始化
就是为类的静态变量赋初始值,或者说是执行类构造器
方法的过程
1)如果类还没有加载和连接,就先加载和连接
2) 没有初始化父类,就先初始化父类
3) 类中存在类中初始化语句(static块)
4)接口的话,初始化一个类的时候,不会初始化他的接口,初始化接口的时候,不会初始化他的父接口。只有调用接口里的变量时,才会初始化接口。classLoader装载一个类,并不会初始化这个类,不是对类的主动使用。
类的使用方式 主动使用 初始化时机
1)创建类实例
2)访问某个类或接口的静态变量
- 静态方法
4)反射某个类
5)初始化某个类的子类
6)JVM启动的时候运行的主类
7)定义了default方法的接口,当接口实现类初始化时
- 被动使用
被动引用:通过子类去引用父类的静态字段
数组引用这个类
访问常量final不会初始化,本质上是从去常量池里拿的
类卸载
- 当Class对象不再被应用,那么Class对象的生命周期就结束了,对应的在方法区中的数据也会被卸载
- JVM自带类加载器装载的类,是不会卸载的,有用户自定义的类加载器是可以卸载的
-
第四章 内存分配基础
java的简化架构
运行时数据区
PC寄存器
- 每个线程拥有一个PC寄存器,是线程私有的,用来存储指向下一个指令的地址
- 在创建线程的时候,创建相应的PC寄存器
- 执行本地方法时,PC寄存器的值为undefined
- 是一个比较小的内存空间,是唯一一个在JVM规范中没有规定OutOfMemoryError的内存区域
- java栈
- 栈由一系列帧组成(因此java栈也叫java栈帧),是线程私有的
- 帧用来保存一个方法的局部变量、操作数栈(Java没有寄存器,所有参数传递都是用操作数栈)、常量池指针、动态链接、方法返回值等
- 每一次方法调用创建一个帧,并压栈,退出方法时候,修改栈顶指针就可以把栈帧中的内容销毁
- 局部表量表存放了编译期可知的各种基本数据类型和引用类型,每个slot存放32位数据
- 栈优点:存取数据比堆快,仅次于寄存器
- 站缺点:数据大小、生存期在编译期决定的,缺乏灵活性
- java堆
- 用来存放应用系统创建的对象和数组,所以线程共享Java堆
- GC主要管理堆空间,对分代GC来说,堆也是分代的
- 优点:运行期动态分配内存大小,自动进行垃圾回收
- 缺点:效率相对较慢
- 方法区
- 线程共享,通常用来保存类的结构信息(常量池、方法字节码)
- 通常和元空间关联在一起,但具体的跟JVM实现和版本有关
- JVM规范把方法区描述为堆的一个逻辑部分,但他有一个别称Non-heap,与Java堆区分开
- 运行时常量池
- 是Class中每个类和接口的常量池表,在运行期间的表现形式,通常包括: 类的版本、字段、方法、接口等信息
- 在方法区中分配
- 通常在加载到JVM之后就创建
- 本地方法栈
- 在jvm中支持native方法执行的栈就是本地方法栈
-
Java堆内存
概述
逻辑上连续,
Java堆式在运行期动态分配内存大小,自动进行垃圾回收
-
Java堆的结构
新生代:存放新分配的对象
- 年龄
- 老年代 比较大的对象
- 整个堆=
- 新生代=Eden+存货去
持久代 存Class、Method等原信息 从JDK8开始去掉了,取而代之的是元空间,元空间并不在虚拟机里面,而直接使用本地内存
对象的内存布局
对象的访问定位
访问方式取决于JVM的实现,句柄/类型指针
间接引用,慢一点
hotpot,速度快Java内存分配参数
Trace跟踪参数
GC日志格式
GC发生时间,也就是JVM从启动以来经过的秒数
- 日志级别信息,和日志类型标记
- GC识别号
- GC类型和说明GC原因
- GC前后容量
-
Java堆的参数
最小初始化堆,默认物理内存1/64
Xms: 1024倍数且大于1m
- -XX:inHeapSize
Runtime.getRuntime().totalMemory().
初试最大堆, 最小初始化堆1/4, - -Xmx:
- -XX:MaxHeapSize
使用MAT进行内存分析
把当前内存dump出来,
原因:
- 内存泄漏,只往上涨,不往下降低(不恰当的引用)
- 老年代/新生代
缺省为2
- -Xss: 每个线程的堆栈大小,通常几百k,决定了函数调用的额深度
递归
https://blog.csdn.net/qq_31331965/article/details/102465589
- 元空间的参数
第五章 字节码引擎
字节码引擎
字节码引擎概述
- 功能基本就是 输入字节码文件,然后对字节码进行解析并处理,最后输出执行的结果
实现方式可能有通过解释器直接解释执行字节码,或者是通过及时编译器产生本地代码,也就是编译执行,当然也可能两者皆有。
栈帧概述
栈帧用于支持JVM进行方法调用和方法执行的数据结构
- 栈帧随着方法调用而创建,随着方法结束而销毁
栈帧里存储了方法的局部变量、操作数栈、动态链接、方法返回地址等信息
局部变量表
局部变量表:用于存放方法参数和方法内部定义的局部变量的存储空间
-
案例1 slot是可复用的
- 不用的变量显式地置为null
案例2 操作数栈
动态连接
方法返回地址
方法执行后返回的地址方法调用
分派
第六章 垃圾回收算法
1、什么是垃圾
简单说就是内存中已经不再被使用到的内存空间就是垃圾
2、如何判定垃圾
3、如何回收
各种引用的实现
- 软引用
- 弱引用
- 需应用
4、根搜索算法、引用分类、GC类型、垃圾收集类型
垃圾收集算法
垃圾收集器
跟踪和记录分析,
触发fullgc的条件、对象分配规则
https://blog.csdn.net/z69183787/article/details/52757727
垃圾回收视频课
https://www.bilibili.com/video/BV1D741177rV?from=search&seid=17138143945258049311
三色标记 和 读写屏障
https://www.jianshu.com/p/12544c0ad5c1