JVM GC 算法原理概述
对于JVM的垃圾收集(GC),这是一个作为Java开发者必须了解的内容,那么,我们需要去了解哪些内容呢,其实,GC主要是解决下面的三个问题:
-
哪些内存需要回收?
-
什么时候回收?
-
如何回收?
回答了这三个问题,也就对于GC算法的原理有了最基本的了解。
1 如何判定哪些内存需要回收
在Java虚拟机的堆中会存放着很多的对象,那么,我们需要回收垃圾的时候,是通过什么算法来判断哪些垃圾的生命周期已到,需要回收呢?接下来的几种算法将帮助你解决这几个问题。
引用计数算法
先讲讲第一个算法:引用计数算法。
其实,这个算法的思想非常的简单,一句话就是:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器加1;当引用失效时,计数器减1;任何时刻计数器为0的对象就是不可能再被使用的。
这些简单的算法现在是否还被大量的使用呢,其实,现在用的已经不多,没有被使用的最主要的原因是他有一个很大的缺点:很难解决对象之间循环引用的问题。
循环引用:当A有B的引用,B又有A的引用的时候,这个时候,即使A和B对象都为null,这个时候,引用计数算法也不会将他们进行垃圾回收。
public class Test_02 {public static void main(String[] args) {Instance instanceA = new Instance();Instance instanceB = new Instance();instanceA.instance = instanceB;instanceB.instance = instanceA;instanceA = null;instanceB = null;System.gc();Scanner scanner = new Scanner(System.in);scanner.next();}
}class Instance{public Object instance = null;
}
如果使用的是引用计数算法,这是不能被回收的,当然,现在的JVM是可以被回收的。
可达性分析算法
这个算法的思想也是很简单的,这里有一个概念叫做可达性分析,如果知道图的数据结构,这里可以把每一个对象当做图中的一个节点,我们把一个节点叫做GC Roots,如果一个节点到GC Roots没有任何的相连的路径,那么就说明这个节点不可达,也就是这个节点可以被回收。

上面图中,虽然obj7、8、9相互引用,但是到GC Roots不可达,所以,这种对象也是会被当做垃圾收集的。
在Java中,可以作为GC Roots的对象包括以下几种:
-
虚拟机栈(栈帧中的局部变量表,Local Variable Table)中引用的对象。
-
方法区中类静态属性引用的对象。
-
方法区中常量引用的对象。
-
本地方法栈中JNI(即一般说的Native方法)引用的对象。
2 什么时候回收
在可达性分析算法中不可达的对象,也不是一定会死亡的,它们暂时都处于“缓刑”阶段,要真正宣告一个对象“死亡”,至少要经历两次标记过程。
step1:判断有没有必要执行finalize()方法
-
如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行`finalize()`方法。
另外,有两种情况都视为“没有必要执行”:
-
对象没有覆盖finaliza()方法。
-
finalize()方法已经被虚拟机调用过。
step2:如何执行
如果这个对象被判定为有必要执行finalize()方法,那么此对象将会放置在一个叫做 F-Queue 的队列中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它。
step3:执行死亡还是逃脱死亡
首先,我们需要知道,finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue 队列中的对象进行第二次小规模的标记。
-
逃脱死亡:对象想在
finalize()方法中成功拯救自己,只要重新与引用链上的任何一个对象建立关联即可,例如把自己(this关键字)赋值给某个类变量或者对象的成员变量,这样在第二次标记时它将被移出“即将回收”的集合。 -
执行死亡:对象没有执行逃脱死亡,那就是死亡了。
3 如何回收
如何回收其实就是利用哪些算法进行回收,垃圾收集算法这里讲几种大家平时也是看到的比较的算法,分别为:标记-清除算法、复制算法、标记-整理算法、分代回收算法。
这部分的内容其实在网上的文章比较多了,而且,基本上的差别不大,所以,从网上的文章选取下来,当做一个小的总结,大家可以参考这篇文章算是一个比较全的总结:GC算法与内存分配策略。
标记-清除(Mark-Sweep)算法
标记-清除(Mark-Sweep) 算法是最基础的垃圾收集算法,后续的收集算法都是基于它的思路并对其不足进行改进而得到的。顾名思义,算法分成“标记”、“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,标记过程在前一节讲述对象标记判定时已经讲过了。
标记-清除算法的不足主要有以下两点:
-
空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不触发另一次垃圾收集动作。
-
效率问题,因为内存碎片的存在,操作会变得更加费时,因为查找下一个可用空闲块已不再是一个简单操作。
标记-清除算法的执行过程如下图所示:


复制(Copying)算法
为了解决标记-清除算法的效率问题,一种称为“复制”(Copying)的收集算法出现了,思想为:它将可用内存按容量分成大小相等的两块,每次只使用其中的一块。当这一块内存用完,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。
这样做使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,代价可能过高了。复制算法的执行过程如下图所示:


标记-整理(Mark-Compact)算法
复制算法在对象存活率较高时要进行较多的复制操作,效率将会变低。更关键的是:如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用复制算法。
根据老年代的特点,标记-整理(Mark-Compact)算法被提出来,主要思想为:此算法的标记过程与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。 具体示意图如下所示:

分代收集(Generational Collection)算法
当前商业虚拟机的垃圾收集都采用分代收集(Generational Collection)算法,此算法相较于前几种没有什么新的特征,主要思想为:根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法:
-
新生代 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
-
老年代 在老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清除或标记-整理算法来进行回收。
4 总结
这里用思维导图做一个小的总结。

参考
堆外内存的回收机制分析 https://www.jianshu.com/p/35cf0f348275
java调用本地方法--jni简介 https://blog.csdn.net/w1992wishes/article/details/80283403
咱们从头到尾说一次 Java 垃圾回收 https://mp.weixin.qq.com/s/pR7U1OTwsNSg5fRyWafucA
深入理解 Java 虚拟机
Java Hotspot G1 GC的一些关键技术 Java Hotspot G1 GC的一些关键技术 - 美团技术团队
附:关于GC Roots的理解
所谓“GC roots”,或者说tracing GC的“根集合”,就是一组必须活跃的引用。
例如说,这些引用可能包括:
- 所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。
- VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。
- JNI handles,包括global handles和local handles
- (看情况)所有当前被加载的Java类
- (看情况)Java类的引用类型静态变量
- (看情况)Java类的运行时常量池里的引用类型常量(String或Class类型)
- (看情况)String常量池(StringTable)里的引用
注意,是一组必须活跃的引用,不是对象。
Tracing GC的根本思路就是:给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,其余对象(也就是没有被遍历到的)就自然被判定为死亡。注意再注意:tracing GC的本质是通过找出所有活对象来把其余空间认定为“无用”,而不是找出所有死掉的对象并回收它们占用的空间。
GC roots这组引用是tracing GC的起点。要实现语义正确的tracing GC,就必须要能完整枚举出所有的GC roots,否则就可能会漏扫描应该存活的对象,导致GC错误回收了这些被漏扫的活对象。
这就像任何递归定义的关系一样,如果只定义了递推项而不定义初始项的话,关系就无法成立——无从开始;而如果初始项定义漏了内容的话,递推出去也会漏内容。
那么分代有什么好处?
对传统的、基本的GC实现来说,由于它们在GC的整个工作过程中都要“stop-the-world”,如果能想办法缩短GC一次工作的时间长度就是件重要的事情。如果说收集整个GC堆耗时太长,那不如只收集其中的一部分?
于是就有好几种不同的划分(partition)GC堆的方式来实现部分收集,而分代式GC就是这其中的一个思路。
这个思路所基于的基本假设大家都很熟悉了:weak generational hypothesis——大部分对象的生命期很短(die young),而没有die young的对象则很可能会存活很长时间(live long)。
这是对过往的很多应用行为分析之后得出的一个假设。基于这个假设,如果让新创建的对象都在young gen里创建,然后频繁收集young gen,则大部分垃圾都能在young GC中被收集掉。由于young gen的大小配置通常只占整个GC堆的较小部分,而且较高的对象死亡率(或者说较低的对象存活率)让它非常适合使用copying算法来收集,这样就不但能降低单次GC的时间长度,还可以提高GC的工作效率。
相关文章:
JVM GC 算法原理概述
对于JVM的垃圾收集(GC),这是一个作为Java开发者必须了解的内容,那么,我们需要去了解哪些内容呢,其实,GC主要是解决下面的三个问题: 哪些内存需要回收? 什么时候回收&…...
【数值分析】LU分解解Ax=b,matlab自己编程实现
LU分解(直接三角分解,Doolittle分解) A x b , A L U Axb \,\,,\,\, ALU Axb,ALU { L y b U x y \begin{cases} Lyb \\ Uxy \end{cases} {LybUxy 矩阵 L {L} L 的对角元素为 1 {1} 1 ,矩阵 U {U} U 的第一行和 A {A} A …...
华为HCIE-Datacom课程介绍
厦门微思网络HCIE-Datacom课程介绍 一、认证简介 HCIE-Datacom(Huawei Certified ICT Expert-Datacom)认证是华为认证体系中的顶级认证,HCIE-Datacom认证定位具备坚实的企业网络跨场景融合解决方案理论知识,能够使用华为数通产品…...
QT(C++)-QTableWight添加行和删除空行
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、QTableWidget的添加行3、删除行 1、前言 最近要用QT开发项目,对QT不是很熟,就根据网上的查到的知识和自己的摸索,将一…...
软件测试/测试开发丨Python 面向对象编程思想
面向对象是什么 Python 是一门面向对象的语言面向对象编程(OOP):Object Oriented Programming 所谓的面向对象,就是在编程的时候尽可能的去模拟真实的现实世界,按照现实世界中的逻辑去处理问题,分析问题中…...
一次降低进程IO延迟的性能优化实践——基于block层bfq调度器
如果有个进程正频繁的读写文件,此时你vim查看一个新文件,将会出现明显卡顿。即便你vim查看的文件只有几十M,也可能会出现卡顿。相对的,线上经常遇到IO敏感进程偶发IO超时问题。这些进程一次读写的文件数据量很少,正常几…...
C语言易错知识点十(指针(the final))
❀❀❀ 文章由不准备秃的大伟原创 ❀❀❀ ♪♪♪ 若有转载,请联系博主哦~ ♪♪♪ ❤❤❤ 致力学好编程的宝藏博主,代码兴国!❤❤❤ 许久不见,甚是想念,真的是时间时间,你慢些吧,不能再让头发变秃…...
React 18 新增的钩子函数
React 18 引入了一些新的钩子函数,用于处理一些常见的场景和问题。以下是 React 18 中引入的一些新钩子函数以及它们的代码示例和使用场景: useTransition: 代码示例:import { useTransition } from react;function MyComponent()…...
安装与部署Hadoop
一、前置安装准备1、机器2、java3、创建hadoop用户 二、安装Hadoop三、环境配置1、workers2、hadoop-env.sh3、core-site.xml4、hdfs-site.xml5、linux中Hadoop环境变量 四、启动hadoop五、验证 一、前置安装准备 1、机器 主机名ip服务node1192.168.233.100NameNode、DataNod…...
MySQL 8.0 InnoDB Tablespaces之General Tablespaces(通用表空间/一般表空间)
文章目录 MySQL 8.0 InnoDB Tablespaces之General Tablespaces(通用表空间/一般表空间)General tablespaces(通用表空间/一般表空间)通用表空间的功能通用表空间的限制 创建通用表空间(一般表空间)创建语法…...
循环生成对抗网络(CycleGAN)
一、说明 循环生成对抗网络(CycleGAN)是一种训练深度卷积神经网络以执行图像到图像翻译任务的方法。网络使用不成对的数据集学习输入和输出图像之间的映射。 二、基本介绍 CycleGAN 是图像到图像的翻译模型,就像Pix2Pix一样。Pix2Pix模型面临…...
数组--53.最大子数组和/medium
53.最大子数组和 1、题目2、题目分析3、解题步骤4、复杂度最优解代码示例5、抽象与扩展 1、题目 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的一个连…...
centos 编译安装 python 和 openssl
安装环境: centos 7.9 : python 3.10.5 和 openssl 3.0.12 centos 6.10 : python 3.10.5 和 openssl 1.1.1 两个环境都能安装成功,可以正常使用。 安装 openssl 下载地址 下载后解压,进入到解压目录 执行…...
【nodejs】前后端身份认证
前后端身份认证 一、web开发模式 服务器渲染,前后端分离。 不同开发模式下的身份认证: 服务端渲染推荐使用Session认证机制前后端分离推荐使用JWT认证机制 二、session认证机制 1.HTTP协议的无状态性 了解HTTP协议的无状态性是进一步学习Session认…...
数据结构【线性表篇】(三)
数据结构【线性表篇】(三) 文章目录 数据结构【线性表篇】(三)前言为什么突然想学算法了?为什么选择码蹄集作为刷题软件? 目录一、双链表二、循环链表三、静态链表 结语 前言 为什么突然想学算法了? > 用较为“官方…...
Python装饰器的专业解释
装饰器,其实是用到了闭包的原理来进行操作的。 单个装饰器: 以下是一个简单的例子: def outer(func):print("OUTER enter ...")def wrapper(*args, **kwargs):print("调用之前......")result func(*args, **kwargs)p…...
vue3框架笔记
Vue Vue 是一个渐进式的前端开发框架,很容易上手。Vue 目前的版本是 3.x,但是公司中也有很多使用的是 Vue2。Vue3 的 API 可以向下兼容 2,Vue3 中新增了很多新的写法。我们课程主要以 Vue3 为主 官网 我们学习 Vue 需要转变思想࿰…...
pytest --collectonly 收集测试案例
pytest --collectonly 是一条命令行指令,用于在运行 pytest 测试时仅收集测试项而不执行它们。它会显示出所有可用的测试项列表,包括测试模块、测试类和测试函数,但不会执行任何实际的测试代码。 这个命令对于查看项目中的测试结构和确保所有…...
dev express 15.2图表绘制性能问题(dotnet绘图表)
dev express 15.2 绘制曲线 前端代码 <dxc:ChartControl Grid.Row"1"><dxc:XYDiagram2D EnableAxisXNavigation"True"><dxc:LineSeries2D x:Name"series" CrosshairLabelPattern"{}{A} : {V:F2}"/></dxc:XYDi…...
WorkPlus:领先的IM即时通讯软件,打造高效沟通协作新时代
在当今快节奏的商业环境中,高效沟通和协作是企业成功的关键。而IM即时通讯软件作为实现高效沟通的利器,成为了现代企业不可或缺的一部分。作为一款领先的IM即时通讯软件,WorkPlus以其卓越的性能和独特的功能,助力企业打造高效沟通…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...
