1.JVM(Java Virtual Machine)
1.1 虚拟机(二进制字节码运行环境)
1. 系统虚拟机(完全是对物理计算机的仿真)
提供了可运行完整操作系统的软件平台
2. 程序虚拟机(专门为执行单个计算机程序)
典型代表就是java虚拟机,java虚拟机中执行的指令我们称为Java字节码指令
3. 作用
3.1 一次编译,到处运行(java语言的可移植性)
3.2 自动内存管理
3.3 自动垃圾回收功能
1.2 jvm位置
1.3 jvm整体结构(背)
1.4 java代码执行流程
1.5 jvm架构模型(栈/寄存器指令集架构)
1. 栈的指令集架构
2. 寄存器的指令集架构
1.6 jvm生命周期(三个状态)
1. 启动
通过引导类加载器创建一个初始类完成
2. 执行
执行java程序(程序执行时jvm执行,程序结束时jvm结束)
3. 退出
1. 程序正常执行结束
2. 程序执行中遇到异常/错误而异常终止
3. 由于操作系统出现错误/java虚拟机进程终止
4. 某线程调用Runtime类/System类的exit()方法/Runtime类的halt()方法
2.内存结构
类加载器子系统+虚拟机栈+执行引擎
3.类加载器子系统
3.1 类加载过程(3步)
①加载:根据查找路径找到相应的class文件,然后放入堆
②验证:检查加载的class文件的正确性
四种验证:【①文件格式验证;②元数据验证;③字节码验证;④符号引用验证】
③准备:给类中的静态变量分配内存空间
三种情况:【①static int b=10;②static final int c=20[基本类型];③static final string d=’b’[引用类型]】
④解析:虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址
⑤初始化:对静态变量和静态代码块执行初始化工作
4.双亲委派机制(类加载器机制)
举例(Car类)
引用在栈,具体存在堆
加载器分类(4个)
1. 虚拟机自带的加载器
2. 启动类(根)加载器
3. 扩展类加载器
4. 应用程序(系统类)加载器
//注意:
如果方法.getParent()返回null有以下两种情况:
1. 不存在
2. java程序获取不到(是其他语言写的 需要native修饰符修饰)
双亲委派机制(保证安全)
1. 类加载器收到类加载请求
2. 向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
APP(应用类) --> EXC(扩展类) --> BOOT(最终执行)
3. 启动加载器检查是否能够加载当前类
3.1 能加载 --> 结束
3.2 不能加载 --> 抛出异常,通知子加载器进行加载
4. 可能会弹出class not found
5.沙箱(sandbox)安全机制
基本组件(字节码校验器/类加载器)
1. 字节码校验器
确保java类文件遵守java语言规范(核心类[java javax开头]的类文件不会经过)
2. 类加载器
2.1 防止恶意代码干涉善意代码 (双亲委派机制)
2.2 守护被信任的类库边界 (双亲委派机制)
2.3 将代码归入保护域 (双亲委派机制)
6.Native关键字(底层c语言的库)
Java的作用范围达不到,要去找C语言的库—底层是C/C++语言编写
1. 进入本地方法栈
2. 调动本地方法接口 JNI(Java native interface) --扩展java的使用(融合不同编程语言为java所用)
7.PC寄存器(Program Counter Register)
每个线程都有一个程序计数器,是线程私有的一个指针
8.方法区(Method Area)
所有线程共享(共享区间)
1. 方法区: 静态变量(Static) 常量(Final) 类信息(构造方法,接口定义) 运行时的常量池
2. 堆内存: 实例变量
9.栈(Stack - 先进后出)
喝多了吐就是栈,吃多了拉就是队列(FIFO)
10.HotSpot
三种JVM
1. Sun公司的HotSpot
2. BEA JRockit
3. IBM J9 VM
HotSpot虚拟机的查看:
11.堆(Heap)
一个JVM只有一个堆内存
11.1 堆内存三个区域
1. 新生区(伊甸园区Ed ) Young/New
2. 养老区 old
3. 永久区(Jdk8之后叫元空间) Perm
GC主要在新生区和养老区
1.新生区—类诞生、成长、死亡
1. 伊甸园区 --所有对象都在这new
2. 幸存区(幸存区0,幸存区1)
2.养老区
新生区没有被淘汰清除的活下来了
3.永久区—常驻内存,存放java运行时环境/class信息
不存在垃圾回收!关闭JVM虚拟机就会释放区域内存
jdk1.6前 永久代 常量池在方法区
jdk1.7 永久化 常量池在堆 (慢慢的退化 -- 去永久代)
jdk1.8 无永久代 常量池在元空间
12.GC(垃圾回收)
12.1 复制算法(新生代使用)
谁空了谁就是to区域
从一个区域复制到另外一个(from和to的交换)
12.2 标记清除算法(标记-清除)
先标记,然后没有标记的进行清除,最后会造成内存碎片
12.3 标记压缩算法(标记-清除-压缩)
1. 标记 - 清除 - 压缩(重复步骤好几次,但是消耗大)
2. 标记清除几次 - 压缩(一次性将碎片整理)
12.4 分代收集算法(分代不同的解决方案)
1. 年轻代 : 存活率低 -> 复制算法
2. 老年代 : 存活率大 -> 标记清除(内存碎片不多) + 标记压缩混合
算法总结
1. 内存效率
复制算法 > 标记清除算法 > 标记压缩算法(时间复杂度)
2. 内存整齐度
复制算法(没有内存碎片) = 标记压缩算法(没有内存碎片) > 标记清除算法(有内存碎片)
3. 内存利用率
标记清除算法 = 标记压缩算法 > 复制算法(有to区域浪费)
JVM后续
参考视频: