前言
本篇通过介绍JVM是什么,认识JVM的内存区域的划分,了解类加载过程,JVM中垃圾回收机制,从中了解到垃圾回收机制中如何找到存活对象的方式,引用计数与可达性分析的方式,再释放垃圾对象时使用的方式,标准清除,复制算法,标准整理,分代回收等等,如有错误,请在评论区指正,让我们一起交流,共同进步!
文章目录
- 前言
- 1. 什么是JVM?
- 2. 认识JVM内存区域的划分
- 3. 认识类加载过程
- 4. 类加载中的双亲委派模型
- 5. JVM中的垃圾回收机制
- 5.1 GC是什么?
- 5.2.1 **第一阶段**:找GC对象,看对象是否存活; - 找垃圾
- 5.2.2 第二阶段:释放垃圾对象
- 5.3 JVM的分代回收机制
- 总结
本文开始
1. 什么是JVM?
JVM: Java虚拟机,通过软件模拟具有硬件功能,运行在一个完全隔离的环境中的完整计算机系统;
2. 认识JVM内存区域的划分
JVM内存区域图示:
问题:查看变量的形态,也就是根据代码判断属于堆,栈还是方法区?
① 堆: 成员变量;(new 出来的对象存放其中)
② 栈:局部变量;(维护方法直接的调用关系)
③ 方法区:静态变量;(放类加载后的类对象, 被static修饰的)
【注】变量处在什么区域与变量类型无关;
线程私有的内存:每个线程都有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储;
- 栈包括本地方法栈和虚拟机栈;
本地方法栈:给JVM内部本地方法使用;
虚拟机栈:给java代码使用; - 程序计数器:记录当前程序执行到那个指令了;
- 堆:存储程序中创建的对象
- 方法区:存放类加载后的类对象;- 静态变量;
【注】线程共享:堆与元数据区; 线程私有:栈(包括虚拟机栈,本地方法栈)与程序计数器;
例如:
void function() {
Student s = new Student();
}
//s是引用类型,是局部变量,处在栈上,而new Student()是对象,处在堆上;
3. 认识类加载过程
类加载:把 .class 文件加载到内存 得到类对象的过程;
类加载过程:
① 加载:找到.class文件,读取文件内容;
② 验证:验证.class文件是否符号规范要求;.class文件有明确的数据格式;
③ 准备:给类对象分配内存空间(给类变量(static修饰的静态变量)分配的内存空间是未初始化,默认全是0,此时静态变量也是0);
例如:public static int value = 66; //此时准备是给value分配空间,初始化为0,不是66;
④ 解析:针对字符串常量进行初始化;
初始化常量的过程: Java 虚拟机将常量池内的 符号引用 替换为 直接引用 的过程;
符号引用:处于相对位置,字符串常量在.class文件中存在,这时只知道他们之间的相对位置也就是符号引用;
直接引用:处于实际位置,加载到内存中知道位置就是实际内存位置也就是直接引用;
⑤ 初始化:针对类对象进行初始化;(初始化静态成员,执行静态代码,类有父类也需要加载父类)
4. 类加载中的双亲委派模型
4.1 触发类加载时机 - 类加载是非必要,不加载
1)创建了类的实例
2)使用类的静态方法/静态属性
3)使用子类,触发父类的加载
4.2 双亲委派模型
双亲委派模型作用:在类加载阶段,找.class文件;
JVM加载类会使用类加载器这里简单介绍一下;
JVM中内置的三个类加载器:
① BootStrap ClassLoader: 负责加载Java标准库中的类
② Extension ClassLoader: 负责加载一些非标准的 Sun/Oracke扩展的库中的类;
③ Application ClassLoader: 负责加载项目中自己写的类,和第三方库中的类;
类加载过程图:
5. JVM中的垃圾回收机制
5.1 GC是什么?
GC是垃圾收集的意思;帮助程序员自动释放内存的;
JVM主要释放哪个区域? =》释放 堆区域
5.2 垃圾回收的两个阶段
5.2.1 第一阶段:找GC对象,看对象是否存活; - 找垃圾
如何找垃圾:Java中使用对象,只能通过引用,所以通过判断一个对象是否有引用指向就可以了;所以引入下面两种方法;
- 引用计数法:给对象里安排一个额外空间,保存一个整数,表示该对象有几个引用指向;Java中不使用, Python,PHP使用
图示:
【注】
计数器作用:引用增加,计数器就增加;引用减少,计数器就减少,当计数器为0时,就认为该对象没有引用了,认为时垃圾;
引用计数的缺点:
① 浪费空间 ② 存在循环引用的情况,会导致引用计数判定逻辑错误;
- 可达性分析法:(Java中使用)把对象之间的引用关系,理解为一个树形结构,从一些特殊起点出发,进行遍历,只要能遍历到的对象,就是可达的,把不能访问到的对象,也就是不可达的当作垃圾;
通过一个树形结构图来看一下:
通过root引用作为起点,就可以访问整个树的节点;
可达性条件:进行图示遍历需要有起点;
① 栈上的局部变量,都是起点;
② 常量池中的引用对象;
③ 方法区中,静态成员引用的对象;
可达性分析的缺点:
1)消耗更多时间,遍历过程消耗时间,不能第一时间发现某个垃圾对象;
2)在遍历的时候,如果过程中当前代码的对象引用关系变了,就会产生麻烦;
5.2.2 第二阶段:释放垃圾对象
三种策略:
1)标记清除:先标记出回收对象,再直接释放垃圾对象内存;
产生问题:会产生大量内存碎片;清理完垃圾对象产生大量内存碎片,剩余的内存空间即使很多,但都是碎片化的,如果要申请一段连续的空间可能都申请不到;
图示:
2)复制算法: 解决了内存碎片问题;
复制算法过程:把整个内存空间分成两段(为了好描述过程,这里把内存空间分为a,b,a与b一样),一次只使用一半;如果使用a内存,将a内存中不是垃圾对象的拷贝到没有使用的另一边b内存中,然后再统一释放内存;
图示复制算法:
缺点:1)内存利用率比较低;
2)如果当前只要少部分垃圾,大部分对象需要保留,就需要花费高的复制成本;
3)标记整理
标记整理思想:结合上述标记清除,标记整理在其上在加上一个从后向前搬运元素的过程;
标记整理图示:
特点:1)解决了内存碎片的问题
2)但从后向前搬运的开销比较大;
5.3 JVM的分代回收机制
分代回收机制:综合上述思想,针对不同的情况,使用不同的策略对垃圾进行回收;
分代回收图示:
分代回收过程:
1)·新创建的对象存放到伊甸区;
2)伊甸区的对象,经过第一轮GC,会通过复制算法,拷贝到生存区;生存区分为两半,大小相等,每次只使用一半;
在生存区中是垃圾对象就淘汰,不是垃圾对象就复制到另一半,一直重复上述过程;
3)对象在生存区,经过若干轮GC,年龄增长到 一定程度,就会通过复制算法拷贝到老年代区;
4)进入老年代的对象,年龄比较大,针对老年代的GC扫描频次会降低,消亡率就降低了;
老年代某个对象是垃圾对象了,进行标记整理法清除;
5)特殊情况:对象非常大,直接进入老年代区;原因是大对象进行复制算法成本高,其次大对象也不多;
总结
✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!