jvm常见的垃圾回收算法_垃圾回收机制算法

jvm常见的垃圾回收算法_垃圾回收机制算法在早期的计算机语言,比如C和C++,需要开发者手动的来跟踪内存,这种机制的优点是。但是它也有它的缺点,新的编程语言,比如JAVA,Go,Python,PHP…现在市面上的大部分主流编程语言,都采取了一个方案,那就是“垃圾回收机制”,运行时自身会运行相应的垃圾回收机制。。垃圾回收器(GC)会在适当的时候将的内存给释放掉。GC的优点:GC的缺点:JVM的内存结构包括四大区域:1.程序计数器2.栈(虚拟机栈,本地方法栈)3.堆4.方法区举个例子,任何组织里,人都有三个派别,1.积极派2.消极派

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用


1. 什么是垃圾回收机制(GC)

在早期的计算机语言,比如 C 和 C++,需要开发者手动的来跟踪内存,这种机制的优点内存分配和释放的效率很高。但是它也有它的缺点如果程序员不小心忘记释放内存,从而造成内存的泄露

内存泄露:申请内存之后,忘记释放了 导致 可用的内容越来越少,最终无内存可用

新的编程语言,比如 JAVA,Go,Python,PHP… 现在市面上的大部分主流编程语言,都采取了一个方案,那就是 “垃圾回收机制”,运行时自身会运行相应的垃圾回收机制。程序员只需要申请内存,而不需要关注内存的释放垃圾回收器(GC)会在适当的时候将已经终止生命周期的变量的内存给释放掉。

1.1 垃圾回收机制的优缺点

GC的优点:

  • 它大大简化了应用层开发的复杂度(不需要开发者再去手动跟踪内存)
  • 降低了内存泄露的风险

GC的缺点:

  • 消耗额外的开销(消耗的资源更多了)
  • 会影响程序的流畅运行

2. 哪些内存需要回收

JVM的内存结构包括四大区域:1.程序计数器 2.栈 (虚拟机栈,本地方法栈)3.堆 4.方法区

在这里插入图片描述

举个例子,任何组织里,人都有三个派别,1.积极派 2.消极派 3.中间摇摆派,如图,对于上述三个派别,哪些是要进行回收释放内存的?

正在使用的内存中的对象 代表 积极派

不再使用,但是尚未回收的内存中的对象 代表消极派

中单部分为中间摇摆派
在这里插入图片描述
需要进行回收释放资源的:消极派

为什么中间摇摆派不回收释放内存呢?对于这种部分仍在使用,一部分不在使用的对象,整体来说是不释放的!等到这个对象彻底完全不使用,才真正的释放!!

注意

垃圾回收的基本单位是“对象”,而不是“字节”

3. 垃圾回收具体是如何回收的

分为两个阶段:

  1. 找垃圾/判定垃圾
  2. 回收垃圾(释放内存)

3.1 找垃圾/判定垃圾

如何找 垃圾/判定垃圾呢?当下主流的思路,有两种方案:

  1. 基于引用计数(不是Java中采取的方案,这是别的语言,像Python采取的方案)
  2. 基于可达性分析(这个是Java采取的方案)

3.11 基于引用计数

什么是基于引用计数:简单来说,针对每个对象,都会额外引入一小块内存,保存这个对象有多少个引用指向他
举个例子
1.Test t = new Test();,此时 new 了一个对象,那么我们就会额外引入一小块内存,此时 t 指向这个对象的引用,因此 引用计数 加 1

在这里插入图片描述
2.Test t2 = t; 此时 t 和 t2 都是指向这个对象的引用,此时引用计数 从1 变为 2
在这里插入图片描述
3.

void func() {
   Test t = new Test();
   Test t2 = t;
}

func()//调用方法过程中,创建了对象(分配内存),在方法执行过程中,引入计数量是2,当方法执行结束,由于 t 和 t2 都是局部变量,跟着栈帧一起释放了,这一释放就导致引用计数为0了(没有引用指向这个对象了,也就没有代码能够访问到这个对象了),此时就认为这个对象是个垃圾!

注意:引用计数为0的时候,就不再使用了,这个内存不再使用,就释放了(为后面理解做铺垫)

3.12 引用计数的优缺

引用技术,简单可靠高效,但是有个两个致命缺陷!!

  1. 空间利用率比较低,每个 new 的对象都得搭配个 计数器,计数器假设 4个字节,如果对象本身很大(几百个字节),多出来4个字节,就不算什么,但是如果本身对象很小(自己才4个字节),多出4个字节,相当于空间被浪费了一半
  2. 会有循环引用的问题

循环引用问题:写个代码举例子便于理解

// 先创建一个类
class Test {
// 成员变量
    Test t = null;
}
// 创建实例
Test t1 = new Test();
Test t2 = new Test();

画出内存布局:

在这里插入图片描述

t1.t = t2;//把 t2 赋值给了 t1里面的t属性,此时对象2有两个引用
引用计数加1,变为2

在这里插入图片描述

t2.t = t1// 把 t1 赋值给 t2 里面的 t 属性,此时对象1 有两个引用
引用计数加1,变为2

在这里插入图片描述
接下来,烧脑的环节:

t1 = null
t2 = null

在这里插入图片描述

此时此刻,两个对象的引用计数,不为0,所以无法释放,但是由于引用长在彼此的身上,外界的代码也无法访问到这两个对象,此时此刻,这俩对象,就不能使用,又不能释放,就出现了“内存泄露”的问题。

所以,像 Python,PHP里进行GC也不只靠引用计数,还依赖其他的机制配合,但是Java可以直接采用可达性分析,来判断垃圾

3.13 基于可达性分析

基于可达性分析:简单的来说,通过额外的线程,定期的针对整个内存空间的对象进行扫描,有一些起始位置(称为 GCRoots),会类似于 深度优先遍历一样,把可以访问到的对象都标记一遍(带有标记的对象就是可达对象),没有被标记的对象,就是不可达,也就是垃圾!

什么才算 GCRoots

  1. 栈上的局部变量
  2. 常量池中的引用指向的对象
  3. 方法区中的静态成员指向的对象

举个栗子吧,比如:写个代码,构造一个二叉树
在这里插入图片描述
如果我们在外面的代码中

Node root = a

代码中只要拿到 树 根节点,就可以掌握所有的节点,树上的任意节点,都可以通过 a 直接/间接的获取到

换句话说,GC在进行可达性分析的时候,当 GC 扫描到 a 的时候,就会把 a 能访问到的所有元素都去访问一遍,并且进行标记,所标记的节点 表示 都不是 垃圾

如果代码中,写了如下代码

c.right = null

在这里插入图片描述
则此时意味着,从 a 出发,访问不到 f,f 就是 不可达,f 就是垃圾,f 就应该被回收

如果代码中

a.right = null

在这里插入图片描述
此时从 a 出发,c 和 f 都是不可达了,也就都被标记成垃圾了!

从上面的这几点我们可以看出,可达性分析是去遍历每一个对象,如果内存中的对象特别多,这个遍历就会很慢,因此 GC 还是比较消耗时间和系统资源的!

3.14 可达性分析的优缺点

优点:

  • 克服了引用计数的两个缺点:
    1. 空间利用率低
    2. 循环引用

缺点:

  • 系统开销大,遍历一次可能比较慢

tips:

找垃圾,核心就是确认这个对象未来是否还会使用,什么算不使用了?没有引用,就不使用了

明确了谁是垃圾之后,接下来就要回收垃圾了!

3.2 回收垃圾(释放内存)

3.21 回收垃圾(释放内存)三种基本策略

标记 – 清除

如图:这是一块内存,上面被分成了很多小块,其中有些部分是垃圾(打钩的)

在这里插入图片描述

这里的 标记 ,就是可达性分析的过程

清除,就是直接释放内存 ,灰色区域代表释放内存

在这里插入图片描述

此时如果直接释放,虽然内存还是还给了系统,但是被释放的内存是离散的(不是连续的)

分散开带来的问题就是:“内存碎片”,这个问题其实非常影响程序的执行!

内存碎片:比如,空闲的内存,有很多,假设一共是 1G,如果要申请 500M 内存,也是可能申请失败的,因为要申请 500M 的内存 必须是连续的,每次申请,都是申请的连续的内存空间,而这里的 1G 可能是多个 碎片加在一起 才 1G,可用的并不多

为了解决内存碎片因此我们引入了复制算法!

复制算法

如图:一块内存,分成两半,左边一半有很多对象,打钩的标记为垃圾,右边为 左边不是垃圾的,拷贝过来

在这里插入图片描述

然后再将左边全部标记为垃圾(灰色),全部释放掉,我们就能保证,左右两侧空间都是整体连续的

在这里插入图片描述

此时内存碎片问题就迎刃而解了!

注意:复制算法的问题有如下几点

  • 内存空间利用率低(只能用一般的空间)
  • 如果要保留的的对象多,要释放的对象少,此时复制开销就很大

针对复制算法我们进行改进!–》 标记 – 整理

标记 – 整理

如图:还是一块内存,上面有一些对象,其中一些被标记为垃圾(打钩的)

在这里插入图片描述

如何进行标记 – 整理呢?

类似于顺序表删除中间元素,有一个搬运操作,我们将 3 搬运到 2 ,再将 5 搬运到 3 ,再把 7 搬运到 4 然后再把后面的部分整体的释放掉

在这里插入图片描述

这个方案空间利用率是高了,但是仍然没有解决复制/搬运元素开销大的问题~

3.22 分代回收

上述的三个方案,虽然能解决回收垃圾的问题,但是都有缺陷,实际 JVM 中的实现,会把多种方案结合起来一起使用,这个思路我们称为 “分代回收”

在这里插入图片描述

我们这个对象,他是怎样在这个区域里来回 轮转 的呢?

  1. 刚创建出来的对象,就放在伊甸区
  2. 如果伊甸区的对象熬过一轮 GC 扫描,就会被拷贝到 幸存区(伊甸区 到 幸存区 应用了复制算法)
  3. 在后续的几轮 GC 中,幸存区的对象就在两个幸存区之间来回拷贝(复制算法),每一轮都会淘汰一波幸存者
  4. 在持续若干轮之后,对象终于,进入老年代,老年代有个特点,里面的对象都是比较老的(年级大的),因此老年代的 GC 扫描频率大大低于新生代。老年代中使用标记整理的方式进行回收!

上述过程是面试中的经典问题!!!一定要重点掌握啊!

注意:
在这里插入图片描述

注意!!!
分代回收中,还有一个特殊情况,有一类对象可以直接进入老年代(大对象,占有内存多的对象),大对象拷贝开销比较大,不适合使用复制算法!

4. 垃圾回收器

上面说的找垃圾,和释放垃圾,说的都是算法思想,不是具体落地实现,在JVM里,真正实现上述算法的模块称为“垃圾回收器”

在这里插入图片描述

在这里插入图片描述


?✨总结

“种一颗树最好的是十年前,其次就是现在”

所以,

“让我们一起努力吧,去奔赴更高更远的山海”

如果有错误❌,欢迎指正哟?

?如果觉得收获满满,可以动动小手,点点赞?,支持一下哟?

以梦为马,不负韶华

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/182489.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 示波器探头如何校准「建议收藏」

    示波器探头如何校准「建议收藏」示波器是电子测试设备中常见的电子器件,通过电子工程师会使用它测量相关电路的信号输出以及相应的电压电流变化。在示波器的应用场合中,除了有些RF或高速数字的场合用电缆直接测量以外,很多板上的调试工作都是借助探头完成的。不过在正式开始使用探头前,我们是需要校准的,那么我们如何进行示波器的探头校准呢?探头是示波器测量系统的一部分,很多高带宽的探头都必须是有源探头,有源探头内部的有源放大器的增益和偏置随着温度或者时间老化可能会有漂移,为了补偿这种漂移,就需要定期对探头进行校准。目前示波器探头的校准方法通常有三

    2022年10月12日
    3
  • 通过PropertyDescriptor反射进行字段名值的获取及设置

    通过PropertyDescriptor反射进行字段名值的获取及设置/** *根据属性名获取对应的value *@paramfieldName *@paramobj *@return *@throwsException */privatestaticStringgetValueByFiled(StringfieldName,Objectobj)throwsException{  //属性扫描器

    2022年10月1日
    4
  • matlab流场可视化后处理「建议收藏」

    matlab流场可视化后处理「建议收藏」matlab流场可视化后处理1流体中标量的可视化1.1云图1.2切片图绘制1.3三维等值面图绘制2流体中矢量的可视化2.1箭头图或速度图2.2流线图2.4带节点的流线图2.5流管图和流带图2.6圆锥体图2.7粒子动画图3参考链接1流体中标量的可视化流体力学中常见的标量为位置、速度绝对值、压强等。1.1云图常用的云图绘制有pcolor、image、imagesc、imsh…

    2025年6月16日
    2
  • 给在读研究生&未来要读研同学们的一封受益匪浅的信

    给在读研究生&未来要读研同学们的一封受益匪浅的信读研首先要改变的是自己的心理状态,说起来简单,但实际上很困难。不过既然已经上了研,就不要抱怨了,努力想想自己该干啥才是王道。首先需要明确几种好的心态。(仅以此文使自己进步,时时规范自己的行为。谦虚使人进步,骄傲使人落后。)当你上课感觉就像打酱油时,当你对研究生很迷茫时,当你坐在电脑前孜孜不倦时,请看下面的文章,很受用,至少我心里现在没有以前浮躁。好的文章有时能改变一个人的精神状态,下面就是…

    2022年5月15日
    34
  • html怎么添加左侧点虚线,css虚线样式,怎么在css里添加虚线

    html怎么添加左侧点虚线,css虚线样式,怎么在css里添加虚线css 虚线样式 怎么在 css 里添加虚线内容导读 控制 div 线框的是 border 我们可以具体设置四条边的线框是否显示 也可以去设置虚线框的颜色和粗细 线框的设置可以让 div 的块状更加明显 至于是用虚线还是实线 大家可以根据具体情况而定 不知道怎么在 css 里添加虚线的朋友 可以拿本文的代码去看看效果 自己再去修改修改 体会下变化 进而更审核的掌握 css 虚线样式 本文给大家介绍的是 css 虚线样式 给大

    2025年8月29日
    0
  • pycahrm激活码【在线破解激活】

    pycahrm激活码【在线破解激活】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    47

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号