JVM之垃圾回收算法详解

垃圾回收算法

Java是如何实现垃圾回收的呢?简单来说,垃圾回收要做的有两件事:

1、找到内存中存活的对象

2、释放不再存活对象的内存,使得程序能再次利用这部分空间

[本质上后续所有的垃圾回收算法,都是在前两种算法的基础上优化而来。]

垃圾回收算法的评价标准
  • 吞吐量、最大暂停时间、堆使用效率

Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为Stop The World简称STW,如果STW时间过长则会影响用户的使用。【无法处理用户请求】

用户代码执行和垃圾回收执行让用户线程停止执行(STW)是交替执行的。

所以判断GC算法是否优秀,可以从三个方面来考虑:

1.吞吐量

吞吐量指的是 CPU 用于执行用户代码的时间与 CPU 总执行时间的比值,即吞吐量 = 执行用户代码时间 /(执行用户代码时间 + GC时间)。吞吐量数值越高,垃圾回收的效率就越高

2.最大暂停时间

最大暂停时间指的是所有在垃圾回收过程中的STW时间最大值。最大暂停时间越短,用户使用系统时受到的影响就越短

3.堆使用效率

不同垃圾回收算法,对堆内存的使用方式是不同的。比如标记清除算法,可以使用完整的堆内存。而复制算法会将堆内存一分为二,每次只能使用一半内存。从堆使用效率上来说,标记清除算法要优于复制算法。

上述三种评价标准:堆使用效率、吞吐量,以及最大暂停时间不可兼得

  • 堆内存越大,最大暂停时间就越长。

  • 想要减少最大暂停时间【将长的拆分成多个小的,准备工作会重复执行】,就会降低吞吐量。

没有一个垃圾回收算法能兼顾上述三点评价标准,所以不同的垃圾回收算法它的侧重点是不同的,适用于不同的应用场景。

比如购物商品,最大暂停时间越短越好

程序在后台处理数据,最大暂停时间就无关紧要了。吞吐量很低的话执行效率肯定收到影响

标记清除算法(Mark-and-Sweep)

标记清除算法的核心思想分为两个阶段:

1.标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root开始通过引用链遍历出所有存活对象。

2.清除阶段,从内存中删除没有被标记也就是非存活对象。

优点:实现简单,只需要在第一阶段给每个对象维护标志位,第二阶段删除对象即可。

缺点:

1.碎片化问题

由于内存是连续的,所以在对象被删除之后,内存中会出现很多细小的可用内存单元。如果我们需要的是一个比较大的空间,很有可能这些内存单元的大小过小无法进行分配。【外部碎片

2.分配速度慢。O(n)

由于内存碎片的存在,需要维护一个空闲链表,极有可能发生每次需要遍历到链表的最后才能获得合适的内存空间。 如果链表很长,遍历也会花费较长的时间。

复制算法
  • 复制性能效率低,不适合老年代【说内存不想一分为二】

复制算法的核心思想是:【新Form放存活对象,旧Form垃圾回收,To是中介,后面换名】

1.准备两块空间From空间和To空间,每次在对象分配阶段,只能使用其中一块空间(From空间)。

(对象A首先分配在From空间。)

2.在垃圾回收GC阶段,将From中存活对象复制到To空间

(在垃圾回收阶段,如果对象A存活,就将其复制到To空间。然后将From空间直接清空。)

3.将两块空间的From和To名字互换

(接下来将两块空间的名称互换,下次依然在From空间上创建对象。)

完整的复制算法的例子:

1.将堆内存分割成两块From空间 To空间,对象分配阶段,创建对象。

2.GC阶段开始,将GC Root搬运到To空间

3.将GC Root关联的对象,搬运到To空间

4.清理From空间,并把名称互换

优点

  • 吞吐量高,复制算法只需要遍历一次存活对象复制到To空间即可。

    • 比标记-整理算法少了一次遍历的过程,因而性能较好

    • 但是不如标记-清除算法,因为标记清除算法不需要进行对象的移动

  • 不会发生碎片化,复制算法在复制之后就会将对象按顺序放入To空间中,所以对象以外的区域都是可用空间,不存在碎片化内存空间。

缺点

  • 内存使用效率低,每次只能让一半的内存空间来为创建对象使用。

  • 不适合老年代:如果存活对象数量比较大,复制性能会变得很差【老年代存活的多,把那么多复制很费事,所以适合新生代】

标记整理算法

【比复制算法多一次遍历】

标记整理算法也叫标记压缩算法,是对标记清理算法中容易产生内存碎片问题的一种解决方案。+ 提高内存使用率

核心思想分为两个阶段:

1.标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root开始通过引用链遍历出所有存活对象。

2.整理阶段,将存活对象移动到堆的一端。清理掉存活对象的内存空间。

优点:

  • 内存使用效率高,整个堆内存都可以使用,不会像复制算法只能使用半个堆内存

  • 不会发生碎片化,在整理阶段可以将对象往内存的一侧进行移动,剩下的空间都是可以分配对象的有效空间

缺点:

整理阶段的效率不高,整理算法有很多种,比如Lisp2整理算法需要对整个堆中的对象搜索3次,整体性能不佳。可以通过Two-Finger、表格算法、ImmixGC等高效的整理算法优化此阶段的性能。

  • 由于多了整理这一步,因此效率也不高,适合老年代这种垃圾回收频率不是很高的场景。

分代GC垃圾回收算法

【目前都在使用的算法】

分代GC算法将堆分成年轻代和老年代主要原因有:【降低垃圾回收对系统的影响】

1、可以通过调整年轻代和老年代的比例来适应不同类型的应用程序,提高内存的利用率和性能

2、新生代和老年代使用不同的垃圾回收算法,新生代一般选择复制算法【不会有内存碎片,吞吐量比标记算法高】,老年代可以选择标记-清除和标记-整理算法,由程序员来选择灵活度较高

3、分代的设计中允许只回收新生代(minor gc),如果能满足对象分配的要求就不需要对整个堆进行回收(full gc),STW时间就会减少。【吞吐量增大。因为FullGC耗时长】避免FullGC

  • 比如在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制“算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。

  • 而老年代的对象存活几率是比较高的(需要回收的少),而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

现代优秀的垃圾回收算法,会将上述描述的垃圾回收算法组合进行使用,其中应用最广的就是分代垃圾回收算法(Generational GC)

分代垃圾回收将整个内存区域划分为年轻代和老年代:【这种设计的本质原因:灵活调整、适应不同场景】

  • 伊甸园:上帝创造亚当和夏娃的地方,代表对象刚创建会放到到这里边

  • 幸存区实现复制算法

我们通过arthas来验证下内存划分的情况:

在JDK8中,添加-XX:+UseSerialGC参数使用分代回收的垃圾回收器,运行程序。

在arthas中使用memory命令查看内存,显示出三个区域的内存情况。

Eden + survivor 这两块区域组成了年轻代。

tenured_gen指的是晋升区域,其实就是老年代。

还可以选择的虚拟机参数如下。注意加上-XX:+UseSerialGC【因为每种垃圾回收算法都有独特的设计】

垃圾回收过程

【From存什么?】

1、分代回收时,创建出来的对象,首先会被放入Eden伊甸园区。

2、随着对象在Eden区越来越多,如果Eden区满,新创建的对象已经无法放入,就会触发年轻代的GC,称为Minor GC或者Young GC

Minor GC会把eden中和From需要回收的对象回收,把没有回收的对象放入To区

3、接下来,S0会变成To区,S1变成From区。当eden区满时再往里放入对象,依然会发生Minor GC。【复制算法

此时会回收eden区和S1(from)中的对象,并把eden和from区中剩余的对象放入S0。【此时s0放不下的话应该会直接放到老年代区,不管年龄】

注意:每次Minor GC中都会为对象记录他的年龄,初始值为0,每次GC完加1

4、如果Minor GC后对象的年龄达到阈值(最大15,默认值和垃圾回收器有关),对象就会被晋升至老年代。【代表存活时间长】

当老年代中空间不足,无法放入新的对象时,先尝试minor gc如果还是不足【是看平均晋升大小有没有超出老年代剩余空间 】,就会触发Full GC,Full GC会对整个堆进行垃圾回收。

Full GC之前并不一定会再次进行一次Minor GC。因为FullGC会回收整个堆

因为被放入老年代·的未必是达到15这个年龄,有可能年轻代满了之后minorGC还是回收不了,就只能放老年代了

  • 伊甸园满了--》回收---》to区放不下--》放入老年代

如果Full GC依然无法回收掉老年代的对象,那么当对象继续放入老年代时,就会抛出Out Of Memory异常

Full GC无法回收掉老年代的对象,那么当对象继续放入老年代时,就会抛出Out Of Memory异常。

空间分配担保

JDK 6 Update 24 之前,在发生 Minor GC 之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那这一次 Minor GC 可以确保是安全的。如果不成立,则虚拟机会先查看 -XX:HandlePromotionFailure 参数的设置值是否允许担保失败(Handle Promotion Failure);如果允许,那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次 Minor GC,尽管这次 Minor GC 是有风险的;如果小于,或者 -XX: HandlePromotionFailure 设置不允许冒险,那这时就要改为进行一次 Full GC。

JDK 6 Update 24 之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行 Minor GC,否则将进行 Full GC

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

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

相关文章

免费下载工具 -- Free Download Manager(FDM) v6.24.0.5818

软件简介 Free Download Manager (FDM) 是一款免费的功能强大的下载管理软件,适用于多种操作系统,包括 Windows、macOS、Android 和 Linux。这款软件的特色在于它快速、安全且高效的下载能力。它可以下载各种热门网站的影片,支持 HTTP/HTTP…

Vatee万腾平台:创新科技,驱动未来

在科技日新月异的今天,每一个创新的火花都可能成为推动社会进步的重要力量。Vatee万腾平台,作为科技创新领域的佼佼者,正以其卓越的技术实力、前瞻性的战略眼光和不懈的探索精神,驱动着未来的车轮滚滚向前。 Vatee万腾平台深知&am…

Linux基本命令的使用示例

目录 1实现效果:在downloads目录下创建1个空文件夹empty,创建1个空文件lake.txt,输入任意数据保存后退出 2实现效果:搜索包含关键字"泉眼"的行 3实现效果:重命名文件夹empty为full,复制文件cc…

Vue3项目如何使用npm link本地测试组件库

一、组件库操作 1、在组件库项目中先运行npm run lib,其效果如下 2、在组件库项目中在运行npm link,其效果如下 会创建一个全局的软连接指向本地的组件库 二、Vue3项目使用 1、在项目中运行 npm link 组件名称(即:组件库packag…

ChatGPT提问提示指南PDF下载经典分享推荐书籍

ChatGPT提问提示指南PDF,在本书的帮助下,您将学习到如何有效地向 ChatGPT 提出问题,以获得更准确和有用的回答。我们希望这本书能够为您提供实用的指南和策略,帮助您更好地与 ChatGPT 交互。 ChatGPT提问提示指南PDF下载 无论您是…

swiftui给视图添加边框或者只给某个边设置border边框

直接使用border()就可以给一个视图添加边框效果,但是这种边框会给所有的边都设置上。 border()里面也可以添加属性.border(.blue, width: 5)这种就是设置颜色和宽度。 设置圆角边框 Text("1024小神").padding().cornerRadius(20).overlay(RoundedRectang…

17.分频器设计拓展练习-任意分频通用模块

(1)Verilog代码: module divider_n(clk,reset_n,clk_out);input clk;input reset_n;output clk_out;wire clk_out1;wire clk_out2;wire [9:0]n;wire m;assign n 9;assign m n % 2;divider_even divider_even_inst(.clk(clk),.reset_n(reset_n),.n(n),.en(!m),.cl…

QT程序异常结束解决方法

在用QT开发第三方SDK的时候,刚开始是运行正常的,但是重装系统之后再次运行程序总是出现:程序异常结束。 以下方法尝试无效,但不失为一种排查方法: 重新安装QT;检查Qt Creator配置,编译器位数和…

下载Windows版本的pycharm

Python环境搭建 第一步下载安装python 等待安装完成 验证python是否安装成功 Python开发工具安装部署 JetBrains: Essential tools for software developers and teams PyCharm: the Python IDE for data science and web development 下载社区版本的PyCharm 双击打开下载好的…

计算机视觉研究院 | 智慧工地:2PCNet,昼夜无监督域自适应目标检测(附原代码)

本文来源公众号“计算机视觉研究院”,仅用于学术分享,侵权删,干货满满。 原文链接:智慧工地:2PCNet,昼夜无监督域自适应目标检测(附原代码) 由于缺乏夜间图像注释,夜间…

C++:多态(继承)

hello,各位小伙伴,本篇文章跟大家一起学习《C:多态》,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 ! 文章目录 :maple_leaf:多态的概念:maple_leaf:继承中的多态1.:leaves:虚函数表 :…

代码随想录算法训练营第四十八天| 115.不同的子序列、583. 两个字符串的删除操作、 72. 编辑距离

115.不同的子序列 题目链接:115.不同的子序列 文档讲解:代码随想录 状态:不会 思路: dp[i][j] 表示在 s 的前 j 个字符中,t 的前 i 个字符作为子序列出现的次数。 匹配的情况: 1.当 s[j-1] 与 t[i-1] 匹配…

接口测试(3)

接口自动化 # 获取图片验证码import requestsresponse requests.get(url"http://kdtx-test.itheima.net/api/captchaImage")print(response.status_code) print(response.text) import requestsurl "http://kdtx-test.itheima.net/api/login" header_da…

计算机网络之WPAN 和 WLAN

上一篇文章内容:无线局域网 1.WPAN(无线个人区域网) WPAN 是以个人为中心来使用的无线个人区域网,它实际上就是一个低功率、小范围、低速率和低价格的电缆替代技术。 (1) 蓝牙系统(Bluetooth) &#…

QT文件生成可执行的exe程序

将qt项目生成可执行的exe程序可按照以下步骤进行: 1、在qt中构建运行生成.exe文件; 2、从自定义的路径中取出exe文件放在一个单独的空文件夹中(exe文件在该文件夹中的release文件夹中); 3、从开始程序中搜索qt&#xf…

CTF php RCE(二)

0x04 php伪协议 这种我们是先看到了include才会想到,利用伪协议来外带文件内容,但是有些同学会问,我们怎么知道文件名是哪个,哪个文件名才是正确的,那么这里我们就得靠猜了 include函数 因为 include 是一个特殊的语…

产品原型设计:从概念到实现的完整指南

如果你是一位产品经理,那么你一定会和原型图打交道,产品原型是产品设计方案和底层逻辑的可视化表达,需要完整清晰地表达出产品目的及需求,在整个产品创造的过程中发挥着不可或缺的作用。而对于一些刚入行的产品经理来说&#xff0…

Instruct-GS2GS:通过用户指令编辑 GS 三维场景

Paper: Instruct-GS2GS: Editing 3D Gaussian Splats with Instructions Introduction: https://instruct-gs2gs.github.io/ Code: https://github.com/cvachha/instruct-gs2gs Instruct-GS2GS 复用了 Instruct-NeRF2NeRF 1 的架构,将基于 NeRF 的三维场景编辑方法迁…

4:表单和通用视图

表单和通用视图 1、编写一个简单的表单&#xff08;1&#xff09;更新polls/detail.html文件 使其包含一个html < form > 元素&#xff08;2&#xff09;创建一个Django视图来处理提交的数据&#xff08;3&#xff09;当有人对 Question 进行投票后&#xff0c;vote()视图…

Python实现吃豆人游戏详解(内附完整代码)

一、吃豆人游戏背景 吃豆人是一款由Namco公司在1980年推出的经典街机游戏。游戏的主角是一个黄色的小圆点&#xff0c;它必须在迷宫中吃掉所有的点数&#xff0c;同时避免被四处游荡的幽灵捉到。如果玩家能够吃掉所有的点数&#xff0c;并且成功避开幽灵&#xff0c;就可以进入…