【JVM】(三) 深入理解JVM垃圾回收机制(GC)
文章目录
- 前言
- 一、死亡对象的判断方法
- 1.1 引用计数算法
- 1.2 可达性分析算法
- 二、垃圾回收算法
- 2.1 标记-清除算法
- 2.2 复制算法
- 2.3 标记-整理算法
- 2.5 分代算法
- 2.6 Minor GC 和 Major GC
前言
JVM 的垃圾回收机制(Garbage Collection)是 Java 中的重要特性之一,它负责在程序运行时自动回收不再使用的内存,以避免内存泄漏和提高程序的性能。垃圾回收机制的设计与实现对于 Java 程序的运行效率和稳定性至关重要。因此,作为一名合格的程序员,对于 JVM 的垃圾回收机制势必要有深入的了解,才能编写设计出合理高效的代码。
一、死亡对象的判断方法
在 JVM 的垃圾回收机制中,判断一个对象是否死亡是非常重要的,因为只有死亡的对象才能够被垃圾回收器回收,释放其占用的内存。其中常用的判断方法包括引用计数算法
和可达性分析算法
。
1.1 引用计数算法
引用计数算法是一种简单的垃圾回收算法,它的基本思想就是为每一个对象都维护一个引用计数器,用于记录当前有多少个引用指向该对象。当引用计数器变为零的时候,表示该对象没有任何引用指向它,因此就可以判断这个对象为死亡状态,就可以进行回收了。
引用计数的实现非常简单,判断效率也很高,在大部分情况下都是一个不错的算法。比如 Python 就是采用的这种方法实现对内存的管理。但是引用计数的最大问题就是循环引用。
例如下面的代码就展示了循环引用:
class Test{public Test test;
}public class Demo {public static void main(String[] args) {Test test1 = new Test();Test test2 = new Test();test1.test = test2;test2.test = test1;test1 = null;test2 = null;}
}
以上代码就是一个简单循环引用的案例,类Test
中的test
字段是一个引用类型,它指向另一个Test
对象。在 main
函数中,分别常见了两个Test
对象,即 test1
和test2
,然后将其相互赋值给对方的 test
字段。
如果是采用引用计数的方式,由于test1
和test2
相互引用,它们的引用计数器始终不为零,因为它们之间的引用数始终为1。根据引用计数算法的原理,即使这两个对象不再被程序使用,它们的引用计数器也不会变为零,因此不会被垃圾回收器回收。
1.2 可达性分析算法
为了解决循环引用的问题,JVM 采用了更为复杂的可达性分析算法。可达性分析算法的基本思想是以一组称为 GC Roots
的对象作为起始点,通过向下搜索和遍历对象应用链,判断对象是否可达(reachable)。
对象可达的含义:
- 一个对象是可达的,意味着它可以通过一系列引用链从
GC Roots
对象到达。- 如果一个对象不可达,即它没有与任何
GC Roots
对象相连接,那么该对象将被判断为死亡状态,即可以被垃圾回收器回收。
JVM 中的GC Roots
对象包括:
- 已加载类的类对象(Class Object);
- 类静态变量引用的对象;
- 活动线程(Active Thread)的栈帧(Stack Frame);
JNI(Java Native Interface)
中的本地方法栈(Native Method Stack)中引用的对象。
通过可达性分析算法,JVM 能够准确地判断对象的生命周期,避免了引用计数算法的循环引用问题,并有效地进行垃圾回收。
二、垃圾回收算法
垃圾回收算法是 JVM 垃圾回收机制的重要组成部分,它负责回收不再使用的对象,释放内存资源,确保程序的运行效率和稳定性。在 JVM 中,常见的垃圾回收算法包括:标记-清除算法、复制算法、标记-整理算法已经分代算法。
2.1 标记-清除算法
标记-清除算法
是最基本的垃圾回收算法之一。其过程分为两个阶段:标记阶段和清除阶段。
- 标记阶段:从一组称为
GC Roots
的对象出发,遍历所有可达对象,并将它们进行标记,表示这些对象是活动对象,不会被回收。 - 清除阶段:遍历整个堆空间,将没有标记的对象视为垃圾对象,直接回收这些垃圾对象的内存。回收后的内存形成一些不连续的碎片,可能会造成内存碎片化问题。
2.2 复制算法
复制算法
是为了解决标记-清除算法
的内存碎片化问题而设计的一种垃圾回收算法。它将堆空间划分为两个大小相等的区域,每次只使用其中的一半空间。其过程分为三个阶段:标记阶段、复制阶段和角色互换阶段。
- 标记阶段:与
标记-清除算法
相同,从GC Roots
对象出发,遍历所有可达对象,标记活动对象。 - 复制阶段:将所有活动对象从一个区域复制到另一个区域,使得复制后的内存是连续的,不会出现内存碎片化问题。
- 角色互换:完成复制后,两个区域的角色互换,原来的存活对象成为新的空闲区域,原来的空闲区域成为新的工作区域。
现在的商用虚拟机,包括
HotSpot
都是采用这种收集算法来回收新生代区域的对象。
- 新生代中 98% 的对象都是
朝生夕死
的,所以并不需要按照1 : 1
的比例来划分内存空间,而是将内存(新生代内存)分为一块较大的Eden
(伊甸园)空间和两块较小的Survivor
(幸存者)空间。- 每次使用
Eden
和其中一块Survivor
(两个Survivor
区域一个称为From(S0)
区,另一个称为To (S1)
区域)。- 当回收时,将
Eden
和Survivor
中还存活的对象一次性复制到另一块Survivor
空间上,最后清理掉Eden
和刚才用过的Survivor
空间。- 当
Survivor
空间不够用时,需要依赖其他内存(如老年代)进行分配担保。
关于HotSpot
:
HotSpot
默认Eden
与Survivor
的大小比例是 8 : 1
,也就是说Eden : Survivor From : Survivor To = 8:1:1
。所以每次新生代可用内存空间为整个新生代容量的 90%,而剩下的 10% 用来存放回收后存活的对象。
HotSpot是Java平台上使用最广泛的Java虚拟机(JVM)的一种实现。它由Oracle(前身是Sun Microsystems)开发,是Java >Development Kit(JDK)的一部分,也是OpenJDK的默认JVM实现之一。
HotSpot
实现的复制算法流程如下:
- 当
Eden
区满的时候,会触发第一次Minor GC
,把还活着的对象拷贝到Survivor From
区;当Eden
区再次触发Minor GC
的时候,会扫描Eden
区和From
区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To
区域,并将Eden
和From
区域清空。 - 当后续
Eden
又发生Minor GC
的时候,会对Eden
和To
区域进行垃圾回收,存活的对象复制到From
区域,并将Eden
和To
区域清空。 - 部分对象会在
From
和To
区域中来回复制
,如此交换15次(由 JVM 参数MaxTenuringThreshold
决定,这个参数默认是15),最终如果还是存活的,就存入到老年代。
2.3 标记-整理算法
复制收集算法
在对象存活率较高时会进行比较多的复制操作,效率会变低,因此在老年代一般不能使用复制算法。
针对老年代的特点,提出了标记-整理算法
。标记过程仍与标记-清除
过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。流程图如下:
2.5 分代算法
以上三种算法都存在一些共同的问题:
-
效率问题:
标记-清除算法
和标记-整理算法
需要对整个堆空间进行遍历,可能导致垃圾回收的效率较低。 -
内存碎片问题:
标记-清除算法
在回收阶段会产生内存碎片,造成内存空间的浪费和不连续的内存布局。
而分代算法
的诞生为了解决这些问题的,分代算法
是一种综合利用多种垃圾回收算法的策略,通过对堆内存进行区域划分,针对不同区域采用不同的垃圾回收策略,从而实现更好的垃圾回收效果。
分代算法
的设计思想:
在 Java 程序中,不同对象的生命周期
往往是不同的。大部分新创建的对象很快就会变成垃圾,而一些对象可能会长时间存活。因此,根据这个特点就将堆划分为不同的代,分别处理不同生命周期的对象,这样就能更好地优化垃圾回收的效率和性能。
分代算法通常将堆内存划分为以下几个代:
-
新生代(Young Generation):新创建的对象通常被分配到新生代。新生代使用
复制算法
进行垃圾回收,因为这些对象的生命周期短,产生的垃圾较多。 -
老年代(Old Generation):经过多次 GC 仍然存活的对象被移到老年代。老年代使用
标记-清除
或标记-整理算法
进行垃圾回收,因为这些对象的生命周期较长,产生的垃圾相对较少。 -
永久代(Permanent Generation):永久代用于存放类的元数据和常量等信息,在Java 8之后,永久代被元空间(Metaspace)所取代。
通过分代算法,不同代采用不同的垃圾回收策略,能够针对不同生命周期的对象进行更精细的垃圾回收,避免将整个堆空间都遍历,提高垃圾回收效率。这样的设计使得垃圾回收器能够根据应用程序的运行情况动态调整回收策略,从而更好地适应不同的应用场景。
2.6 Minor GC 和 Major GC
在 JVM 中,垃圾回收(Garbage Collection,GC)可以根据执行的任务不同而分为两种类型:即 Minor GC
和 Major GC(也称为 Full GC)
。
Minor GC
:
Minor GC
是针对新生代
的垃圾回收过程。新生代
是 Java 堆内存中的一部分,用于存放刚被创建的对象。通常,新创建对象的生命周期较短,因此在新生代
使用复制算法
进行垃圾回收。
Minor GC
的工作过程如下:
-
标记阶段:从
GC Roots
对象出发,标记所有在新生代中存活的对象。 -
复制阶段:将所有存活的对象从
Eden
区复制到Survivor
区。 -
角色互换:完成复制后,
Eden
区和Survivor
区的角色互换,使得原来的Eden
区成为新的空闲区,原来的Survivor
区成为新的工作区。
Minor GC
的目的是清理新生代中的垃圾对象,使得新生代能够为新的对象分配空间,尽量保证新生代的空间是连续的,避免产生内存碎片。
Major GC
:
Major GC
是针对老年区的垃圾回收过程,老年区用于存放长时间存活的对象。用于老年区中的对象生命周期较长,如果使用复制算法进行垃圾回收可能就会导致较大的复制成本,因此通常使用的是标记-清除
或标记-整理算法
。
Major GC
的工作过程如下:
-
标记阶段:从
GC Roots
对象出发,标记所有在老年代中存活的对象。 -
清除或整理阶段:根据采用的算法进行相应的垃圾回收操作。
标记-清除算法
将清除未标记的垃圾对象,标记-整理算法
将移动存活对象并清理未标记的垃圾对象。
Major GC
的目的是清理老年代中的垃圾对象,以避免老年代占用过多内存资源,同时也是为了确保老年代的空间是连续的,避免内存碎片化问题。
相关文章:

【JVM】(三) 深入理解JVM垃圾回收机制(GC)
文章目录 前言一、死亡对象的判断方法1.1 引用计数算法1.2 可达性分析算法 二、垃圾回收算法2.1 标记-清除算法2.2 复制算法2.3 标记-整理算法2.5 分代算法2.6 Minor GC 和 Major GC 前言 JVM 的垃圾回收机制(Garbage Collection)是 Java 中的重要特性之…...

Flink CEP(二) 运行源码解析
通过DemoApp学习一下,CEP的源码执行逻辑。为下一篇实现CEP动态Pattern奠定理论基础。 1. Pattern的定义 Pattern<Tuple3<String, Long, String>,?> pattern Pattern.<Tuple3<String, Long, String>>begin("begin").where(new…...

剑指Offer-学习计划(四)双指针(下)
剑指 Offer 57. 和为s的两个数字 剑指 Offer 58 - I. 翻转单词顺序 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 题目一:调整数组顺序使奇数位于偶数前面 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的…...

深度学习——常见注意力机制
1.SENet SENet属于通道注意力机制。2017年提出,是imageNet最后的冠军 SENet采用的方法是对于特征层赋予权值。 重点在于如何赋权 1.将输入信息的所有通道平均池化。 2.平均池化后进行两次全连接,第一次全连接链接的神经元较少,第二次全连…...

Python 进阶(七):高级文件操作(shutil 模块)
❤️ 博客主页:水滴技术 🌸 订阅专栏:Python 入门核心技术 🚀 支持水滴:点赞👍 收藏⭐ 留言💬 文章目录 1. 简介2. 常用函数2.1 复制文件2.2 复制目录2.3 移动文件或目录2.4 删除文件或目录2.…...

保留网络:大型语言模型的Transformer继任者
原文信息 原文题目:《Retentive Network: A Successor to Transformer for Large Language Models》 原文引用:Sun Y, Dong L, Huang S, et al. Retentive Network: A Successor to Transformer for Large Language Models[J]. arXiv preprint arXiv:2…...

算法通关村第二关——反转链表青铜笔记
LeetCode 206.反转链表 建立虚拟结点辅助翻转 public ListNode reverseList(ListNode head) {ListNode ans new ListNode(-1);ListNode cur head;while(cur!null){ListNode curNext cur.next;cur.next ans.next;ans.next cur;cur curNext;}return ans.next; }不带虚拟头…...

【Linux】——线程安全
目录 关于线程进程的问题 可重入与线程安全 常见的线程安全的情况 常见的不可重入的情况 常见的可重入的情况 可重入与线程安全区别 可重入与线程安全联系 Linux线程互斥 进程线程间的互斥相关概念 互斥量mutex 互斥量mutex常用接口 互斥量改造抢票系统 互斥量的原…...

[React]生命周期
前言 学习React,生命周期很重要,我们了解完生命周期的各个组件,对写高性能组件会有很大的帮助. Ract生命周期 React 生命周期分为三种状态 1. 初始化 2.更新 3.销毁 初始化 1、getDefaultProps() 设置默认的props,也可以用duf…...

【2023】Redis实现消息队列的方式汇总以及代码实现
Redis实现消息队列的方式汇总以及代码实现 前言开始前准备1、添加依赖2、添加配置的Bean 具体实现一、从最简单的开始:List 队列代码实现 二、发布订阅模式:Pub/Sub1、使用RedisMessageListenerContainer实现订阅2、还可以使用redisTemplate实现订阅 三、…...

ARM裸机-10
1、X210开发板和光盘资料 1.1、配置信息 CPU:三星S5PV210 内存:512M DDR2 SDRAM Flash:4GB iBand LCD:7寸,分辨率800x480 触摸屏:电容触摸屏 2、X210开发板硬件手册 3、X210开发板刷系统 3.1、什么是刷…...

「C/C++」C/C++指针详解
✨博客主页何曾参静谧的博客📌文章专栏「C/C」C/C程序设计📚全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C」C/C程序设计「Win」Windows程序设计「算法」数据结构与算法「File」数据文件格式 目录 一、术语…...

提高电脑寿命的维护技巧与方法分享
在维护电脑运行方面,我有一些自己觉得非常有用的技巧和方法。下面我将分享一些我常用的维护技巧,并解释为什么我会选择这样做以及这样做的好处。 首先,我经常清理我的电脑内部的灰尘。电脑内部的灰尘会影响散热效果,导致电脑发热…...

React常见面试题
React常见面试题 一、React中的样式管理有哪些方法 内联样式:对象,作用于当前组件普通样式表: 作用于全局,文件名是:xxx.scssCSS模块:类似Vue的scoped, 文件名需是:xxx.module.scs…...

C++中数据的输入输出介绍
C中数据的输入输出介绍 C中数据的输入输出涉及到的文件 <iostream>:这是C标准库中最常用的头文件之一,包含了进行标准输入输出操作的类和对象,如std::cin、std::cout、std::endl等。 <iomanip>:该头文件提供了一些用…...

0101日志-运维-mysql
1 错误日志 错误日志(Error Log):错误日志记录了MySQL引擎在运行过程中出现的错误和异常情况。这些错误可能包括启动和关闭问题、数据库崩溃、权限问题等。错误日志对于排查和解决MySQL引擎问题非常有帮助。 改日志默认开启,默认存…...

LabVIEW使用灰度和边缘检测进行视频滤波
LabVIEW使用灰度和边缘检测进行视频滤波 数字图像处理(DIP)是真实和连续世界的离散表示。除此之外,这种数字图像在通信、医学、遥感、地震学、工业自动化、机器人、航空航天和教育等领域变得非常重要。计算机技术越来越需要视频图像的数字图…...

SpringBoot整合WebService
SpringBoot整合WebService WebService是一个比较旧的远程调用通信框架,现在企业项目中用的比较少,因为它逐步被SpringCloud所取代,它的优势就是能够跨语言平台通信,所以还有点价值,下面来看看如何在SpringBoot项目中使…...

【LangChain】向量存储之FAISS
LangChain学习文档 【LangChain】向量存储(Vector stores)【LangChain】向量存储之FAISS 概要 Facebook AI 相似性搜索(Faiss)是一个用于高效相似性搜索和密集向量聚类的库。它包含的算法可以搜索任意大小的向量集,甚至可能无法容纳在 RAM 中…...

小研究 - 主动式微服务细粒度弹性缩放算法研究(三)
微服务架构已成为云数据中心的基本服务架构。但目前关于微服务系统弹性缩放的研究大多是基于服务或实例级别的水平缩放,忽略了能够充分利用单台服务器资源的细粒度垂直缩放,从而导致资源浪费。为此,本文设计了主动式微服务细粒度弹性缩放算法…...

驱动开发相关内容复盘
并发与竞争 并发 多个“用户”同时访问同一个共享资源。 竞争 并发和竞争的处理方法 处理并发和竞争的机制:原子操作、自旋锁、信号量和互斥体。 1、原子操作 原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作。 …...

2.2 身份鉴别与访问控制
数据参考:CISP官方 目录 身份鉴别基础基于实体所知的鉴别基于实体所有的鉴别基于实体特征的鉴别访问控制基础访问控制模型 一、身份鉴别基础 1、身份鉴别的概念 标识 实体身份的一种计算机表达每个实体与计算机内部的一个身份表达绑定信息系统在执行操作时&a…...

C++ 注释
程序的注释是解释性语句,您可以在 C 代码中包含注释,这将提高源代码的可读性。所有的编程语言都允许某种形式的注释。 C 支持单行注释和多行注释。注释中的所有字符会被 C 编译器忽略。 C 注释一般有两种: // - 一般用于单行注释。 /* … …...

Spring事务(声明式事务)(Spring的事务,Spring隔离级别,事务传播机制)
目录 一、什么是事务,为什么要用事务 二、Spring声明式事务 🍅 1、Transactional的使用 🎈 事务回滚 🎈注意:异常被捕获,不会发生事务回滚 🍅 2、Transactional 作⽤范围 🍅 …...

Linux运维面试题(四)之Linux服务管理
Linux运维面试题(四)之Linux服务管理 4.1 SSHSSH的登录验证方式SSH的登陆端口(默认22)和监听设置(/etc/ssh/sshd_config)SSH的登录用户限制(/etc/ssh/sshd_config PermitRootLogin)SSH的登录超时设置(/etc/…...

ChatGPT能否撰写科研论文?
ChatGPT,这款被许多人誉为语言处理领域的“黑马”,究竟能否应用于撰写科研论文?近期,以色列理工学院生物学家兼数据科学家Roy Kishony带领的团队,针对这一问题进行了系列研究,其结果已在《Nature》杂志上发…...

2023 电赛 E 题 K210方案
第一章:K210 介绍 K210芯片是一款基于RISC-V架构的嵌入式人工智能芯片,具备低功耗、高性能的特点。它拥有强大的图像处理和机器学习能力,适用于边缘计算设备和物联网应用。为了方便开发者,K210芯片提供了丰富的外设接口ÿ…...

网络知识介绍
一、TCP 传输控制协议,Transmission Control Protocol。 面向广域网的通信协议,跨域多个网络通信时,为两个通信端点之间提供一条具有如下特点的通信方式: 基于流、面向连接、可靠通信方式、网络状况不佳时尽量降低系统由于重传带…...

MapStruct设置全局的ComponentModel
在mapStruct上边,如果我们要切换成非默认的组件模式,常常要在Mapper注释中添加componentModel "spring",如果类太多的了的话,非常麻烦,有没有更好的方式呢,有的,可以在pom中添加一个…...

LinearAlgebraMIT_6_ColumnSpaceAndNullSpace
这节课的两个重点是column space列空间和null space零空间。 x.1 pre-multiply/left multiply and post-multiply/right multiply 对于pre-multiply/left multiply左乘和post-multiply/right multiply右乘,如果用英文的pre-和post-是比较容易理解的, A…...