这是我参与更文挑战的第2天,活动详情查看:更文挑战
一、jdk发展历史
- 1990 - sun公司 java
- 1996 - sun公司 JDK1.0
- 1998 - j2ee j2se j2me
- 2000 - java hotspot virtual machine 成为默认java 虚拟机
- 2003 - Scala、Groovy 加入java平台
- 2005 - JDK 1.5 -> 5.0
- 2006 - JDK 6.0 java 开源并建立了OpenJDK;Hotspot 成为 OpenJDK 默认的虚拟机
- 2008 - Oracle收购BEA 得到 JRockit 虚拟机
- 2010 - Oracle收购sun 获得java商标及Hotspot虚拟机;Oracle拥有 Hotspot、- JRockit两款虚拟机,计划后期整合为: HotRockit (2014年其实已经整合)
- 2011 - jdk7发布 -->>垃圾回收器 G1 --->>
- 2014 - jdk8 -->>
- 2017 - jdk9发布 将G1设置为默认GC 替代CMS
- 2018 - jdk11发布 --->>垃圾回收器 ZGC --->>
- 2019-03 - jdk12发布
- 2019-09 - jdk13发布
- 2020-03 - jdk14发布
- 2020-09 - jdk15发布
二、jvm相关
1.jvm生命周期
2.常见虚拟机
- SUN Classic VM
- Exact VM
- Hotspot VM:其他两个商用虚拟机没有 方法区的概念
- BEA JRockit VM 商用 专注于服务器端;不包含解析器的实现;最快的JVM
- IBM J9 VM (IT4J) 商用
三、类加载子系统(重点)
1.类加载过程
- 类加载子系统
- 负责从文件系统或网络中加载class文件 class文件在文件开头有特定 的 文件标识 (A... Hexadecimal CA FE BA BE)
- ClassLoader 只负责class文件的加载,至于是否可以运行、则由Execution Engine决定
- 加载的类信息存放于一块 称为 方法区 的内存空间、(物理磁盘->内存 形成内存实例)
- 类加载的7个步骤 加载、链接(验证、准备、解析)、初始化、使用、卸载
-
加载(loading)
-
链接(Link)
- 验证(Verify)
- 准备(Prepare)
- 解析(Reslove)
-
初始化(initial)
-
使用
-
卸载
2.双亲委派机制
- java虚拟机对 class文件 采用的是 【按需加载】 的方式;
- 在加载某个类的时候, java虚拟机 采用的是 【双亲委派机制】
- 【按需加载】:需要使用该类时候 才会去将class文件加载到内存 生成class对象
- 【双亲委派机制】:把请求交给父类处理、它是一种任务委派模式
- 优点:
- 避免类的重复加载
- 保护程序安全、防止核心API轻易被篡改 - 沙箱安全机制
3. jvm整体结构
四、运行时数据区(重点)
1.程序计数器
- 程序计数器(
Program Counter Register)- PC寄存器 -- 程序钩子
- 作用:用来存储下一条指令的地址(存储的是当前栈帧对应的地址)
- 任何时间一个线程都有一个方法在执行,也就是所谓的【当前方法】
- 程序计数器会存储【当前线程】 正在执行的 java方法的jvm地址;如果是native方法,则值为undefined
- 此区域不会出现OutOfMemory
- PC寄存器 -- 程序钩子
面试问题:
使用PC寄存器存储字节码指令地址用什么用呢?
为什么使用PC寄存器存储当前线程地址?
回答:
程序运行过程中,cpu不停切换线程,这时候切换回来的时候就需要知道从哪个地方开始继续执行。
[执行引擎中 JVM字节码解释器] 需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。
2.栈-虚拟机栈
- 栈-虚拟机栈(
VM stack)- 局部变量表 (LV) Local Variable
- 定义为一个‘数字数组’
- 主要存储:方法参数、定义方法内部的局部变量(包括基本数据类型、对象引用、以及returnAddress类型)
- 局部变量表 所需容量的大小 是在编译时期确定下来的
- slot(变量槽): 局部变量表 最基本的存储单元 (32位以内 占用 1个槽;64位类型 占用 2个)
- 操作数栈 (OS) Operand Stack
- 在方法执行过程中,根据字节码指令,往栈中写入数据或者提取数据;(即:入栈push 出栈 pop操作)
- 也是一个数组结构;操作数栈 所需容量的大小 是在编译时期确定下来的 与 局变量表 大小是两回事
- 栈顶缓存技术:将栈顶的元素 全部缓存在物理CPU寄存器中、以此降低对内存的读写次数、提升执行引擎的执行效率
- 动态链接 (DL) (帧数据区)
- 也叫:'指向运行时常量池的方法引用'
- 每个栈帧内部都包含一个指向 运行时常量池 中该栈帧所属方法的引用、
- 每个栈帧内部都包含 一个 动态链接
- 方法返回地址 (RA) (帧数据区)
- 作用:【用来存储 该方法(当前方法)的pc寄存器的值】
- 本质上:方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置pc寄存器值,让调用者方法继续执行下去。
- 一些附加信息 (帧数据区)
- 栈帧中还允许携带与java虚拟实现相关的一些附加信息。
- 例如:对程序调试提供支持的信息
- 局部变量表 (LV) Local Variable
** 数据存放:
栈空间存放的数据:
方法内部基本类型数据、方法内部引用类型地址
堆空间存放的数据:
对象信息、类成员属性等
** -Xss 设置 虚拟机栈大小空间
** 栈帧
- 栈中 以 栈帧(stack frame) 格式存在
- 方法与栈帧 完全 一对一 的关系
- 栈帧 是一个内存区块,是一个数据集,维系着方法执行过程中各种数据信息
3.栈-本地方法栈
- 栈-本地方法栈(
Native Method stack)- 本地方法:native 关键字修饰的方法叫本地方法,方法的实现为非java语言,(非抽象法,与abstract关键字不能同时使用)
- Native Method : java调用非java代码的接口
- java 虚拟机栈用于管理java 方法的调用
- 本地方发栈 用于管理 本地方法的调用
4.堆
- 堆(
heap)- 几乎所有的对象实例及数组 都应当在运行时分配在堆上
- 两个关键参数
- 年轻代
- 老年代
1个进程 ->
1个jvm实例 ->
1 运行时数据区 ->
1个 方法区 | 1个堆
N个 程序计数器 | N个虚拟机栈 | N个本地方法栈
概念:
一个jvm实例只存在一个堆内存;
堆在 虚拟机启动时候被创建,堆的大小随之被确定,
堆是jvm管理 中最大的一块内存区间
jvm规范规定: 堆空间 -> "物理上不连续 逻辑上连续"
5.方法区/元空间
- 方法区/元空间(
Method area/metaspace)- 存放的内容
- 类(类型)信息
- 方法信息
- 运行时常量池
- 静态变量
- JIT代码缓存
- 域信息(属性信息)
- 参数
- 存放的内容
JDK1.7 :
-XX:PermSize=100M 永久代初始分配空间
-XX:MaxPerSize=100M 永久代最大可分配空间
JDK1.8:
-XX:MetaspaceSize=100M 元空间初始分配空间
-XX:MaxMetaspaceSize=100M 元空间最大可分配空间
命令行查看参数:
jinfo -flag MetaspaceSize
jinfo -flag MaxMetaspaceSize
五、执行引擎
- 解释器
- 功能:逐行见识字节码
- 优点:速度快
- 编译器 JIT(just in time)
- 功能:寻找热点代码 进行JIT编译 将这部分缓存起来
- [作用]:字节码指令转换为机器指令
- 主要面对 --> [java 操作数栈]
- 执行一行一行的字节码指令 -- 转换为 --> 机器指令
六、垃圾回收
- 【垃圾】 概念:
- 【内存泄漏】 概念:
- 垃圾标记阶段
- 引用计数算法(reference count)
- 可达性分析算法
- 垃圾清除阶段
- 标记-清除算法
- 复制算法
- 标记-压缩算法
- 分代收集算法