前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入浅出java虚拟机系列:(二)GC&垃圾收集算法

深入浅出java虚拟机系列:(二)GC&垃圾收集算法

作者头像
爱笑的架构师
发布2020-09-24 11:37:31
3010
发布2020-09-24 11:37:31
举报
文章被收录于专栏:爱笑的架构师爱笑的架构师

1. 概述

GC需要完成的三件事:

哪些内存需要回收?

什么时候回收?

如何回收?

其中 程序计算器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭,不需要过多考虑回收的问题。

而java 堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的。

2. 判断对象死亡or生存?(重要)

heap里面存放着几乎所有的java对象,GC在对堆进行回收前,首先要判断哪些对象是存活着,哪些已经死去。

2.1 引用计数算法(Reference Counting)

描述:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用时效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。

COM、Python语言使用这种算法,但是,主流java 虚拟机并没有选用这种算法来管理内存, 最主要的原因是:它很难解决对象之间相互循环引用的问题。

2.2 可达性分析算法(Reachability Analysis)

基本思路:通过一系列的称为“GC Roots“的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots 没有任何引用链相连(用图论,即从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

主流商用语言,如java、C#等都是使用这种算法。

2.3 谈引用

java将引用分为:强引用、软引用、弱引用、虚引用4种,这4种引用强度依次逐渐减弱。

强引用:类似 Object obj = new Object();垃圾收集器永远不会回收引用所指对象;

软引用:描述一些还有用但并非必需的对象;在系统内存吃紧,要发生OOM之前,会把这些对象列入第二次垃圾回收的范围中,如果回收之后内存还是不够,才会发生OOM。

弱引用:也是用来描述有用但并非必需的对象;被弱引用关联的对象只能生存到下一次GG发生之前。当GC工作时,无论当前内存是否足够,都会回收被弱引用关联的对象;

虚引用:无法通过虚引用取得一个对象的实例。设置虚引用的唯一目的是:能在这个对象被收集器回收时收到一个系统通知。

2.4 生存还是死亡

要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析时发现没有与GC Roots 相连接的引用链,那它会被第一次标记并且进行一个筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法以及被虚拟机调用过,虚拟机将这两种情况都视为”没有必要执行“。

如果被判定为有必要执行,则这个对象将会放置在F-Queue的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的Finalize线程去执行它。finalize()是对象逃脱死亡命运的最后一次机会,稍后GC将对整个F-Queue中 的对象进行第二次小规模的标记。

2.5 回收方法区(也称永久代)

在方法区中进行GC性价比一般比较低;

永久代的垃圾回收主要包括两部分内容:废弃常量和无用的类;

判断一个类是否是“无用的类”,需同时满足以下三个条件:

a)该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例;

b)加载该类的ClasLoader已经被回收;

c)该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法;

3. 垃圾收集算法(重要)

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

缺点:a)效率问题:标记和清除两个过程的效率都不高;

b)空间问题:产生大量碎片;

3.2 复制算法(Coping)(适用新生代)

优点:实现简单,运行高效;

缺点:内存浪费了一半;

现在的商业虚拟机都采用这种算法来回收新生代;将新生代内存划分为一块较大的Eden和两块较小的Survivor空间(默认是8:1:1),当回收时将Eden和Survivor中还存活的对象一次性复制到另外的一块Survivor空间上,最后清理掉Eden和刚那块Survivor空间。

HotSpot默认Eden和Survivor比例为8:1,只有10%的内存会被浪费。有时我们没办法保证每次回收都只有不多余10%的对象存活,当survivor不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。

3.3 标记-整理算法(Mark-Compact)(适用老年代)

复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。

标记-整理算法中标记仍然与标记-清除算法一样,但“整理”是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

3.4 总结

当前商业jvm的GC都采用”分代手机(Generational Collection)算法。

新生代中:每次GC都有大批对象死去,只有少量存活,选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

老年代:对象存活率高,没有额外空间对它进行分配担保,采用“标记-整理”算法。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-04-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档