十分钟让你搞懂JVM中的GC垃圾回收机制(分代回收)

文章目录

  • 0. 为什么要有垃圾回收?
  • 1. 垃圾回收哪个内存区域?
  • 2. 如何找到垃圾(死亡对象的判断)
    • 2.1 引用计数法
    • 2.2 可达性分析法
    • 2.3 两种算法的差别
  • 3. 如何清理垃圾(死亡对象的回收)
    • 3.1 标记-清楚法
    • 3.2 复制法
    • 3.3 标记-整理法
  • 4. JVM使用的回收方法
    • 4.1 什么是分代回收
    • 4.2 哪些对象会进入新生代?哪些对象会进入老年代?
    • 4.3 工作过程
  • 5. 垃圾回收器是什么
  • 总结


0. 为什么要有垃圾回收?

JVM 中的垃圾回收机制, 可以自动的释放不再使用的内存. 可以有效的防止程序中内存泄漏问题. 本文我们就来聊一下垃圾回收机制.

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

关注收藏, 开始学习吧🧐


1. 垃圾回收哪个内存区域?

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

因此本文所讲的有关内存分配和回收关注的为Java 堆 与 方法区 这两个区域。

垃圾回收, 应该分为两步:

  1. 找到垃圾.
  2. 清理垃圾.

对于 Java 来说, 垃圾回收, 回收的是 “对象”, 而不是 “字节”. 所以我们会认为 “垃圾” 指的就是 “已经死亡的对象”, 那么我们如何判定这个对象是否已经死亡, 是否是垃圾呢?

2. 如何找到垃圾(死亡对象的判断)

如果一个对象, 在后续的代码中, 不会被继续使用到了, 就可以视为是垃圾了. 那么我们该怎样判断这个对象不会再被继续使用到了呢?

在 Java 中, 使用一个对象, 只有一种途径, 就是搞个引用指向它, 然后通过引用来访问这个对象.

  • 一个对象, 只要有引用指向, 就无法确保后续孩会不会进行使用.
  • 一个对象, 如果没有引用指向, 那么这个对象以后一定无法被使用.

所以我们可以认为, 一个对象, 如果没有任何引用指向它, 就代表着之后也不会被使用到了, 那它就是一个 “垃圾” 了. 接下来我们来介绍两个主流的判断方法.

2.1 引用计数法

该方法简单来说, 就是给每个对象中安排一个计数器, 每次有引用指向它时, 内置的计数器就 +1. 每次有引用被销毁时, 计数器就 -1. 当计数器成为 0 时, 意味着该对象就变成垃圾了.

引用计数法实现简单, 判定效率也比较高, 在大部分情况下都是一个不错的算法, 比如 Python 语言就采用引用计数法进行内存管理.

但上述方案, 并不是 JVM 中判断垃圾所使用的方案. 是因为引用技术方案, 还存在着两个无法忽视的问题:

  1. 空间利用率会比较低, 浪费了不必要的内存空间. 如果一个对象本体只有 4 个字节时, 给引用计数分配 2 个字节, 就相当于浪费了 50% 的内存空间.
  2. 可能会存在循环引用的问题, 导致对象不能被正常识别为垃圾.

观察循环引用问题.
在这里插入图片描述
当前是简单画了一个内存草图, 我们执行左面的伪代码, 就会造成右面的内存布局. 可以看到, 明明已经没有引用指向对象了, 可是对象中的计数器, 还没有到 0. 此时这俩对象的引用计数不为 0, 就不能被视为垃圾, 也就无法被识别清理.

2.2 可达性分析法

上面我们说到, Java并不是采用引用计数法来判断对象是否已经"死亡", 其采用的其实是 “可达性分析法”.

JVM 首先会从现有代码中能直接访问到的引用出发, 尝试遍历所有能访问的对象. 只要这个对象能被访问到, 就被视为 “可达”, 反之为不可达. 完成整个遍历后, 视所有 “不可达” 的对象为垃圾.

此算法的核心思想为 : 通过一系列称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称之为"引用链",当一个对象到 GC Roots 没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象是不可用的。以下图为例:

在这里插入图片描述
对象 Object5-Object7 之间虽然彼此还有关联,但是它们到GC Roots是不可达的,因此他们会被判定为可回收对象。

在 Java 中,可作为GC Roots的对象包含下面几种:

  1. 栈上的局部变量.
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象。

2.3 两种算法的差别

  • 引用计数消耗的是空间, 通过引用计数的数值, 不需要持续判断, 就可以非常快速的得知这个对象是否是垃圾.
  • 可达性分析消耗的是时间, 不消耗额外的空间开销, 通过持续性的扫描, 就可以得知哪些对象是垃圾.

3. 如何清理垃圾(死亡对象的回收)

我们找到垃圾之后, 就应该对它进行清理了. 目前主流的一些清理方法有以下几种.

3.1 标记-清楚法

"标记-清除"算法是最基础的收集算法。算法分为"标记"和"清除"两个阶段 : 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。但该算法也有一些问题.

"标记-清除"算法的不足主要有两个 :

  1. 效率问题 : 标记和清除这两个过程的效率都不高
  2. 空间问题 : 标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行中需要分配较大对象时,无法找到足够连续内存而不得不提前触发另一次垃圾收集。

在申请内存时, 都是申请的连续内存空间, 释放内存, 就可能会破坏原有的连续性, 导致 “有内存, 但无法申请”.

在这里插入图片描述
假设当前剩余四块空间(黑色的)可以申请, 每个空间大小是 1mb, 总的空闲空间是 4mb, 但其实实际上已经无法继续分配大于 1mb 的内存了. 这种问题就叫做 “内存碎片” 问题.

3.2 复制法

"复制"算法是为了解决"标记-清理"的问题。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。

在这里插入图片描述

就是把一个内存, 分成两份空间 A 和 B, 只用一份A. 清理时, 把 A 中存活下来的对象全部复制到 B 中, 把 A 中内存统一释放 接下来就继续使用 B 空间. 再次清理时, 把存活下来的对象再复制回 A 中去, 把 B 中资源统一释放. 当前流程一直循环进行.

这个算法虽然提高了一些效率, 但也有些不足的地方,

  • 内存李永利比较低, 他真正能利用的内存空间, 只有原先内存空间的一半, 很浪费内存空间.
  • 在对象存活率较高时会进行比较多的复制操作, 效率会变低.

3.3 标记-整理法

针对 “标记-清楚” 提出了一种为"标记-整理"的算法。标记过程仍与 “标记-清除” 过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。

在这里插入图片描述

这样的方式, 避免了刚才复制算法中内存利用率比较低的问题. 但是也有个很明显的个问题, 就是这里搬运的成本, 也是比较高的.

4. JVM使用的回收方法

设计 JVM 中垃圾回收算法时, 虽然已经有了这么多的回收机制, 但还是没有哪个方法能让设计 JVM 的大佬们满意, 于是他们想了个办法, 集百家之长, 结合上面的回收方案, 搞一个综合性质的方案, 在不同的场景下, 使用不同的回收方式, 做到扬长而避短.

4.1 什么是分代回收

分代算法和上面讲的 3 种算法不同,分代算法是通过区域划分,实现不同区域和不同的垃圾回收策略,从而实现更好的垃圾回收。对于不同的情况设置更符合该情况的规则,从而达到更高的效率,这就是分代算法的设计思想。

当前 JVM 垃圾收集都采用的是"分代收集(Generational Collection)"算法,这个算法并没有新思想,只是根据对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用"标记-清理"或者"标记-整理"算法。

4.2 哪些对象会进入新生代?哪些对象会进入老年代?

  • 新生代:一般创建的对象都会进入新生代;
  • 老年代:大对象和经历了 N 次(一般情况默认是 15 次)垃圾回收依然存活下来的对象会从新生代移动到老年代。

4.3 工作过程

在这里插入图片描述

  • 堆内存为两个区:新生代 (Young) 和老年代 (Old);
  • 新生代默认占堆内存的 1/3,老年代默认占堆内存的 2/3;
  • 新生代又分为 Eden 区、Survivor From区、Survivor To区默认比例是 8:1:1.

先理解两种GC:

  1. Minor GC 又称为 新生代GC : 指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快。
  2. Full GC 又称为 老年代GC 或者 Major GC : 指发生在老年代的垃圾收集。出现了 Major GC,经常会伴随至少一次的 Minor GC。Major GC 的速度一般会比 Minor GC 慢10倍以上。

工作过程

  • 所有新创建的对象都在 Eden 区,当 Eden 区内存满后将 Eden 区+ Survivor From 区存活的对象复制到 Survivor To区;
  • 清空 Eden 区与 Survivor From 区;
  • 同时 Survivor From 与 Survivor To 分区进行交换;
  • 每次 Minor GC 存活对象年龄加 1,当年龄达到 15(默认值)岁时,被移到老年代;
  • 当 Eden 的空间无法容纳新创建的对象时,这些对象直接被移至老年代;
  • 当老年代空间占用达到阈值时,触发 Full GC;
  • 以上流程循环执行。

在这里插入图片描述

5. 垃圾回收器是什么

如果说上面我们讲的收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

垃圾收集器的作用:垃圾收集器是为了保证程序能够正常、持久运行的一种技术,它是将程序中不用的死亡对象也就是垃圾对象进行清除,从而保证了新对象能够正常申请到内存空间。

以下这些收集器是不同版本推出的重要的垃圾收集器, 读者了解一下即可.
在这里插入图片描述


总结

✨ 本文重点讲解了垃圾回收的一些概念, 包括怎么找垃圾, 怎么清理垃圾等等.
✨ 想了解更多知识, 请持续关注博主, 本人会不断更新学习记录, 跟随我一起不断学习.
✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.

再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM——几种常见的对象引用

目录 1. 软引用软引用的使用场景-缓存 2.弱引用3.虚引用和终结器引用 可达性算法中描述的对象引用,一般指的是强引用,即是GCRoot对象对普通对象有引用关系,只要这层关系存在, 普通对象就不会被回收。除了强引用之外,Ja…

C++局域网从服务器获取已连接用户的列表(linux to linux)

目录 服务器端 代码 客户端 代码解析 服务器端 原理 遇到的阻碍以及解决办法 客户端 原理 遇到的阻碍以及解决办法 运行结果截图 总结 服务器端 代码 #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <netinet…

深入了解 Pinia:现代 Vue 应用的状态管理利器

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【Python】用三种方法创建tkinter桌面窗口

Python的tkinter是Python的标准GUI库之一&#xff0c;它是一个开源的、跨平台的GUI工具包&#xff0c;可以用于创建桌面应用程序。 tkinter提供了许多常见的GUI组件&#xff0c;例如按钮、文本框、标签、列表框等等&#xff0c;可以轻松地创建各种类型的桌面应用程序。它还支持…

云服务器上部署 Web 项目及端口异常处理

文章目录 1. 在云服务器的 MySQL(MariaDB) 中, 建库建表2. 微调代码3. 打包4. 把 war 包 拷贝到云服务器上端口被占用处理 1. 在云服务器的 MySQL(MariaDB) 中, 建库建表 在云服务器中进入 MySQL mysql -u root -p把之前本地写好的 SQL 代码一粘贴即可 例如: -- 这个文件主要…

线性表,也是Java中数组的知识点!

线性表定义&#xff1a; 由n (n≥0)个数据特性相同的元素构成的有限序列称为线性表&#xff0c;(n0)的时候被称为空表。 线性表的顺序表示 线性表的顺序存储又被称为顺序表 优点 无需为表示表中元素之间的逻辑关系而增加额外的存储空间可以随意读取任意位置的元素 缺点 插入…

人力资源管理后台 === 登陆+主页灵鉴权

目录 1. 分析登录流程 2. Vuex中用户模块的实现 3.Vue-cli代理解决跨域 4.axios封装 5.环境区分 6. 登录联调 7.主页权限验证-鉴权 1. 分析登录流程 传统思路都是登录校验通过之后&#xff0c;直接调用接口&#xff0c;获取token之后&#xff0c;跳转到主页。 vue-elemen…

html实现360度产品预览(附源码)

文章目录 1.设计来源1.1 拖动汽车产品旋转1.2 汽车产品自动控制 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134613931 html实现360度产品预览&#xff08;附源码&…

VUE留言板

效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>作业</title><styl…

Flutter | TextField长按时选项菜单复制、粘贴显示为英文问题解决

Flutter | TextField长按时选项菜单复制、粘贴显示为英文问题解决 问题描述&#xff1a; 长按TextField后&#xff0c;显示剪切、复制等选项为英文&#xff0c;如下图所示&#xff0c;这是因为问未设置语言本地化&#xff0c;我们需要进行设置。 首先在pubspec.yaml加入以下依赖…

rsyslog出现Unit rsyslog.service is masked不可用问题解决

博主在测试将日志发送到日志服务器的功能时遇到了rsyslog服务不可用的问题&#xff0c;具体来说&#xff0c;就是执行systemctl restart rsyslog或者 service rsyslog restart命令时&#xff0c;出现了标题中所述的Unit rsyslog.service is masked问题。网上查找了很多资料&…

【代码】平抑风电波动的电-氢混合储能容量优化配置(完美复现)matlab-yalmip-cplex/gurobi

程序名称&#xff1a;平抑风电波动的电-氢混合储能容量优化配置 实现平台&#xff1a;matlab-yalmip-cplex/gurobi 代码简介&#xff1a;针对电-氢混合系统协同平抑接入新型电力系统的 新能源波动问题&#xff0c;提出考虑碱性电解槽运行特性的电-氢 混合储能容量优化配置方案…

这一款 Mac 系统终端工具,已经用的爱不释手了!

&#x1f525;&#x1f525;&#x1f525;作为程序员或者运维管理人员&#xff0c;我们经常需要使用终端工具来进行服务器管理及各种操作&#xff0c;比如部署项目、调试代码、查看/优化服务、管理服务器等。 相信大家用的最多的终端工具就是 Xshell、iTerm2和Mobaxterm&#…

【JavaEE初阶】浅谈进程

✏️✏️✏️今天正式进入JavaEE初阶的学习&#xff0c;给大家分享一下关于进程的一些基础知识。了解这部分内容&#xff0c;只是为后续多线程编程打好基础&#xff0c;因此进程部分的知识&#xff0c;不需要了解更加细节的内容。 清风的CSDN博客 &#x1f61b;&#x1f61b;&a…

汽车电子 - UDS

汽车电子 - UDS 概念基本概念分类请求与响应寻址信息物理寻址功能寻址 协议格式&#xff1f;&#xff1f;&#xff1f;750/758厂家自定义的吗&#xff1f;&#xff1f;&#xff1f;&#xff0c; 所有的UDS服务都在这里边吗&#xff1f;&#xff1f;&#xff1f;&#xff0c;代码…

offer 选择难?说说我的 2 个思考

大家好&#xff0c;我是鱼皮。秋招仍在进行中&#xff0c;随着越来越多的公司开奖&#xff0c;最近 编程导航星球 的小伙伴们也陆续发来了 offer 报喜&#xff1a; 图片 图片 但也有一部分小伙伴陷入了 “甜蜜的烦恼”&#xff0c;拿了几个 offer 却不知道怎么选择。 offer 选择…

存算一体还是存算分离?谈谈数据库基础设施的架构选择

从一则用户案例说起 某金融用户问&#xff0c;数据库用服务器本地盘性能好还是外置存储好&#xff1f;直觉上&#xff0c;本地盘路径短性能应该更好。然而测试结果却出乎意料&#xff1a;同等中等并发压力&#xff0c;混合随机读写模型&#xff0c;服务器本地SSD盘合计4万 IOPS…

【Spring篇】spring核心——AOP面向切面编程

目录 想要彻底理解AOP&#xff0c;我觉得你的先要了解框架的模块化思想&#xff0c;为此先记录框架在讲AOP 什么是java框架&#xff1f;为什么要出现框架&#xff1f; 我总结以下七点来讲述和帮助理解java框架思想 什么是AOP&#xff1f; 如何理解上面这句话呢&#xff1…

快速压缩:迅速减小PDF文件大小的步骤与技巧

虽然png图片格式是一种无损压缩格式&#xff0c;但是png图片的内存大小也是比较大的&#xff0c;而且兼容性上也没有jpg图片好&#xff0c;许多平台推荐的也都是jpg格式&#xff0c;所以当我们需要把png转jpg格式的时候&#xff0c;就需要用到图片格式转换器&#xff0c;今天推…

Pure-Pursuit 跟踪五次多项式轨迹

Pure-Pursuit 跟踪五次多项式轨迹 考虑双移线轨迹 X 轴方向位移较大&#xff0c;机械楼停车场长度无法满足 100 ~ 120 m&#xff0c;因此采用五次多项式进行轨迹规划&#xff0c;在轨迹跟踪部分也能水一些内容 调整 double_lane.cpp 为 ref_lane.cpp&#xff0c;结合 FrenetP…