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

news2024/11/28 14:32:13

文章目录

  • 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.coloradmin.cn/o/1254283.html

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

相关文章

数据结构 / day04 作业

1. 单链表任意位置删除, 单链表任意位置修改, 单链表任意位置查找, 单链表任意元素查找, 单链表任意元素修改, 单链表任意元素删除, 单链表逆置 // main.c#include "head.h"int main(int argc, const char *argv[]) {Linklist headNULL; //head 是头指针// printf(&q…

基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(四)

目录 前言总体设计系统整体结构图系统流程图 运行环境爬虫模型训练实际应用 模块实现1. 数据准备1)爬虫下载原始图片2)手动筛选图片 2. 数据处理3. 模型训练及保存4. 模型测试1)前端2)后端 系统测试1. 测试效果2. 模型应用1&#…

极兔快递查询,极兔快递单号查询,对需要的单号记录进行备注

批量查询极兔快递单号的物流信息,对需要的快递单号记录进行备注。 所需工具: 一个【快递批量查询高手】软件 极兔快递单号若干 操作步骤: 步骤1:运行【快递批量查询高手】软件,并登录 步骤2:点击主界面左…

redis(Remote Dictionary Service) 底层数据结构

redis 底层数据结构 动态字符串SDS 优点 获取字符串长度的时间复杂度O(1) 支持动态扩容,减少内存分配次数 新字符串小于1M – 新空间为扩展后字符串长度的两倍 1 新字符串大于1M – 新空间为扩展后字符串长度 1M 1. 内存预分配 二进制安全(记录了…

java springboot中使用 AOP监听方法执行周期

首先 我们在 pom.xml 中 dependencies标签中加入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>然后 我们随便创建一个类 编写代码如下 package com.ex…

pytorch导出rot90算子至onnx

如何导出rot90算子至onnx 1 背景描述2 等价替换2.1 rot90替换(NCHW)2.2 rot180替换(NCHW)2.3 rot270替换(NCHW) 3 rot导出ONNX 1 背景描述 在部署模型时&#xff0c;如果某些模型中或者前后处理中含有rot90算子&#xff0c;但又希望一起和模型导出onnx时&#xff0c;可能会遇到…

YOLOv5轻量化改进之mobilenetv3,更换mobilenetv3中的注意力机制。

目录 一、原理 二、代码 三、YOLOv5改进 一、原理 我们提出了基于互补搜索技术和新颖架构设计相结合的下一代mobilenet。MobileNetV3通过硬件网络架构搜索(NAS)和NetAdapt算法的结合来调整到移动电话cpu,然后通过新的架构进步进行改进。本文开始探索自动搜索算法和网络设计如…

Java新建项目如何整理项目结构,没有src文件夹

现在IDEA2023中新建项目时, 不会有src文件夹。这时需要自己创建一个src的包&#xff0c;然后将这个包设置为source root。 可能出现没有这个选项的情况&#xff0c;这是需要把设置的当前项目首先Unmark了&#xff0c;然后再对src文件夹mark一下。 src: 这是源代码的根目录。 …

Self Distillation 自蒸馏论文解读

paper&#xff1a;Be Your Own Teacher: Improve the Performance of Convolutional Neural Networks via Self Distillation official implementation&#xff1a; https://github.com/luanyunteng/pytorch-be-your-own-teacher 前言 知识蒸馏作为一种流行的压缩方法&#…

与Windows 10更新大同小异!一步一步教你如何更新Windows 11

如果你想让你的Windows 11设备获得最佳性能&#xff0c;那么定期更新是至关重要的。即使是最好的电脑如果不更新也会受到影响&#xff0c;因为更新会应用软件调整&#xff0c;帮助你的设备更快、更平稳地运行。它还提高了安全性&#xff0c;意味着你可以从Microsoft的最新功能中…

自动驾驶学习笔记(十一)——高精地图

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 高精地图 地图采集 底图制作 地图…

OpenFeign入门

OpenFeign是Spring Cloud OpenFeign&#xff0c;是Spring Cloud团队开发的基于Feign的框架 1、OpenFeign功能升级 OpenFeign在Feign的基础上提供了以下增强和扩展功能 &#xff08;1&#xff09;便于集成Spring Cloud组件&#xff1a;OpenFeign与Spring Cloud其他组件&#…

TCP/IP协议、三次握手、四次挥手

TCP/IP TCP/IP协议分层TCP头部三次握手TCP四次挥手常见问题1、什么是TCP网络分层2、TCP为什么是三次握手&#xff0c;不是两次或者四次&#xff1f;3、TCP为什么是四次挥手&#xff0c;为什么不能是三次挥手将第二次挥手和第三次挥手合并&#xff1f;4、四次挥手时为什么TIME_W…

汽车电子 - UDS

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

Redis-缓存设计

缓存穿透 缓存穿透是指查询一个根本不存在的数据&#xff0c; 缓存层和存储层都不会命中&#xff0c; 通常出于容错的考虑&#xff0c; 如果从存储层查不到数据则不写入缓存层。 缓存穿透将导致不存在的数据每次请求都要到存储层去查询&#xff0c; 失去了缓存保护后端存储的…

Linux:docker容器操作(4)

docker的基础操作 Linux&#xff1a;docker基础操作&#xff08;3&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/134616198?spm1001.2014.3001.5501 我这里准备了两个镜像 镜像加载到容器 docker create [选项] 镜像 运行的程序 -i 让容器的标准输…

基于单片机的可升降助眠婴儿床(论文+源码)

1.系统设计 本课题为基于单片机的可升降助眠婴儿床系统&#xff0c;在设计目标上确定如下&#xff1a; 1. 可以实现婴儿床的升降&#xff0c;摇床功能控制&#xff1b; 2. 具有音乐播放功能&#xff0c;并且有多首曲目&#xff1b; 3. 用户可以通过按键或者红外遥控&#x…

6.12找树左下角的值(LC513-M)

算法&#xff1a; 这道题适合用迭代法&#xff0c;层序遍历&#xff1a;按层遍历&#xff0c;每次把每层最左边的值保存、更新到result里面。 看看Java怎么实现层序遍历的&#xff08;用队列&#xff09;&#xff1a; /*** Definition for a binary tree node.* public clas…

openpnp - 给底部相机加防尘罩

文章目录 openpnp - 给底部相机加防尘罩概述笔记END openpnp - 给底部相机加防尘罩 概述 设备标定完, 看着底部相机, 有点担心掉进去东西, 万一从吸嘴掉下去的料(或者清理设备台面时, 不小心掉进去东西)将顶部相机搞短路怎么办. 就想加个防尘罩, 如果有东西掉进去, 可以掉到机…