一张图让你看懂JVM之垃圾回收算法详解

前言

从上面这个图我们总体上对JVM的结构特别是内存结构有了比较清晰的认识,虽然在JDK1.8+的版本中,JVM内存管理结构有了一定的优化调整。主要是方法区(持久代)取消变成了直接使用元数据区(直接内存)的方式,但是整体上JVM的结构并没有大改,特别是我们最为关心的堆内存管理方式并没有在JDK1.8+的版本中有什么变化,所以图中的结构整体上是没有什么不准确的,之所以将方法区以及持久代标注出来,主要还是为了起到对比认识的作用,大家知道就可以了。

关于持久代元数据区的使用问题,目前可以理解就是使用的物理内存,理论上是不受JVM自动内存回收机制管理的,如果不设置参数大小默认最大使用限制就是操作系统可用物理内存的大小,设置了-XX:MetaspaceSize参数的话,JVM就会在使用物理内存空间时自己进行限制。

至于直接内存与物理内存到底是不是一回事,我认为对于我们理解上没有区别,只是概念的区别,另外就是对这块内存使用细节上的区别,如果不受JVM的自动回收管理,那么怎么管理呢?说到底还是JVM本身在直接使用物理内存或者说是直接内存(用时直接“malloc”物理内存区域,而不再是JVM进程启动时初始化的内存区域),还有一种概念叫native memory,说实话我暂时还不理解他们到底有啥区别,如果大家对这些概念有更好的认识,也可以给我留言哦!之所以对这几个问题做一些笔墨的说明,主要是在之前的文章中大家对此提出了疑问,所以正好在这节的内容中进行下阐述。

回到今天的主题,我们知道JAVA最大的优点就是可以实现自动内存管理,这极大的便利了JAVA程序员,降低了使用成本。但这也使得平时我们在使用JAVA编程时不太关注JVM到底是怎样进行内存回收的,只有在需要实际对JVM进行系统性能调优,这里的场景可能是在系统面临极致性能优化要求时,我们才发现需要对JAVA的整体内存结构以及内存回收机制要有一定的认识和了解才行。

从上面的图中,我们也大致对整个垃圾回收系统进行了标注,这里主要涉及回收策略、回收算法、垃圾回收器这几个部分。形象一点表述,就是JVM需要知道那些内存可以被回收,要有一套识别机制,在知道那些内存可以回收以后具体采用什么样的回收方式,这就需要设计一些回收算法,而具体的垃圾回收器就是根据不同内存区域的使用特点,采用相应地回收策略和算法的具体实现了。

在👆图中,我们也标注了不同垃圾回收器所适用的特定内存区域,对于JVM垃圾回收这块的优化,就是我们需要在了解这些垃圾回收算法、垃圾回收器特点后能够根据自己应用的场景选择合适的垃圾收集器,以及各区域垃圾收集器的搭配关系。下面我们就从这几个方面给大家介绍,JVM的垃圾回收相关的知识点。

回收策略

我们知道,JVM进行内存回收的主要目的是为了回收不再使用的内存,因为在进行JAVA程序编写时,我们只有new的操作,而不需要收工释放不再使用的空间,如果这些空闲内存不能及时被回收,很快我们的JVM内存空间就会泄露(新申请内存空间的操作失败,导致程序报错),所以回收不再使用的内存的目的则是为了及时释放空间,腾笼换鸟,以防止内存泄漏

那么问题来了,JAVA程序申请了那么多的内存空间,那些内存才能被认定是不再使用的内存呢?搞错了,如果把正在被程序使用的内存给释放了,程序逻辑就空指针异常了!

我们知道在JVM中内存分配的基本粒度主要是对象、基本类型。而基本类型的使用主要是包括在对象中的局部变量,所以回收对象所占用的内存是JAVA垃圾回收的主要目标。

那么如何判断对象是处于可回收状态的呢?在主流的JVM中是采用“可达性分析算法”来进行判断的。

这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,并从这些节点开始往下进行搜索,搜索走过的路径我们称之为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,我们就称之为对象引用不可达,则证明这个对象是不可用的,就可以暂时判定这个对象为可回收对象。示意图如下:

在图中虽然Obj F与Obj J之间互相有关联但是它们到GC Roots是不可达的,所以将会被判定为可回收对象。既然如此,什么样的对象可以作为GC Roots对象呢?

在JAVA中可以被作为GC Roots的对象主要是:虚拟机栈-栈帧中的本地变量表所引用的对象、方法区(<JDK1.8)中类静态属性所引用的对象/常量属性所引用的对象、本地方法栈中引用的对象。

这里还需要注意一个小的细节,就是被判定为对象不可达的对象也并非会被立刻回收,在学习JAVA语法是我们应该学习过finalize()方法,如果对象重写了finalize方法,并重新把this关键字赋值给了某个类变量或对象的成员变量的话,该对象就会被"救活",具体过程可参考上图所示,只是这种方式并不鼓励大家使用,了解下就行。

在关于如何判定对象是否属于不再使用的内存时,还有个通常会被大家错误认为是JVM使用的方式-“引用计数法”,事实上引用计数法的实现比较简单,判定效率也比较高,在Python语言中就使用了这种算法进行内存管理,但是它有一个比较难解决的对象之间循环引用的问题,所以在JAVA虚拟机里并没有选用“引用计数法”来管理内存。这个问题很多人都会搞错,包括有很多年开发经验的程序员,需要大家注意下!

回收算法

在JVM中主要的垃圾收集算法有:标记-清除、标记-清除-压缩(简称“标记-整理”)、标记-复制-清除(简称“复制”、分代收集算法。这几种收集算法互相配合,针对不同的内存区域采取对应的收集算法实现(这里具体是由相应的垃圾收集器实现)

下面我们就分别来看下这几种收集算法的特点:

1)、标记-清除

标记-清除算法是最为基础的一种收集算法,算法分为:“标记”和“清除”两个阶段。首先标记出所有需要回收的对象(标记的过程就是上面介绍过的根节点可达算法),在标记完后统一回收所有被标记对象占用的内存空间。

示意图如下:

这种收集算法的优点是简单直接,不会影响JVM进程的正常运行。而其缺点也是非常明显,首先,这样的回收方式会产生大量不连续的内存碎片,不利于后续连续内存的分配;其次,这种方式的效率也不高。

2)、标记-复制-清除

这种算法的思路是将可用的内存空间按容量划分为大小相等的两块,每次只使用其中一块。当这一块使用完了,就将还存活着的对象复制到另外一块上面(移动堆顶指针,按顺序分配内存),然后再把已使用过的内存空间一次清理掉。

示意图如下:

这种收集方式比较好的解决了效率和内存碎片的问题,但是会浪费掉一般的内存空间。目前此种算法主要用于新生代回收(文顶的图中有标注)。

因为新生代的中98%的对象都是很快就需要被回收的对象,这一点大家在编程时可以体会到,所以并不需要1:1的比例来划分内存空间,在新生代中JVM是按照“8:1:1”的比例(文顶图中有标注)来将整个新生代内存划分为一块较大的Eden区和两块较小的Survivor区(S0、S1)。

每次使用Eden区和其中一个Survivor区,当发生回收时将Eden区和Survivor区中还存活的对象一次性复制到另一块Survivor区上,最后清理掉Eden区和刚才使用过的Survivor区。理想情况下,每次新生代中的可用空间是整个新生代容量的90%(80%+10%),只会有10%的内存会被浪费。实际情况中,如果另外一个10%的Survivor区无法装下所有还存活的对象时,就会将这些对象直接放入老年代空间中(这块在后面的分代回收算法会说到,这里先了解下)。

3)、标记-清除-压缩

如果在对象存活率较高的情况下,仍然采用复制算法的话,因为要进行较多的复制操作,效率就会变得很低,而且如果不想浪费50%的内存空间的话,就还需要额外的空间进行分配担保****,以应对存活对象超额的情况显然老年代不能采用2)中的复制算法。

根据老年代的特点,标记-清除-压缩(简称标记-整理)算法应运而生,这种算法的标记过程仍然与“标记-清除”算法一样,只是后续的步骤不再是直接清除可以回收的对象,而是将所有存活的对象都向一端移动后,再直接清理掉端边界以外的内存。

示意图如下:

4)、分代回收算法

实际上在讲解复制算法时已经涉及到了分代回收的内容,这种算法根据对象存活周期的不同将内存划分为几块,Java中主要是新生代、年老代这样就可以根据各个年代的特点,采用合适的收集算法了在文顶的图中已经标示,新生代采用了复制算法,而老年代采用了整理算法这里就不再赘述

垃圾回收器

关于垃圾回收器部分的内容,由于篇幅的关系会在后续的《一张图让你看懂JVM之垃圾回收器详解》一文中进行讲解,敬请关注!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容