6种java垃圾回收算法_被说烂了的Java垃圾回收算法,我带来了最“清新脱俗”的详细图解...

一、概况

理解Java虚拟机垃圾回收机制的底层原理,是系统调优与线上问题排查的基础,也是一个高级Java程序员的基本功,本文就针对Java垃圾回收这一主题做一些整理与记录。Java垃圾回收器的种类繁多,它们的设计要在吞吐量(内存空间)与实时性(用户线程中断)方面进行权衡,各个垃圾回收器的适应场景也不尽相同(如:桌面应用,web应用),因此,这里我们只讨论JDK8下的默认垃圾回收器,毕竟目前JDK8版本是业界的主流(占80%),并且我们只讨论堆内存空间的垃圾回收。JDK8下的默认垃圾回收器:UseParallelGC : Parallel (新生代)+ (老年代)堆内存回收机制

二、如何判断对象是否可回收?

首先思考一个问题,内存堆中那么多对象,回收器要回收哪些对象?怎么判断出这些要回收的对象呢?因此对于垃圾回收,判断并标识对象是否可回收是第一步。从理论层面来说,判断对象是否可回收一般两种方法。

第一种、引用计数器算法:每当对象被引用一次计数器加1,对象失去引用计数器减1,计数器为0是就可以判断对象死亡了。这种算法简单高效,但是对于循环引用或其他复杂情况,需要更多额外的开销,因此Java几乎不使用该算法。

第二种、根搜索算法-可达性分析算法:所谓可达性分析是指,顺着GCRoots根一直向下搜索(用一个成语概括就是“顺藤摸瓜”),整个搜索的过程就构成了一条“引用链”,只要在引用链上的对象叫做可达,在引用链之外的(说明跟GCRoots没有任何关系)叫不可达,不可达的对象就可以判断为可回收的对象。 哪些对象可作为GCRoots对象呢? 包括如下:虚拟机栈帧上本地变量表中的引用对象(方法参数、局部变量、临时变量)

方法区中的静态属性引用类型对象、常量引用对象

本地方法栈中的引用对象(Native方法的引用对象)

Java虚拟机内部的引用对象,如异常对象、系统类加载器等

所以被同步锁(synchronize)持有的对象

Java虚拟机内部情况的注册回调、本地缓存等如果对虚拟机的内存布局与运行流程有所了解的话,这些作为GCRoots都很好理解,它们是程序运行时的源头,程序的正常运行必须依赖它们,而与这些源头没有任何关系的对象,即可视为可回收对象。就好比“瓜从藤上掉下来了, 那这瓜肯定也没有用了”​GCRoots可达性分析 不可达对象可达性分析可达性分析从理论上很好理解,但在垃圾收集器具体运行时,要考虑的问题不知道要复杂多少倍,因为在可达性分析的同时,程序也是在并行运行着,整个内存堆的状态随着程序的运行是实时变化的,要实

现分析结果与内存状态的一致性,就必须要暂停用户线程,在一个快照去进行分析。

三、垃圾回收算法

可达性分析解决了判断对象是否可回收的问题,那么在垃圾回收时内存空间会发生哪些变化呢?这就是垃圾回收算法要讨论的问题,我们根据算法对内存采取的不同操作,可将垃圾回收算法分为3种,标记-清除算法、标记-复制算法、标记-整理算法。

3.1 标记-清除算法

根据名称就可以理解改算法分为两个阶段:首先标记出所有需要被回收的对象,然后对标记的对象进行统一清除,清空对象所占用的内存区域,下图展示了回收前与回收后内存区域的对比,红色的表示可回收对象,橙色表示不可回收对象,白色表示内存空白区域。标记-清除算法 垃圾回收前后内存区域对比

标记-清除算法的两个缺点:第一个:是执行效率不可控,试想一下如果堆中大部分的对象都可回收的,收集器要执行大量的标记、收集操作。

第二个:产生了许多内存碎片,通过回收后的内存状态图可以知道,被回收后的区域内存并不是连续的,当有大对象要分配而找不到满足大小的空间时,要触发下一次垃圾收集。

3.2 标记-复制算法

针对标记-清除算法执行效率与内存碎片的缺点,计算机科学家又提出了一种“半复制区域”的算法。

标记-复制算法将内存分为大小相同的两个区域,运行区域,预留区域,所有创建的新对象都分配到运行区域,当运行区域内存不够时,将运作区域中存活对象全部复制到预留区域,然后再清空整个运行区域内存,这时两块区域的角色也发生了变化,每次存活的对象就像皮球一下在运行区域与预留区域踢来踢出,而垃圾对象会随着整个区域内存的清空而释放掉,内存前后的状态参考下图:​标记-复制算法回收前后内存对比

​标记-复制算法在大量垃圾对象的情况下,只需复制少量的存活对象,并且不会产生内存碎片问题,新内存的分配只需要移动堆顶指针顺序分配即可,很好的兼顾了效率与内存碎片的问题。

标注-复制算法也存在缺点预留一半的内存区域未免有些浪费了,并且如果内存中大量的是存活状态,只有少量的垃圾对象,收集器要执行更多次的复制操作才能释放少量的内存空间,得不偿失。

3.3 标记-整理算法

标记-复制算法要浪费一半内存空间,且在大多数状态为存活状态时使用效率会很低,针对这一情况计算机科学家又提出了一种新的算法“标记-整理算法”,标记整理算法的标记阶段与其他算法一样,但是在整理阶段,算法将存活的对象向内存空间的一端移动,然后将存活对象边界以外的空间全部清空,如下图所示:​标记-整理算法回收前后内存对比

标记整理算法解决了内存碎片问题,也不存在空间的浪费问题,看上去挺美好的。但是,当内存中存活对象多,并且都是一些微小对象,而垃圾对象少时,要移动大量的存活对象才能换取少量的内存空间。

不同的垃圾回收算法都有各自的优缺点,适应于不同的垃圾回收场景

四、新生代、老年代堆内存结构

Java 堆内存空间新生代、老年代是如何划分的?对象创建后是如何分配到不同的区域的?结合下图可以知道,整个堆内存被分为了2个大的区域,新生代,老年代,默认情况下新生代占1/3的空间,老年代占2/3的空间,新生代又分为两个区 Eden区Survial区,Survial又分为S0、S1区 默认各占8/10与1/10,1/10的空间。​年轻代 老年代 堆空间结构

为什么要这么设计呢?为什么要分那么多不同的内存区域干嘛?这是由对象的生命周期特征、与各类垃圾回收算法的优缺点所决定的,这正式垃圾回收器设计的理论基础。经过统计分析,大多数应用程序对象生命周期符合两个特征:

垃圾回收的理论基础绝大多数的对象都是“朝生夕灭”的,既创建不久即可消亡;

熬过越多此垃圾回收过程的对象就越难以消亡;一块独立的内存区域只能使用一种回收算法,根据对象生命周期特征,将其划分到不同的区域,再对特定区域使用特定的垃圾回收算法,只有这样才能将垃圾算法的优点发挥到极致,这种组合的垃圾回收算法叫:分代垃圾算法。。比如:在新生代使用标记-复制算法,在老年代使用标记-整理算法。

五、堆内存回收过程详解

我们分析了如何判断对象是否可回收,还有3中基础的垃圾回收算法,以及年轻代、老年代的内存区域划分与原因。接下来我们就一步一步来分析堆内存的回收流程。

5.1 内存初始状态

假设在第一垃圾回收之前,内存中的状态如图所示,Eden区有2个存活对象,3个垃圾对象,内存的可用区域已经所剩无几,Survivor区因为还没有进行任何MinorGC所以是空的,有1个大对象直接分配到了老年代,垃圾回收初始状态

5.2 第1次执行MinorGC后状态

当新的对象分配到Eden区,发现内存空间不够,于是触发第一次MinorGC,垃圾回收器首先将Edne区中的两个存活对象复制到S0区,然后在清空Eden区的空间,如下图:​第一次MinorGC内存状态

5.3 程序运行一段时间后状态

经过第1次MinorGC程序再运行一段时间后,堆内存状态如下:Eden区又产生了大量的对象,并且大部分对象都可回收状态,这也符合对象“朝生夕灭”的特征,S0区中也有1个对象可以回收,S1与老年代没有变化,在这种状态下,如果新对象分配再次触发MinorGC会发生什么呢?​程序运行一度时间后的状态

5.4 执行第2次MinorGC后状态

新对象分配Eden区空间不足,又触发了第二次MinorGC,第二次MinorGC与第一次GC时在Eden区的操作是一样的:将Eden区存活的对象复制到S1区,然后在清空整个Eden区,同时也将S0区存活的对象复制到S1区并将对象的年龄加1,再清空S0区,GC后的状态如下图所示:​执行第二次MinorGC后状态

5.5 第2MinorGC程序运行一段时间后状态

经过第二MinorGC后程序又运行了一段时间,Eden区中有生成了很多对象,S1区也有一个对象可回收。第二MinorGC程序运行一段时间后状态

5.6 第15MinorGC后内存状态

在接下来的每次MinorGC时,都是第二次一样,从Eden区和survivor非空白区移动存活对象到survivor区中空白区域,并清空这两个区域内存空间,存活对象每此从survivor两个区域移动一次,对象年龄加1,下图表示经过了15次MinorGC后的堆内存状态。​经过15次MinorGC后的内存状态对于年轻代区域的内存收集,使用的是标记-复制算法,只是为了减少复制算法空白区域的内存浪费,并不是将内存一份为二,而是巧妙的将内存分为三个区域,预留的空白区域只占整个年轻代区域的1/10。

5.7 对象如何进入老年代

以上是年轻代的分配与回收问题,那对象如何进入老年代呢?个人认为对象进入老年代,可以分为2种类型6种情况。​对象晋升入老年代​对象晋升入老年代

第一种类型--直接分配:对象创建时直接分配到老年代具体分为3种情况。超过虚拟机PretenureSizeThreshold参数设置大小的对象,该参数的默认值是0,也就是说任何大小的对象都会先分配到Eden区。

超过Eden大小的对象

如果新生代分配失败,一个大数组或者大字符串

第二种类型--从年轻代晋升:从年轻代空间晋升到老年代也可分为3种情况。新生代分配担保,在执行MinorGC时要将Eden区存活的对象复制到Survivor区,但是Survivor区默认空间是只有新生代的2/10,实际使用的只有1/10,当Survivor区内存不够所有存活对象分配时,就需要将Survivor无法容纳的对象分配到老年代去,这种机制就叫分配担保。

对象年龄超过虚拟机MaxTenuringThreshold的设置值,最大为15,

Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半(TargetSurvivorRatio),年龄大于或等于该年龄的对象直接进入老年代。内存分配担保机制

在执行MinorGC时要将Eden区存活的对象复制到Survivor区,但是Survivor区默认空间是只有新生代的2/10,实际使用的只有1/10,当Survivor区内存不够所有存活对象分配时,就需要Survivor无法容纳将对象分配到老年代空间中,这种机制就叫分配担保,但是,老年代的空间也是有限的,如果老年代中空间也不够的话,那只能乖乖的执行一次FullGC了。

5.8 老年代回收算法-FullGC

当有对象要进入老年代,而老年代空间又不足时就会触发FullGC,当然,反过来说触发FullGC的条件不仅仅只是老年代空间不足,FullGC使用的算法是上面说的标记-整理算法。​完整堆内存回收过程

六、总结

判断对象是否可以回收是垃圾回收的基础与前提,通过可达性分析从GCRoots开始进行"顺藤摸瓜"找到不可达对象(可回收)。

对象生命周期的特征"朝生夕灭"与"越战越强"是垃圾回收算法的理论基础。

基础的垃圾回收算法有3种分别是 标记-清除算法、标记-负责算法、标记整理算法,都有各自的适应场合与优缺点。

分代垃圾算法根据对象生命周期的特征,将其划分到不同的区域,从而使用最适合的垃圾算法来进行优化。

在JDK8默认的配置下使用 新生代,老年代的垃圾回收策略,新生代区域使用标记-复制算法,老年代区域使用标记-整理算法。

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

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

相关文章

Gartner:2020 年 AI 平台魔力象限:意外多多

来源:云头条众多企业决策者向市场研究公司Gartner寻求企业软件堆栈方面的建议。魔力象限报告是Gartner发布的最可信、最真实、最权威的研究报告之一。由于它影响企业的采购决策,因此诸多供应商竭力想在报告中占有一席之地。Gartner最近发布了数据科学和机…

CentOS6.9编译安装LNMP环境

CentOS6.9编译安装LNMP环境 今天尝试一下用编译的方式来搭建lnmp运行环境。所有软件都采用当前最新版本,除了CentOS。这是由于目前企业大多数应该都还在使用CentOS6的缘故,并且CentOS7目前还在迭代中。虽说不会有大的改动,但也算不上完全稳定…

python实现号码簿_使用Python进行号码簿的格式转换

今天碰到一个问题,如何将功能机的号码簿转换到智能机中。但是 这款BBK手机没有对应的电脑端软件,所以备份号码簿并不方便。我的解决方案是将功能机的电话簿导出成.csv格式的文件。导出的格式如下: --------------- 姓名,电话&…

生物,AI,心理:目前的大脑/认知/意识/AGI/DRL模型

来源:人工智能前沿讲习一 基于生物和经验的模型首先是 2012 年的 Spaun,基于生物基础(脑图谱),类生物神经元(尖峰放电 SNN)。在训练后可完成多种识别和生成和反应任务。map the visual hierar…

java 使用nullable_Java Stream ofNullable(T)用法及代码示例

如果此流不为null,则ofNullable(T)方法将返回包含单个元素的顺序Stream,否则该方法将返回空Stream。它有助于处理空流和NullPointerException。用法:static Stream ofNullable(T t)参数:此方法接受单个参数t,该参数t是要返回其Str…

阿里云SLB负载均衡与使用SSL域名证书

阿里云SLB负载均衡与使用SSL证书 1.购买两台ECS服务器,这就是后台服务器,在这两个服务器上面部署你的网站,注意网站的端口要一样;比如都是 88。 2.在阿里云控制台的菜单里找到 负载均衡,创建一个SLB,把这两…

python数据标注工具_数据标注工具大全汇总,有了这些工具再也不用自己开发了...

数据标注工具大全汇总,有了这些工具再也不用自己开发了。 做数据标注三年了,总是遇到各种各样的需求,总是想找一款最高效,最快速,最好用的标注工具,最重要的是免费,经过三年的收集,大…

java 数据类型 string_java的基本数据类型和引用数据类型都有哪些,string属于什么类型...

基本数据类型有四类八种:第一类:逻辑型booleanboolean类型只允许取值true或者false,不可以为0或者非0的整数代替true和false,这点和C语言不同.第二类:文本型char字符常量为用单引号括起来的单个字符,例如: char ch1 a; char ch2 中;第三类:整数型(byte,short,int,long)byte…

决策智能(Decision Intelligence)二三事

来源:https://www.zhihu.com/people/wang-jing-28-89-94什么是决策智能?大家看到这四个字,大多数人心里的发问会是”什么是决策智能“呢?别说你没想,别骗我了。那么什么是决策智能呢?以下是维基的定义&…

python基础——面向对象的程序设计

python基础——面向对象的程序设计 1 什么是面向对象的程序设计 面向过程的程序设计的核心是过程,过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。 优点是:极大的降低了程序的复杂…

移动端 h5如何生成快捷方式_削微整理了几个经常在H5移动端开发遇到的东西

不用说我也知道,此类文章太多太多了,常见的譬如:viewport、强制浏览器全屏、IOS的Web APP模式、可点击元素出现阴影(这个我觉得真没必要去掉,用户点击是需要反馈的,而这个背景色刚刚好提供了一种反馈&#…

SCI至上只是结果,而不是原因

来源:赵斌科学网博客我从来不认为SCI一无是处,相反,我们大多数科研人员应该感谢它。中国科研评价体系中的的问题,不是破四唯,破SCI至上,而是打破特权、消除歧视。近日,教育部和科技部联合发文&a…

spark mysql 交互_Spark - 直接操作数据源 MySQL

如果我们的Mysql服务器性能不咋滴,但是硬盘很够,如何才能做各种复杂的聚合操作?答案就是使用spark的计算能力的,我们可以将mysql数据源接入到spark中。读取val mysqlDF spark.read.format("jdbc").option("driver…

maven+SSM框架工程搭建

1.百度下载 maven 和 tomcat 安装 配置环境变量 2.使用最新版eclipse 集成maven maven conf文件夹下的settings.xml文件配置存放maven仓库的位置,D:\hongzhimei\repository 为存放路径 3.新建工程 项目原型选择webapp项目 在buildPath中选择Edit更改为工作空间默认的…

flask session_Flask干货:Flask数据交换——Session的使用

上一次我们学习了Cookie,知道Cookie是保存在客户端的。那么有的小伙伴就问了,难道只有客户端能保存?服务器就不可以保存吗?!当然可以!Session就是另一种记录用户状态的机制。Flask的Session是基于Cookie实现…

神经科学中的数学之美

来源:数学中国“不偏袒地讲,数学,不但掌握着真理,还是至美之物。”——罗素关于美学最新的神经学研究显示,视觉、听觉和道德上的美感体验都与“情绪化大脑”的同一个区域有关:内侧眶额叶皮层(me…

调用相机

<input type"file" accept"image/*;capturecamera">转载于:https://www.cnblogs.com/intangible/p/6739063.html

java that关键字_Java中this关键字的几种用法

1 . 当成员变量和局部变量重名时&#xff0c;在方法中使用this时&#xff0c;表示的是该方法所在类中的成员变量。(this是当前对象自己)如&#xff1a;public class Hello {String s "Hello";public Hello(String s) {System.out.println("s " s);Syste…

手机端富文本编辑器_谷歌Pixel系列手机每月更新无痛刷机技巧

本文适合小白&#xff0c;高手可以关闭。&#xff08;以下技巧基本为Pixel3操作&#xff0c;window7平台&#xff0c;Pixel系列手机大同小异&#xff09;谷歌Pixel系列手机属于小众手机&#xff0c;当中刷机有技巧&#xff0c;本人也是小白&#xff0c;经过无数次的实践经验得出…

js中this的指向问题

JS 面试题&#xff1a; var obj { foo: function(){ console.log(this) } } var bar obj.foo obj.foo() // 打印出的 this 是 obj bar() // 打印出的 this 是 window 请解释最后两行函数的值为什么不一样。 ——- 初学者关于 this 的理解一直很模糊。今天这篇文章就要一次讲清…