【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对象,即 test1test2,然后将其相互赋值给对方的 test字段。

如果是采用引用计数的方式,由于test1test2相互引用,它们的引用计数器始终不为零,因为它们之间的引用数始终为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 标记-清除算法

标记-清除算法是最基本的垃圾回收算法之一。其过程分为两个阶段:标记阶段和清除阶段。

  1. 标记阶段:从一组称为 GC Roots 的对象出发,遍历所有可达对象,并将它们进行标记,表示这些对象是活动对象,不会被回收。
  2. 清除阶段:遍历整个堆空间,将没有标记的对象视为垃圾对象,直接回收这些垃圾对象的内存。回收后的内存形成一些不连续的碎片,可能会造成内存碎片化问题

2.2 复制算法

复制算法是为了解决标记-清除算法的内存碎片化问题而设计的一种垃圾回收算法。它将堆空间划分为两个大小相等的区域,每次只使用其中的一半空间。其过程分为三个阶段:标记阶段、复制阶段和角色互换阶段。

  1. 标记阶段:与标记-清除算法相同,从GC Roots对象出发,遍历所有可达对象,标记活动对象。
  2. 复制阶段将所有活动对象从一个区域复制到另一个区域,使得复制后的内存是连续的,不会出现内存碎片化问题
  3. 角色互换:完成复制后,两个区域的角色互换,原来的存活对象成为新的空闲区域,原来的空闲区域成为新的工作区域。

现在的商用虚拟机,包括HotSpot都是采用这种收集算法来回收新生代区域的对象。

  • 新生代中 98% 的对象都是朝生夕死的,所以并不需要按照1 : 1的比例来划分内存空间,而是将内存(新生代内存)分为一块较大的 Eden(伊甸园)空间和两块较小的 Survivor(幸存者)空间。
  • 每次使用 Eden 和其中一块 Survivor(两个 Survivor 区域一个称为 From(S0) 区,另一个称为 To (S1)区域)。
  • 当回收时,将 EdenSurvivor 中还存活的对象一次性复制到另一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。
  • Survivor 空间不够用时,需要依赖其他内存(如老年代)进行分配担保。

关于HotSpot

HotSpot默认EdenSurvivor的大小比例是 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实现的复制算法流程如下:

  1. Eden区满的时候,会触发第一次Minor GC,把还活着的对象拷贝到Survivor From区;当Eden区再次触发Minor GC的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域,并将EdenFrom区域清空。
  2. 当后续Eden又发生Minor GC的时候,会对EdenTo区域进行垃圾回收,存活的对象复制到From区域,并将EdenTo区域清空。
  3. 部分对象会在FromTo区域中来回复制,如此交换15次(由 JVM 参数 MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活的,就存入到老年代。

2.3 标记-整理算法

复制收集算法对象存活率较高时会进行比较多的复制操作,效率会变低,因此在老年代一般不能使用复制算法

针对老年代的特点,提出了标记-整理算法。标记过程仍与标记-清除过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。流程图如下:

2.5 分代算法

以上三种算法都存在一些共同的问题:

  1. 效率问题标记-清除算法标记-整理算法需要对整个堆空间进行遍历,可能导致垃圾回收的效率较低

  2. 内存碎片问题标记-清除算法在回收阶段会产生内存碎片,造成内存空间的浪费和不连续的内存布局

分代算法的诞生为了解决这些问题的,分代算法是一种综合利用多种垃圾回收算法的策略,通过对堆内存进行区域划分,针对不同区域采用不同的垃圾回收策略,从而实现更好的垃圾回收效果

分代算法的设计思想:
在 Java 程序中,不同对象的生命周期往往是不同的。大部分新创建的对象很快就会变成垃圾,而一些对象可能会长时间存活。因此,根据这个特点就将堆划分为不同的代,分别处理不同生命周期的对象,这样就能更好地优化垃圾回收的效率和性能。

分代算法通常将堆内存划分为以下几个代:

  1. 新生代(Young Generation)新创建的对象通常被分配到新生代。新生代使用复制算法进行垃圾回收,因为这些对象的生命周期短,产生的垃圾较多

  2. 老年代(Old Generation)经过多次 GC 仍然存活的对象被移到老年代。老年代使用标记-清除标记-整理算法进行垃圾回收,因为这些对象的生命周期较长,产生的垃圾相对较少

  3. 永久代(Permanent Generation)永久代用于存放类的元数据和常量等信息,在Java 8之后,永久代被元空间(Metaspace)所取代

通过分代算法,不同代采用不同的垃圾回收策略,能够针对不同生命周期的对象进行更精细的垃圾回收,避免将整个堆空间都遍历,提高垃圾回收效率。这样的设计使得垃圾回收器能够根据应用程序的运行情况动态调整回收策略,从而更好地适应不同的应用场景。

2.6 Minor GC 和 Major GC

在 JVM 中,垃圾回收(Garbage Collection,GC)可以根据执行的任务不同而分为两种类型:即 Minor GCMajor GC(也称为 Full GC)

Minor GC

Minor GC是针对新生代的垃圾回收过程。新生代是 Java 堆内存中的一部分,用于存放刚被创建的对象。通常,新创建对象的生命周期较短,因此在新生代使用复制算法进行垃圾回收。

Minor GC的工作过程如下:

  1. 标记阶段:从GC Roots对象出发,标记所有在新生代中存活的对象。

  2. 复制阶段:将所有存活的对象从Eden区复制到Survivor区。

  3. 角色互换:完成复制后,Eden区和Survivor区的角色互换,使得原来的Eden区成为新的空闲区,原来的Survivor区成为新的工作区。

Minor GC的目的是清理新生代中的垃圾对象,使得新生代能够为新的对象分配空间,尽量保证新生代的空间是连续的,避免产生内存碎片

Major GC

Major GC是针对老年区的垃圾回收过程,老年区用于存放长时间存活的对象。用于老年区中的对象生命周期较长,如果使用复制算法进行垃圾回收可能就会导致较大的复制成本,因此通常使用的是标记-清除标记-整理算法

Major GC的工作过程如下:

  1. 标记阶段:从GC Roots对象出发,标记所有在老年代中存活的对象。

  2. 清除或整理阶段:根据采用的算法进行相应的垃圾回收操作。标记-清除算法将清除未标记的垃圾对象,标记-整理算法将移动存活对象并清理未标记的垃圾对象。

Major GC的目的是清理老年代中的垃圾对象,以避免老年代占用过多内存资源,同时也是为了确保老年代的空间是连续的,避免内存碎片化问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/58127.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单元测试之 - Review一个微服务的单元测试

这里以github上一个microservice的demo代码为例,来看看如何为一个完整的服务编写单元测试。具体代码如下所示,我们重点查看一下catalog和customer,order中的单元测试有哪些。 首先来看catalog服务的单元测试,这个服务下面主要编写了CatalogWe…

无涯教程-Lua - 变量声明

变量的名称可以由字母,数字和下划线字符组成。它必须以字母或下划线开头,由于Lua区分大小写,因此大写和小写字母是不同的。 在Lua中,尽管无涯教程没有变量数据类型,但是根据变量的范围有三种类型。 全局变量(Global) …

A Survey of Embodied AI: From Simulators to Research Tasks 论文阅读

论文信息: 题目:A Survey of Embodied AI: From Simulators to Research Tasks 作者:Jiafei Duan, Samson Yu 来源:arXiv 时间:2022 Abstract 通过评估当前的九个具体人工智能模拟器与我们提出的七个功能&#xff0…

使用Git在GitHub上部署静态页面

在GitHub中,我们可以将自己的静态页面部署到GitHub中,它会给我们提供一个地址使得我们的页面变成一个真正的网站,可以供用户访问。 一、在GitHub下创建仓库 二、将项目部署到GitHub上 1. 初始化Git仓库 2. 提交代码 3. 关联远程仓库 在Gi…

STM32 NOR_FLASH 学习

NOR FLASH FLASH是常用的,用于存储数据的半导体器件,它具有容量大,可重复擦写、按“扇区/块”擦除、掉电后数据可继续保存的特性。 NOR FLASH的单位是MB,EEPROM的单位是KB。 NM25Q128,是NOR FLASH的一种&#xff0c…

​LeetCode解法汇总143. 重排链表

目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:143. 重排链表 描述: 给定一个单链表 L 的头节点 head ,单链表 L 表示为&#x…

Ubuntu-文件和目录相关命令

🔮linux的文件系统结构 ⛳目录结构及目录路径 🧩文件系统层次结构标准FHS Filesystem Hierarchy Standard(文件系统层次结构标准) Linux是开源的软件,各Linux发行机构都可以按照自己的需求对文件系统进行裁剪,所以众多…

EXCEL,vlookup以及数据去重

1,新建一个work表格,将数据copy进来,并做简单处理,让看起来舒服 2,使用vlookup函数查找数据是否在库中 注意:上图中的Table_array A1:C152,这个值要加绝对引用,写成: $A$1:$C$15…

地产变革中,物业等风来

2023年7月,也许是中国房地产行业变局中的一个大拐点。 中信建投研报表示,政治局会议指出当前我国房地产形势已发生重大变化,要适时调整优化政策,为行业形势定调……当前房地产行业β已至。 不久前,国家统计局公布了2…

《吐血整理》高级系列教程-吃透Fiddler抓包教程(26)-Fiddler如何抓取Android7.0以上的Https包-上篇

1.简介 众所周知,假如设备是android 7.0的系统同时应用设置targetSdkVersion > 24的话,那么应用默认是不信任安装的Fiddler用户证书的,所以你就没法抓到应用发起的https请求,然后你在Fiddler就会看到一堆200 HTTP Tunnel to x…

基于图像形态学处理的停车位检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1. 图像预处理 4.2. 车辆定位 4.3. 停车位检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ......................................…

Python-Python基础综合案例:数据可视化 - 折线图可视化

版本说明 当前版本号[20230729]。 版本修改说明20230729初版 目录 文章目录 版本说明目录知识总览图Python基础综合案例:数据可视化 - 折线图可视化json数据格式什么是jsonjson有什么用json格式数据转化Python数据和Json数据的相互转化 pyecharts模块介绍概况如何…

企业既要用u盘又要防止u盘泄密怎么办?

企业在日常生产生活过程中,使用u盘交换数据是最企业最常用也是最便携的方式,但是在使用u盘的同时,也给企业的数据保密工作带来了很大的挑战,往往很多情况下企业的是通过u盘进行数据泄漏的。很多企业采用一刀切的方式,直…

Flutter环境搭建踩坑集锦

Flutter 背景准备工作先检查一下自己的电脑,看一下是不是满足配置要求下载安装配置环境下载安装JDK下载安装Android studio下载Flutterflutter doctor故障Android license status unknownNetwork resources 故障 后记 背景 发现一个不错的框架Flutter,听…

Dockerfile构建LNMP镜像(yum方式)

目录 Dockerfile构建LNMP镜像 1、建立工作目录 2、编写Dockerfile文件 3、构建镜像 4、测试容器 5、浏览器访问测试: Dockerfile构建LNMP镜像 1、建立工作目录 [roothuyang1 ~]# mkdir lnmp/ [roothuyang1 ~]# cd lnmp/ 2、编写Dockerfile文件 [roothuyang1 …

【第一阶段】kotlin的range表达式

range:范围:从哪里到哪里的意思 in:表示在 !in:表示不在 … :表示range表达式 代码示例: fun main() {var num:Int20if(num in 0..9){println("差劲")}else if(num in 10..59){println("不及格")}else if(num in 60..89…

FFmepg视频解码

1 前言 上一篇文章<FFmpeg下载安装及Windows开发环境设置>介绍了FFmpeg的下载安装及环境配置&#xff0c;本文介绍最简单的FFmpeg视频解码示例。 2 视频解码过程 本文只讨论视频解码。 FFmpeg视频解码的过程比较简单&#xff0c;实际就4步&#xff1a; 打开媒体流获取…

人工智能发展的五个主要技术方向是什么?

人工智能主要分支介绍 通讯、感知与行动是现代人工智能的三个关键能力&#xff0c;在这里我们将根据这些能力/应用对这三个技术领域进行介绍&#xff1a; 计算机视觉(CV) 自然语言处理(NLP) 在 NLP 领域中&#xff0c;将覆盖文本挖掘/分类、机器翻译和语音识别。 机器人 1、…

人工智能与物理学(软体机器人能量角度)的结合思考

前言 好久没有更新我的CSDN博客了&#xff0c;细细数下来已经有了16个月。在本科时期我主要研究嵌入式&#xff0c;研究生阶段对人工智能感兴趣&#xff0c;看了一些这方面的论文和视频&#xff0c;因此用博客记录了一下&#xff0c;后来因为要搞自己的研究方向&#xff0c;就…

vscode 第一个文件夹在上一层文件夹同行,怎么处理

我的是这样的 打开终端特别麻烦 解决方法就是 打开vscode里边的首选项 进入设置 把Compact Folders下边对勾给勾掉