网易首页 > 网易号 > 正文 申请入驻

一文就能理解的java垃圾回收机制

0
分享至

我们知道,在平时的开发当中,有时候我们需要创建大量的对象,如果我们动态创建的对象没有得到及时回收,持续堆积,最后会导致内存被占满,造成溢出。因此Java 提供了一种垃圾回收机制,在后台创建一个守护进程。该进程会在内存紧张的时候自动跳出来,把内存的垃圾全部进行回收,从而保证程序的正常运行。

在前面的文章中曾经分析了java8的内存结构和类的加载机制。这篇文章,我主要分析一下垃圾回收算法。为什么要安排这样的顺序去讲解,主要是根据类的生命周期来的。我们知道类的生命周期大体上包含了七个:

在《类的加载机制》一文中,曾经描述了前五个过程。垃圾回收机制就是描述的最后一个卸载过程,也就是把垃圾如何从内存中回收的问题。

有了这个概念,就可以开始今天的文章了:

主要涉及下面几个问题:
  • 首先,回顾一下jvm中的内存结构
  • 然后,确定那些对象是垃圾?
  • 其次,有哪些算法回收这些垃圾?
  • 接下来,jvm提供了哪些垃圾回收器
  • 最后,对垃圾回收机制进行一个总结

一、jvm的内存结构

我们的垃圾回收都是基于内存去回收的,因此,先要对内存结构有一个大概的了解,下面这一张是java7的内存结构:

从上面我们可以看到,Java内存运行时区域大概分了三部分,

  • 其中PC寄存器、java虚拟机栈、本地方法栈3个区域是所有线程独有的一块区域,随线程而生,随线程而灭。栈中的栈帧随着方法的进入和退出而有条不紊地执行着入栈和出栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要过多考虑回收的问题,因为方法结束或者线程结束,内存自然就跟随着回收了。

  • 而Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收是动态的,垃圾收集关注的是这部分的内存。

知道了我们要回收的这部分区域之后,下面就是确定那些对象是垃圾的问题了。

二、哪些对象是垃圾呢?

为了确定哪些对象是垃圾,jvm为我们提供了一些算法去判定。常见的判断是否存活有两种方法:引用计数法和可达性分析。

1、引用计数法

为每一个创建的对象分配一个引用计数器,用来存储该对象被引用的个数。当该个数为零,意味着没有人再使用这个对象,可以认为“对象死亡”。每当有一个地方去引用它时候,引用计数器就增加1。但是,这种方案存在严重的问题,就是无法检测“循环引用”:当两个对象互相引用,它俩的计数都不为零,因此永远不会被回收。而实际上对于开发者而言,这两个对象已经完全没有用处了。

比如我们都曾经遇到过:两个类相互是对方的成员变量,toString的时候,相互调用。造成循环引用。

因此,Java 里没有采用这样的方案来判定对象的“存活性”。

2、可达性分析

可达性分析基本思路是把所有引用的对象想象成一棵树,从树的根结点 GC Roots 出发,持续遍历找出所有连接的树枝对象,这些对象则被称为“可达”对象,或称“存活”对象。不能到达的则被可回收对象。

下面这张图就是可达性分析的描述:

我们发现,GC Roots 本身是一个出发点,也就是说我们每次进行可达性分析的时候都要从这个初始点出发。换句话说,初始点我们一定是可达的。那么,Java 里有哪些对象可以作为GC Roots呢?主要有以下四种:

  • 虚拟机栈(帧栈中的本地变量表)中引用的对象。

  • 方法区中静态属性引用的对象。

  • 方法区中常量引用的对象。

  • 本地方法栈中 JNI 引用的对象。

三、有哪些算法回收这些垃圾呢?

上面我们已经能够确定那些对象可以被视为垃圾了。下面我们可以分析一下,如何去回收这些垃圾,同样的,有一系列算法。首先我们定义一个规则确定那些是垃圾、存活对象、空白空间

1、标记-清理

第一步(标记),利用可达性遍历内存,把“存活”对象和“垃圾”对象进行标记。第二步(清理),我们再遍历一遍,把所有“垃圾”对象所占的空间直接 清空 即可。

结果如下:

特点:

  • 简单方便

  • 容易产生内存碎片

2、标记-整理

上面的方法我们发现会产生内存碎片,因此在这个方法中同样为两步:

第一步(标记):利用可达性遍历内存,把“存活”对象和“垃圾”对象进行标记。

第二步(整理):把所有存活对象堆到同一个地方,这样就没有内存碎片了。

结果如下:

特点:

  • 适合存活对象多,垃圾少的情况

  • 需要整理的过程

3、复制

将内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这一块用完了,就将还活着的对象复制到另一块上,然后再把使用过的内存空间一次性清理掉

过程如下:

特点:

  • 简单

  • 不会产生碎片

  • 内存利用率太低,只用了一半

四、堆和方法区的垃圾回收

上面我提到了三种方法来回收内存,下面要讲的分代回收算法是第四种。首先在文章的第一部分我们曾说到,java中的垃圾回收大致在两部分,第一个就是堆、第二个就是方法区。为此先看方法区是如何进行垃圾回收的。

1、方法区的垃圾回收

方法区又叫做永久代。永久代的垃圾回收主要有两部分:废弃常量和无用的类。

首先是废弃常量垃圾回收的一般步骤:

第一步:判定一个常量是否是废弃常量:没有任何一个地方对这个常量进行引用就表示是废弃常量。

第二步:垃圾回收

然后是无用的类垃圾回收的一般步骤

第一步:判定一个类是否是“无用的类”:需要满足下面三个条件

  • Java堆中不存在该类的任何实例,也就是该类的所有实例都被回收

  • 加载该类的ClassLoader已经被回收

  • 该类对应的Class对象在任何地方没有引用了,也不能通过反射访问该类的方法。

第二步:满足上面三个条件就可以回收了,但不是强制的。

注意:《java虚拟机规范》里面曾经说到过,不要求虚拟机对方法区进行垃圾回收。而且方法区进行垃圾回收性价比比较低。

2、Java 堆的垃圾回收:分代回收算法

在讲回收算法前,我们先来看一下 Java 堆的结构。

Java 堆空间分成了三部分,这三部分用来存储三类数据:

  • 刚刚创建的对象。

  • 存活了一段时间的对象。

  • 永久存在的对象。

也就是说,常规的 Java 堆至少包括了 新生代 和 老年代 两块内存区域,而且这两块区域有很明显的特征:

  • 新生代:存活对象少、垃圾多

  • 老年代:存活对象多、垃圾少

针对这种特点,我们有如下的几种方案;

(1)新生代-复制 回收机制

对于新生代区域,由于每次 GC 都会有大量新对象死去,只有少量存活。因此采用 复制 回收算法,GC 时把少量的存活对象复制过去即可。但是从上面我们可以看到,新生代也划分了三个部分比例:Eden:S1:S2=8:1:1。

其中 Eden 意为伊甸园,形容有很多新生对象在里面创建;S1和S2中的S表示Survivor,为幸存者,即经历 GC 后仍然存活下来的对象。

工作原理如下:

  1. 1. 首先,Eden对外提供堆内存。当 Eden区快要满了,触发垃圾回收机制,把存活对象放入 Survivor A 区,清空 Eden 区;

  2. 2. Eden区被清空后,继续对外提供堆内存;

  3. 3. 当 Eden 区再次被填满,对 Eden区和 Survivor A 区同时进行垃圾回收,把存活对象放入 Survivor B区,同时清空 Eden 区和Survivor A 区;

  4. 4. 当某个 Survivor区被填满,把多余对象放到Old 区;

  5. 5. 当 Old 区也被填满时,进行 下一阶段的垃圾回收。

下面看看老年代的垃圾回收。

(2)老年代-标记整理 回收机制

老年代的特点是:存活对象多、垃圾少。因此,根据老年代的特点,这里仅仅通过少量地移动对象就能清理垃圾,而且不存在内存碎片化。也就是标记整理的回收机制。既然是标记整理算法,而且老年代内部也不存在着内存划分,所以只需要根据标记整理的具体步骤进行垃圾回收就好了。

到了这,基本上对堆内存的分代回收机制进行了描述。但是要有一个问题没有解决,那就是jvm提供的垃圾回收器。

五、垃圾回收器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

在了解 垃圾回收器之前,首先得了解一下垃圾回收器的几个名词。

1. 吞吐量

CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值。比如说虚拟机总运行了 100 分钟,用户代码时间 99 分钟,垃圾回收 时间 1 分钟,那么吞吐量就是 99%。

2. 停顿时间

停顿时间 指垃圾回收器正在运行时,应用程序 的 暂停时间。

3. GC的名词

新生代GC:Minor GC

老年代GC:Major GC

4. 并发与并行

(1)串行(Parallel)

垃圾回收线程 进行垃圾回收工作,但此时 用户线程 仍然处于 等待状态

(2)并发(Concurrent)

这里的并发指 用户线程垃圾回收线程 交替执行。

(3)并行(Parallel)

这里的并行指 用户线程 和多条 垃圾回收线程 分别在不同 CPU 上同时工作。

下面其中垃圾回收器是基于HotSpot虚拟机。先给一张图看一下

在 JVM 中,具体实现有 Serial、ParNew、Parallel Scavenge、CMS、Serial Old(MSC)、Parallel Old、G1 等。在上图中,你可以看到 不同垃圾回收器 适合于 不同的内存区域,如果两个垃圾回收器之间 存在连线,那么表示两者可以 配合使用

下面对这其中垃圾回收器有一个了解。

第一种:Serial(单线程)

Serial 回收器是最基本的 新生代垃圾回收器,是单线程的垃圾回收器。采用的是 复制算法。垃圾清理时,Serial回收器不存在线程间的切换,因此,在单 CPU` 的环境下,垃圾清除效率比较高。

第二种:Serial Old(单线程)

Serial Old回收器是 Serial回收器的老生代版本,单线程回收器,使用 标记-整理算法。在 JDK1.5 及其以前,它常与Parallel Scavenge回收器配合使用,达到较好的吞吐量,另外它也是 CMS 回收器在Concurrent Mode Failure时的后备方案。

第三种:ParNew(多线程)

ParNew回收器是在Serial回收器的基础上演化而来的,属于Serial回收器的多线程版本,采用复制算法。运行在新生代区域。在实现上,两者共用很多代码。在不同运行环境下,根据CPU核数,开启不同的线程数,从而达到最优的垃圾回收效果。

第四种:Parallel Scavenge(多线程)

Parallel Scavenge回收器也是运行在新生代区域,属于多线程的回收器,采用复制算法。与ParNew不同的是,ParNew回收器是通过控制垃圾回收的线程数来进行参数调整,而Parallel Scavenge回收器更关心的是程序运行的吞吐量。即一段时间内用户代码运行时间占总运行时间的百分比。

第五种:Parallel Old(多线程)

Parallel Old回收器是Parallel Scavenge回收器的老生代版本,属于多线程回收器,采用标记-整理算法。Parallel Old回收器和Parallel Scavenge回收器同样考虑了吞吐量优先这一指标,非常适合那些注重吞吐量和CPU资源敏感的场合。

第六种:CMS(多线程回收)

CMS回收器是在最短回收停顿时间为前提的回收器,属于多线程回收器,采用标记-清除算法。

第七种:G1回收器

G1是 JDK 1.7中正式投入使用的用于取代CMS的压缩回收器。它虽然没有在物理上隔断新生代与老生代,但是仍然属于分代垃圾回收器。G1仍然会区分年轻代与老年代,年轻代依然分有Eden区与Survivor区。

G1首先将堆分为大小相等的 Region,避免全区域的垃圾回收。G1的分区示例如下图所示:

这种使用区域划分内存空间以及有优先级的区域回收方式,保证G1回收器在有限的时间内可以获得尽可能高的回收效率。

下面对这几种垃圾回收机制进行一个总结:

六、总结

OK,到这jvm的垃圾回收机制都已经讲完了,有问题还请大家提出来,欢迎批评指正。如果你喜欢,还请给个关注,谢谢。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
据说吓死过人,国产恐怖片巅峰之作

据说吓死过人,国产恐怖片巅峰之作

剧透电影
2024-04-15 17:54:16
中方航母突然宣布出手,四国军演刚开始,就落入了我军“包围圈”

中方航母突然宣布出手,四国军演刚开始,就落入了我军“包围圈”

绝对军评
2024-04-26 08:18:33
高校一对情侣校园长椅上激情互动,被拍下全过程,保安迅速撤走长椅

高校一对情侣校园长椅上激情互动,被拍下全过程,保安迅速撤走长椅

趣笔谈
2024-04-25 23:30:49
拜仁球迷请愿留下图赫尔:他是球队主帅正确人选,不需要朗尼克

拜仁球迷请愿留下图赫尔:他是球队主帅正确人选,不需要朗尼克

直播吧
2024-04-25 21:04:15
男人过了六十,若还有女人愿让你“搞”,往往是出于这些原因

男人过了六十,若还有女人愿让你“搞”,往往是出于这些原因

户外阿崭
2024-04-26 03:43:07
吴亦凡有没有后悔没有给都美竹索要的800万,如果最初给了800万是不是就不会发生后面的事?

吴亦凡有没有后悔没有给都美竹索要的800万,如果最初给了800万是不是就不会发生后面的事?

阿芒娱乐说
2024-04-24 07:55:26
被特斯拉毁约应届生发声:协议违约金是0元,对方给了一个月底薪

被特斯拉毁约应届生发声:协议违约金是0元,对方给了一个月底薪

环球网资讯
2024-04-25 09:42:09
C919不适合飞京沪线?C919首飞北京后,为什么改飞沪蓉航线?

C919不适合飞京沪线?C919首飞北京后,为什么改飞沪蓉航线?

文学科技圈
2024-04-26 04:00:35
“坚决不回国”!华人富豪将被永久逐出美国,与三个孩子分离

“坚决不回国”!华人富豪将被永久逐出美国,与三个孩子分离

起喜电影
2024-04-25 19:57:33
恨铁不成钢!曝周琦禁赛广东球员豪车炸街,徐杰被牵连,杜锋上火

恨铁不成钢!曝周琦禁赛广东球员豪车炸街,徐杰被牵连,杜锋上火

娱乐圈酸柠檬
2024-04-25 17:08:10
河南:姑娘老是相亲不成功,媒婆看到她都怕了,网友:倒贴都不要

河南:姑娘老是相亲不成功,媒婆看到她都怕了,网友:倒贴都不要

户外阿崭
2024-04-24 15:38:32
破格提拔的25岁女干部,新职务明确!参加工作不到3年

破格提拔的25岁女干部,新职务明确!参加工作不到3年

上观新闻
2024-04-25 17:25:11
广东20岁女孩相亲被5人看中,秒杀6位大龄剩女,网友:很识货

广东20岁女孩相亲被5人看中,秒杀6位大龄剩女,网友:很识货

户外阿崭
2024-04-25 12:45:01
金融制真的要来了么?会有什么后果?

金融制真的要来了么?会有什么后果?

老方看财
2024-04-25 21:39:25
上海南站是否是城市规划中的一个败笔?

上海南站是否是城市规划中的一个败笔?

刘哥谈体育
2024-04-26 07:30:22
比亚迪再发“全球颠覆性”创新技术

比亚迪再发“全球颠覆性”创新技术

电动知家
2024-04-23 15:10:24
“不接受篮协判罚”,周琦禁赛杜锋强硬表态,广东男篮已无退路

“不接受篮协判罚”,周琦禁赛杜锋强硬表态,广东男篮已无退路

姜大叔侃球
2024-04-25 12:37:36
曝何超莲窦骁已分居!两人未进行婚姻登记,婚前婚后互动差距大

曝何超莲窦骁已分居!两人未进行婚姻登记,婚前婚后互动差距大

萌神木木
2024-04-25 20:29:53
1女8夫!广西26岁女孩5年内结8次婚,民政局:所有结婚证都合法!

1女8夫!广西26岁女孩5年内结8次婚,民政局:所有结婚证都合法!

青栀伊人
2024-04-24 23:45:33
毛星火向往朝鲜,让我们祝福他!

毛星火向往朝鲜,让我们祝福他!

古老板的老巢
2024-04-23 12:09:40
2024-04-26 11:26:44
java的架构师技术栈
java的架构师技术栈
java架构师一系列文章
84文章数 3701关注度
往期回顾 全部

科技要闻

雷军周鸿祎出圈:中年CEO,抢着当网红

头条要闻

牛弹琴:"欧洲一姐"被指比美国还美国 马克龙想炒掉她

头条要闻

牛弹琴:"欧洲一姐"被指比美国还美国 马克龙想炒掉她

体育要闻

库里当选最佳关键球员 10项数据联盟第一

娱乐要闻

心疼!伊能静曝儿子曾被狗仔追到洗手间

财经要闻

24年后再产纯净水 农夫山泉为何要打自己脸

汽车要闻

全新哈弗H9亮相 大号方盒子硬派SUV入列

态度原创

健康
艺术
亲子
本地
公开课

这2种水果可降低高血压死亡风险

艺术要闻

艺术名画︱爱尔兰画家大卫·科因的刀画作品

亲子要闻

子宫内压力过大可能影响宝宝颜值……一起来听健康早闻!2024年4月26日

本地新闻

云游中国|苗族蜡染:九黎城的“潮”文化

公开课

睡前进食会让你发胖吗?

无障碍浏览 进入关怀版