GC回收算法--当女友跟你提分手!

Java语言引入了垃圾回收机制,让C++语言中令人头疼的内存管理问题迎刃而解,使得我们Java狗每天开开心心地创建对象而不用管对象死活,这些都是Java的垃圾回收机制带来的好处。但是Java的垃圾回收机制的核心原理是什么呢?今天我们来聊聊GC回收算法吧。

JVM的GC回收场景很复杂,不是单个算法就可以搞定的,大致可以分为可达性分析算法、标记-清除算法、标记-整理算法、分代回收算法、复制算法

广场上,女朋友突然跟你闹分手,然后头也不回地一个人走了,留下你一个人站在树下,BGM缓缓响起“雪花飘飘 北风啸啸 天地 一片 苍茫~~~”树叶纷纷落下,这时的你仿佛被夏洛特里的元华附身,成了全世界最悲伤的人。当你沉浸在悲伤不可自拔,旁边的环卫大妈一脸嫌弃看着你“年轻人你挪一下,别挡到我扫地”。

对你没猜错,地上的落叶,就是GC垃圾回收算法的核心--可达性分析算法

可达性分析算法

轻风乍起,泛黄的树叶纷纷掉下,刚分手的你不禁长叹“叶子的离开是风的追求还是树的不挽留”,当叶子从枝头掉落的那一刻,它跟树就再也没有任何关系。同样的,可达性分析算法的基本思路就是JVM内存中的对象以树的形式管理,我们称之为"GC tree"。GC tree的根节点叫做GC Roots,通过一些列GC Roots为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象不可用了。

图中的对象1、2、3、4不管哪个都可以找到与GC Roots相连的引用链,属于存活对象,而对象5、6、7虽然彼此相互有联系,但是他们到GC Roots是不可达的,所以属于死亡对象。

有哪些对象可以作为GC Roots呢?

虚拟机栈(栈桢中的本地变量表)中的引用的对象
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈中JNI(Native方法)的引用的对象

标记-清除算法(Mark-Sweep) 分手不是马上分

二次标记

当女朋友跟你闹分手,她是真的要跟你分吗?太天真了少年!!!女人都是感性动物,刀子嘴豆腐心,她只是给你判了个死缓,如果你什么都不做,那我没话说,注孤生吧小伙子;如果你态度端正,那你还有得救!

同样的,当GC线程遍历GC tree检测到无用对象的时候,并不是立马人道毁灭,只是先给它做个标记,告诉对象你已经上了枪毙名单。这里是第一次标记

挽留爱情该如何做?说情话哄她,拉她去心心念念的馆子吃顿好的,又或者去商场给她买向往已久的迪奥999口红······这些套路我就不说了,反正只要能让女朋友开心,什么付出都是值得的。

当对象第一次被标记的时候,GC线程会去检查此对象是否有必要执行finalize()方法。finalize()方法还记得吧?finalize()定义:finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。什么意思?就是意味着如果我们重写该方法的话,那么在GC回收之前该方法会被执行。

但是有两种情况GC线程会认为没有必要去执行。

1.对象没有覆写finalize()的。女友跟你闹分手,你却像没事人一样回家继续撸游戏,小伙子你心可真大。

2.finalize()已经被虚拟机执行过的。女友此时内心OS:上次吵架你送我一只迪奥999赔罪,这次你又送,你就不知道我这段时间一直想买萝卜丁吗?一定是在敷衍我!呵!男人!

生存还是死亡 要爱情还是要自由 That is a question!

如果对象被判定需要执行finalize()方法,那么它将会放置在一个叫F-Queue的队列里面挨个等待执行,对象自我救赎的机会来了!如果对象想在finalize()中成功拯救自己,只要重新与GC Roots建立关联即可,比如把自己赋值给某个类对象或者对象的成员变量,那么在第二次标记的时候它从“即将回收”的集合中中被移除;如果这时它还没有建立关联,那么它这次真的是GG了,我们用一首《凉凉》给它送别吧。

图解:

以上就是标记-清除算法,不过它有两点不足之处

1.效率问题,标记和清除过程的效率不高。

2.空间问题,标记清除后会产生大量不连续的内存碎片,碎片太多可能会导致以后在程序中需要分配占用较大连续空间的对象(如数组)时,无法找到足够的连续内存而不得不提前触发下另一次垃圾收集动作。

为了解决这些问题,“复制算法”应运而生。

复制算法(Copying) 物种大逃亡 诺亚方舟!

复制算法思路比较简单:将内存按容量划分为大小相等的两块,每次只使用其中的一块。当一块内存空间满了,就将还活着的对象复制到另外一块,然后再将之前那块内存空间彻底清空。有点像《圣经》里的一个故事:大洪水要来了,生物纷纷逃上诺亚方舟以躲避灾难。这样玩的话每次都是对整个半区进行内存回收,内存分配的时候也不用考虑内存碎片的情况,简单粗暴让人喜欢!只不过这种算法将内存缩小为了原来的一半,代价太高昂了,我们要知道,内存是很宝贵的资源!

黄金比例 8:1:1

医学研究证明,感冒是由病毒引起的。咳咳开个玩笑!软件团队研究表明,内存中的绝大部分对象都是“朝生暮死”的,所以完全没必要非要按照1:1的比例来玩。而是把内存分成了一块较大的Eden区(伊甸区)和两块较小的Survivor区(幸存区),每次都使用伊甸区和其中一块幸存区(我们取个别名叫幸存者1号吧,另外一块取名幸存者2号)。当回收的时候,将伊甸区和幸存者1号区域里面的对象一次性复制到幸存者2号里面,最后对伊甸区和幸存者1号进行清算,里面的所有对象不管生存还是死亡彻底清除干净,比灭霸打个响指还厉害!

图解:

现在HotSpot虚拟机默认伊甸区和两块幸存区的比例大小为8:1:1,这样平时工作的时候,只有10%的内存会被浪费掉,这样是不是很划算呢?

看到这里有人鞋会问,幸存区为什么要分为两块?比例9:1才是最完美吧?NO!NO!NO!,这里不得不引出另一个算法--分代收集算法了。

分代收集算法(Generational Collection) 历经考验 修成正果

有没有发现,所谓的爱情其实都是要经历无数次的磨合,无数次的考验,在一起的两个人只有经受住了这些磨难,才会走进婚姻的殿堂,有了圆满的结局,而经受不住这些考验,那双方就只有各自安好,相忘江湖。

在我们每次GC回收的时候,都会有一小部分对象活下来,然后一直活到下一次GC再次被检测。最近很火的吃鸡游戏,玩家不管用什么手段,刚枪也好苟也好,只求活下去成为最后的幸存者。而Java对象也是这样,经历GC的层层考验,最终成了打不死的小强。这时候该轮到GC不爽了,你丫的每次都浪费我的时间,小强内心OS"就喜欢看你不爽我又干不掉我的样子!",对于这批顽固分子,GC作为执法者决定眼不见为净,于是委托JVM专门划分出一块区域给他们颐养天年,从此天涯是路人。而划分出的这块区域就是赫赫有名的“老年代”了,而与之相对应的就是之前GC频繁的“新生代”。

一个对象该如何从“新生代”跑到“老年代”去呢?

我们创建一个对象,它的对象头里面会有一个GC分代标识,每经历一次GC如果能活下来该标识+1,当加到一定次数后,GC会判定该对象是个老流氓,于是乎把它从“新生代”转移到“老年代”了,安排!具体参考我的另一篇博客《假如Java对象是个人······》

新生代每经历一次GC,幸存者2号区域活下来的对象年龄标识自动+1,然后判断是否满15岁(默认值15次),如果满15岁了,那么就从幸存者2号复制到“老年代”里面取颐养天年,如果没有的话,那么就复制到幸存者1号区域里面去,然后幸存者2号区域被清空。

由于老年代对象存活率极高,用不着复制算法这一套。于是有人提出了另外一种算法叫做“标记-整理算法”

标记-整理算法(Mark-Compact)

标记-整理算法其实基本过程跟“标记-删除”算法差不多,只不过后续的步骤不是对无用对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理到端边界以外的内存。这样就完美解决了“标记-清除算法”内存碎片化的问题。

图解:

讲到这里,JAVA的GC回收算法基本就差不多了。我们的GC就是针对内存中的不同区域,采取合理的算法从而达到自动清理的效果。新生代的对象大多数朝生暮死,就采用“复制算法”,老年代的对象存活率极高,就采用“标记-删除算法”或者“标记-整理算法”。

现在是不是觉得GC回收算法没有想象中那么神秘?希望我的理解能给你带来一点帮助,由于人懒,图片都是网上直接拿来用的。另外,如果现实中跟女友有摩擦,该服软还是得服软,男人就应该表现得大度一点,毕竟两个人相处不易,更何况她还是将和你共度余生的人,不要因为一时冲动而抱憾终生。额······我仿佛又闻到了爱情的酸臭味!

参考资料:《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》

原文地址:https://www.cnblogs.com/missOfAugust/p/9528166.html

时间: 2024-09-28 03:06:39

GC回收算法--当女友跟你提分手!的相关文章

GC回收算法

GC回收算法 https://www.cnblogs.com/missOfAugust/p/9528166.html Java语言引入了垃圾回收机制,让C++语言中令人头疼的内存管理问题迎刃而解,使得我们Java狗每天开开心心地创建对象而不用管对象死活,这些都是Java的垃圾回收机制带来的好处.但是Java的垃圾回收机制的核心原理是什么呢?今天我们来聊聊GC回收算法吧. JVM的GC回收场景很复杂,不是单个算法就可以搞定的,大致可以分为可达性分析算法.标记-清除算法.标记-整理算法.分代回收算法

老年代 CMS gc回收算法 对hbase的影响

老年代 CMS gc回收算法 对hbase的影响***** 参考链接: 深入研究java gc https://blog.51cto.com/12445535/2372976CMS失败模式(CMS Failure Mode)1.上文提到在正常的情况下CMS整个流程的暂停时间都是很短的,一般也就在10ms-100ms左右.2.然而这与线上的情况并不相符,线上集群在读写压力很大的情况下,经常会出现长时间的卡顿,有些卡顿甚至长达几分钟,导致很严重的读写阻塞,甚至会造成Region Server和Zoo

GC回收算法&&GC回收器

GC回收算法 什么是垃圾? 类比日常生活中,如果一个东西经常没被使用,那么就可以说是垃圾. 同理,如果一个对象不可能再被引用,那么这个对象就是垃圾,应该被回收. 垃圾:不可能再被引用的对象. finalize方法 在对象没有被引用时调用 在Object类里定义 新生代与老年代 IBM公司的研究表明,在新生代中的对象 98% 是朝生夕死的. 在实际的 JVM 新生代划分中,不是采用等分为两块内存的形式.而是分为:Eden 区域.Survivorfrom 区域.Survivorto 区域 这三个区域

java虚拟机学习总结之GC回收算法与GC收集器

GC回收算法 1.标记清除算法分为标记阶段和清除阶段标记阶段:通过特定的判断方式找出无用的对象实例并将其标记清除阶段:将已标记的对象所占用的内存回收缺点:运行多次以后容易产生空间碎片,当需要一整段连续内存时虽然空间足够但是无法分配,会多次触发GC操作. 2.复制算法为了提高标记清除算法的效率,减少内存碎片的产生而出现的,该算法将内存空间分为两个完全相同的两部分,每次只使用其中的一部分.分为标记阶段.复制阶段和清除阶段标记阶段:同标记清除算法的标记阶段一致复制阶段:将为标记的对象全部复制到另一块未

初步了解JVM第三篇(堆和GC回收算法)

在<初步了解JVM第一篇>和<初步了解JVM第二篇>中,分别介绍了: 类加载器:负责加载*.class文件,将字节码内容加载到内存中.其中类加载器的类型有如下:执行引擎:负责解释命令,提交给操作系统执行. 启动类加载器(Bootstrap) 扩展类加载器(Extension) 应用程序类加载器(AppClassLoader) 用户自定义加载器(User-Defined) 执行引擎:负责解释命令,提交给操作系统执行. 本地接口:目的是为了融合不同的编程语言提供给Java所用,但是企业

GC垃圾收集算法

GC判断对象是否存活算法 1> 引用计数算法 2> 根搜索算法(GC Root) GC垃圾收集算法 1> 标记清除算法(Mark-Sweep):分为标记和清除两个阶段,首先标记出可以回收的对象,标记完后统一回收.缺点如下: a) 效率低:标记和清除过程效率都不高: b) 空间问题:清除之后产生大量不连续的内存碎片. 2> 复制算法(Copying):将内存划分成大小相等的两块,每次只使用其中的一块,当需要清理时,就直接将存活的对象复制到另一块.这种方式实现简单效率高,也不会存在碎片

jvm垃圾回收算法整理

java推荐 内存的自动化整理 也就是自动化解决给对象分配内存以及回收对象的内存  ,这两个问题也是主要针对java的内存模型 堆 :有效解决内存丢失等问题: 1.内存分类: 新生代: eden内存 新建的对象存储的位置 survivor0 当eden内存空间存满之后就会将存活的对象进行复制进入survivor0空间,eden内存空间进行一次GC回收 survivor1 当Eden内存和survivor0都存满之后,就会将存活的对象复制进入survivor1空间,eden和survivor0都回

Java GC 垃圾回收算法 内存分配

垃圾回收(Garbage Collection, GC)是Java不同于c与c++的重要特性之一. 他帮助Java自动清空堆中不再使用的对象. 由于不需要手动释放内存,程序员在编程中也可以减少犯错的机会. 利用垃圾回收,程序员可以避免一些指针和内存泄露相关的bug(这一类bug通常很隐蔽). 垃圾回收实际上是将原本属于程序员的责任转移给计算机. GC需要完成的3件事情: 哪些内存需要回收 什么时候回收 如何回收 1 回收那些对象? 在Java中采用可达性分析算法来判定对象是否存活,是否可以被回收

JVM优化 垃圾回收 算法 垃圾收集器 GC日志可视化查看

今日内容了解什么是垃圾回收掌握垃圾会回收的常见算法学习串行.并行.并发.G1垃圾收集器学习GC日志的可视化查看 1.什么是垃圾回收?程序的运行必然需要申请内存资源,无效的对象资源如果不及时处理就会一直占有内存 资源,最终将导致内存溢出,所以对内存资源的管理是非常重要了. 1.1.C/C++语言的垃圾回收在C/C++语言中,没有自动垃圾回收机制,是通过new关键字申请内存资源,通过delete 关键字释放内存资源.如果,程序员在某些位置没有写delete进行释放,那么申请的对象将一直占用内存资源,