JVM 的垃圾回收机制以及垃圾回收算法的详解

简介: JVM 的垃圾回收机制以及垃圾回收算法的详解

1、JVM 的垃圾回收机制

对于?程序计数器?、?虚拟机栈?、?本地方法栈?这三部分区域而言,其生命周期与相关线程有关,随线程而生,随线程而灭。并且这三个区域的内存分配与回收具有确定性,因为当方法结束或者线程结束时,内存就自然跟着线程回收了。


因此有关内存分配和回收关注的为?堆区?和?方法区?这两个区域。


垃圾回收,就是回收内存。而 JVM 中的内存又分为好几块:

1、程序计数器,由于程序计数器占用的内存空间小,不需要“垃圾回收”。

2、栈区,局部变量都是在代码块执行结束后自动销毁,这和“垃圾回收”没有关系。

3、元数据区 / 方法区,一般都是涉及到“类加载”,很少涉及到“类卸载”,少量使用到“垃圾回收”。

4、堆区,“垃圾回收”的主要战场。



2、识别垃圾

想要回收垃圾,首先要识别出垃圾。在 Java 中,使用对象一定需要通过引用的方法来使用(除了匿名对象,执行完匿名对象那行代码后,对应的对象就会被当作垃圾),如果一个对象没有任何引用指向它,就视为是无法被代码使用的,即可以当作垃圾。


但是,如果代码复杂一些,这些引用的生命周期各不相同,此时识别垃圾的判定过程就会复杂一些。为了解决这个问题,引入了两种解决方法:


2.1、引用计数

给每个对象安排一个额外的空间,空间里要保存当前这个对象有几个引用。这种思想虽然并没有在 JVM 中使用,但是广泛应用于其他主流语言的垃圾回收机制中(Python、php)。



引用计数机制,是一个简单有效的机制,但是存在两个关键的问题:


  • 问题一:消耗额外的内存空间。
  • 问题二:引用计数可能会产生“循环引用”的问题,此时引用计数就无法正常工作。所谓“循环引用”是指类似于:一个类中的一个成员变量指向另一个类的引用,另一个类中的成员变量同样又指向这个类的引用,当将原先引用类的变量置空,此时引用计数依然不为0。


上述代码出现了问题,此时两个对象的引用计数都不是0,不能被 GC 回收掉,但是这两个对象又无法使用,出现了类似“死锁”的情况,这也是 Java 不用引用计数的原因之一。


2.2、可达性分析

本质上是用“时间”换取“空间”,相比引用计数需要消耗更多的额外空间,但总体来说是可控的,不会产生类似“循环引用”的问题。


在写代码的过程中会定义很多变量,就可以从这些变量作为起点入手,尝试进行“遍历”,所谓遍历就是沿着变量中持有的引用类型的成员,再进一步的往下进行访问。


JVM 中存在专门的扫描线程,会不停的尝试对代码中已有的变量进行遍历,尽可能多的访问到对象。



使用上图举例,root 指向这棵二叉树的根节点,如果代码中出现了 root.right.right = null,此时二叉树与 f 节点断开,无法再使用 root 出发进行遍历操作访问 f 了,此时就说 f 节点为 “不可达” ,即可称为是垃圾;同样的代码中出现 root.right = null,此时二叉树与 c 节点断开,此时无法使用 root 遍历到达 c 节点,自然也无法到达 f 节点,此时就说 c 和 f 都是垃圾。


因此,所有能被访问到的对象,自然就不是垃圾了,反之剩下遍历了一圈也访问不到的对象,就是垃圾。


3、垃圾回收算法

把标记为垃圾的对象的内存空间进行释放,主要的释放方式有三种。


3.1、标记-清除

把标记为垃圾的对象直接释放掉(最朴素的做法),但是会产生内存碎片。如果存在很多内存碎片,很可能导致申请内存空间失败的情况。

因此一般不会使用这种方案,因为“内存碎片问题”是比较致命的。



3.2、复制算法

核心是不直接释放内存,而是把不是垃圾的对象,复制到内存的另外一半中,然后再整体将左则空间整体释放掉。



复制算法确实能够规避内存碎片问题,但是也有缺点:


  • 因为需要留有空间进行复制操作,总的可用内存变少了。
  • 如果每次要复制的对象比较多,此时复制的开销就会很大。(只有当该轮 GC 过程中,有大量对象被释放,少数对象存活,此时才适合使用“复制算法”)

3.3、标记-整理

类似于顺序表删除中间元素的操作(向前搬运),通过这个过程也能够有效解决内存碎片问题,也没有复制算法需要浪费很多空间,但是搬运内存的开销非常大。



由于这三种释放方式都存在各自的问题,因此 JVM 并没有直接使用上述方式,而是结合上述思想取长补短,做出了综合性方案,称为“分代回收”。


4、分代回收

引入了“对象的年龄”这样的概念,JVM 中有专门的线程负责周期性扫描/释放,一个对象如果被线程扫描一次,并且“可达性分析”证明可达(不是垃圾),此时将该对象的年龄属性 + 1(初始年龄为0)。JVM 中根据年龄的差异,将整个堆内存分成两个大的部分,?“新时代?”和?“老年代?”。而新生代区中又分为“伊甸区”、“生存区s0”和“生存区s1”。



当代码中 new 出一些对象,这些对象会先被创建在伊甸区,而在伊甸区中的对象,绝大部分会死去,只有少量存活,因此此处的垃圾回收算法使用“复制算法”。


然后经过一轮 GC 扫描后,会有少数存活的对象通过“复制算法”拷贝到生存区(有两个生存区),后续的 GC 扫描线程会对伊甸区和生存区都进行扫描,伊甸区中少数存活的对象同样会进入生存区,而生存区中仍然存活的对象会拷贝到另外一个生存区。


当对象的年龄达到一定阈值(一般情况默认 15 次)并且依然存活时,此时 JVM 就会认为该对象的生命周期大概率很长,就将这个对象从生成区拷贝到老年代区。


而在老年代的对象,GC 扫描的频率会大大降低。在老年代中结束的对象会按照 JVM的“标记整理”或者“标记清除”方法释放内存。



目录
相关文章
|
4天前
|
存储 算法 Java
先有JVM还是先有垃圾回收器?
是先有垃圾回收器再有JVM呢,还是先有JVM再有垃圾回收器呢?或者是先有垃圾回收再有JVM呢?历史上还真是垃圾回收更早面世,先有垃圾回收再有JVM。下面我们就来刨析刨析JVM的垃圾回收~
14 0
先有JVM还是先有垃圾回收器?
|
4天前
|
JavaScript 前端开发 算法
JavaScript的垃圾回收机制通过标记-清除算法自动管理内存
【5月更文挑战第11天】JavaScript的垃圾回收机制通过标记-清除算法自动管理内存,免除开发者处理内存泄漏问题。它从根对象开始遍历,标记活动对象,未标记的对象被视为垃圾并释放内存。优化技术包括分代收集和增量收集,以提升性能。然而,开发者仍需谨慎处理全局变量、闭包、定时器和DOM引用,防止内存泄漏,保证程序稳定性和性能。
18 0
|
4天前
|
安全 算法 Java
深入浅出JVM(十三)之垃圾回收算法细节
深入浅出JVM(十三)之垃圾回收算法细节
|
4天前
|
存储 算法 Java
深入浅出JVM(十二)之垃圾回收算法
深入浅出JVM(十二)之垃圾回收算法
|
4天前
|
算法 数据安全/隐私保护 计算机视觉
基于二维CS-SCHT变换和LABS方法的水印嵌入和提取算法matlab仿真
该内容包括一个算法的运行展示和详细步骤,使用了MATLAB2022a。算法涉及水印嵌入和提取,利用LAB色彩空间可能用于隐藏水印。水印通过二维CS-SCHT变换、低频系数处理和特定解码策略来提取。代码段展示了水印置乱、图像处理(如噪声、旋转、剪切等攻击)以及水印的逆置乱和提取过程。最后,计算并保存了比特率,用于评估水印的稳健性。
|
1天前
|
算法
m基于BP译码算法的LDPC编译码matlab误码率仿真,对比不同的码长
MATLAB 2022a仿真实现了LDPC码的性能分析,展示了不同码长对纠错能力的影响。短码长LDPC码收敛快但纠错能力有限,长码长则提供更强纠错能力但易陷入局部最优。核心代码通过循环进行误码率仿真,根据EsN0计算误比特率,并保存不同码长(12-768)的结果数据。
19 9
m基于BP译码算法的LDPC编译码matlab误码率仿真,对比不同的码长
|
2天前
|
算法
MATLAB|【免费】融合正余弦和柯西变异的麻雀优化算法SCSSA-CNN-BiLSTM双向长短期记忆网络预测模型
这段内容介绍了一个使用改进的麻雀搜索算法优化CNN-BiLSTM模型进行多输入单输出预测的程序。程序通过融合正余弦和柯西变异提升算法性能,主要优化学习率、正则化参数及BiLSTM的隐层神经元数量。它利用一段简单的风速数据进行演示,对比了改进算法与粒子群、灰狼算法的优化效果。代码包括数据导入、预处理和模型构建部分,并展示了优化前后的效果。建议使用高版本MATLAB运行。
|
4天前
|
算法 计算机视觉
基于高斯混合模型的视频背景提取和人员跟踪算法matlab仿真
该内容是关于使用MATLAB2013B实现基于高斯混合模型(GMM)的视频背景提取和人员跟踪算法。算法通过GMM建立背景模型,新帧与模型比较,提取前景并进行人员跟踪。文章附有程序代码示例,展示从读取视频到结果显示的流程。最后,结果保存在Result.mat文件中。
|
4天前
|
资源调度 算法 块存储
m基于遗传优化的LDPC码OMS译码算法最优偏移参数计算和误码率matlab仿真
MATLAB2022a仿真实现了遗传优化的LDPC码OSD译码算法,通过自动搜索最佳偏移参数ΔΔ以提升纠错性能。该算法结合了低密度奇偶校验码和有序统计译码理论,利用遗传算法进行全局优化,避免手动调整,提高译码效率。核心程序包括编码、调制、AWGN信道模拟及软输入软输出译码等步骤,通过仿真曲线展示了不同SNR下的误码率性能。
9 1
|
4天前
|
存储 算法 数据可视化
基于harris角点和RANSAC算法的图像拼接matlab仿真
本文介绍了使用MATLAB2022a进行图像拼接的流程,涉及Harris角点检测和RANSAC算法。Harris角点检测寻找图像中局部曲率变化显著的点,RANSAC则用于排除噪声和异常点,找到最佳匹配。核心程序包括自定义的Harris角点计算函数,RANSAC参数设置,以及匹配点的可视化和仿射变换矩阵计算,最终生成全景图像。
http://www.vxiaotou.com