Java垃圾回收、引用计数法、根可达算法

1.概述

提到垃圾回收,顾名思义,就是把已经分配出去的,但却不再使用的内存回收回来。对于JVM来说,垃圾指的是在堆中死亡的对象所占据的内存空间。

那么自然而然的,我们就能够提出一个问题:怎么知道对象死没死?由这个问题让我们引出俩个比较有名的思路:

1.引用计数法

引用计数法是一个颇为古老的方式,原因它有致命的缺点。先不说缺点,咱们看一看它的思路。

它的做法是为每个对象添加一个引用计数器,用来统计指向该对象的引用个数。一旦某个对象的引用计数器为0,则说明该对象已经死亡,便可以被回收了

它的具体实现是这样子的:如果有一个引用,被赋值为某一对象,那么将该对象的引用计数器+1。如果一个指向某一对象的引用,被赋值为其他值,那么将该对象的引用计数器 -1

对于引用计数法来说,除了需要额外的空间来存储计数器,以及繁琐的更新计数器以外;引用计数法还有一个重大的漏洞:无法处理循环引用

假设对象 a 与 b 相互引用,除此之外没有其他引用指向 a 或者 b。在这种情况下,a 和 b 实际上已经死了,但由于它们的引用计数器皆不为 0,在引用计数法的心中,这两个对象还活着。因此,这种情况下就造成了内存泄露。

1.png

因为main方法才是我们需要的对象,正在使用的,那么 A-B-C-D都是在使用的,但是 E-F-H三者互相引用,导致无法用标记清除法回收。

所以目前 Java 虚拟机的主流垃圾回收器采取的是可达性分析算法。

2.可达性分析

2.1、基本概念

这种算法的思路在于:将一系列被称为GC Roots的变量作为初始的存活对象合集,然后从该合集出发,所有能够被该集合引用到的对象,并将其加入到该集合中,而不能被该合集所引用到的对象,并可对其宣告死亡。

那么什么是 GC Roots 呢?

注意这句话:GC Roots是一些由堆外指向堆内的引用,

一般而言,GC Roots 包括(但不限于)如下几种:

Java 方法栈桢中的局部变量;已加载类的静态变量;JNI handles;已启动且未停止的 Java 线程。可达性分析可以解决引用计数法所不能解决的循环引用问题。举例来说,即便对象 a 和 b 相互引用,只要从 GC Roots 出发无法到达 a 或者 b,那么a和b就是死亡的对象。

2.png

如这个图,网上的资料一般都没说,根是什么?根就可以理解为main方法你main方法就是根,你这这里new出来的对象,就是最开始的根,比如A。

根可达就是从mian方法往里面找,一直能找到最末尾的都是可达的,比如CD,但是 EFH三个,从根是找不到的,这就是垃圾。

虽然可达性分析的算法本身很简明,但是在实践中还是有不少其他问题需要解决的。

2.2、多线程环境存在问题

在多线程环境下,其他线程可能会更新已经访问过的对象中的引用,而我们的可达性分析线程却没有同步到最新的内容。那么就会造成误报或者漏报。

对于JVM来说漏报顶多损失了部分垃圾回收的机会。漏报则比较麻烦,因为垃圾回收器可能回收了仍被引用的对象…

怎么解决这个问题呢?

2.2.1、Stop-the-world以及安全点

在 Java 虚拟机里,传统的垃圾回收算法采用的是一种简单粗暴的方式,那便是 Stop-the-world,停止其他非垃圾回收线程的工作,直到完成垃圾回收。这也就造成了垃圾回收所谓的暂停时间(GC pause)。

Java 虚拟机中的 Stop-the-world 是通过安全点(safepoint)机制来实现的。当 Java 虚拟机收到 Stop-the-world 请求,它便会等待所有的线程都到达安全点,才会停止所有线程,并允许请求Stop-the-world的那个线程进行独占的工作。

当然也并非蛮横的强制停止,毕竟多线程情况下,啥事都可能发生。安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。在这个执行状态下,Java 虚拟机的堆栈不会发生变化。

3、垃圾回收的三种方式

通过上文我们聊到的可达性分析,我们可以对死亡对象宣判死刑。那么接下来我们便可以对死亡对象进行回收工作了。主流的基础回收方式可分为三种。

3.1、清除(sweep)

常见的一种叫法:标记清除

思想:把死亡对象所占据的内存标记为空闲内存,并记录在一个空闲列表(free list)之中。当需要新建对象时,内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。

清除这种回收方式的原理及其简单,但是有两个缺点:

  1. 造成内存碎片。由于 Java 虚拟机的堆中对象必须是连续分布的,因此可能出现总空闲内存足够,但是无法分配的极端情况。比如:总空间100M,此时我们需要申请100M的数组。但是由于内存不连续,因此我们就会申请失败。
  2. 分配效率较低。如果是一块连续的内存空间,那么我们可以通过指针加法(pointer bumping)来做分配。而对于空闲列表,Java 虚拟机则需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。

3.2、压缩(compact)

常见的一种叫法:标记整理

思想:把存活的对象聚集到内存区域的起始位置,从而留下一段连续的内存空间。

这种做法优缺点都比较的明显:

优点:能够解决内存碎片化的问题缺点:压缩算法的性能开销

3.3、复制(copy)

思想:把内存区域分为两等分,分别用两个指针 from 和 to 来维护,并且只是用 from 指针指向的内存区域来分配内存。当发生垃圾回收时,便把存活的对象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。

这种做法的优缺点同样明显:

优点:能够解决内存碎片化的问题缺点:堆空间的使用效率极其低下(毕竟分成两半,一次只使用一半

4、现代设计方案

经研究发现,大多数的对象其实死的很快。因此,现在的垃圾回收器采用上述三种方式并存的思路:

一般是把Java堆分作新生代和老年代,根据各个年代的特点采用最适当的收集算法:

新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就用复制算法,只要少量复制成本就可以完成收集。老年代中因为对象的存活率较高、周期长,就用标记-整理或标记-清除算法来回收。稍稍正式官方的描述:Java 虚拟机将堆划分为新生代和老年代。其中,新生代又被划分为 Eden 区,以及两个大小相同的 Survivor 区。分别用 from 和 to 来指代。其中 to 指向的 Survivior 区是空的。

如果Eden区不够分配,那么就会触发Minor GC。而此时Eden 区和 from 指向的 Survivor 区中的存活对象会被复制到 to 指向的 Survivor 区中,然后交换 from 和 to 指针,以保证下一次 Minor GC 时,to 指向的 Survivor 区还是空的。

Java 虚拟机会记录 Survivor 区中的对象一共被来回复制了几次。如果一个对象被复制的次数为 15

可以通过虚拟机参数 -XX:+MaxTenuringThreshold进行设置。

那么该对象将被放到老年代。另外,如果单个 Survivor 区已经被占用了 50%,

可以通过虚拟机参数 -XX:TargetSurvivorRatio进行设置

那么较高复制次数的对象也会被晋升至老年代。

而以上的过程,可以用一个图,轻松的描述清楚:

3.png

转载地址:https://blog.csdn.net/qq_21383435/article/details/106310383

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

推荐阅读更多精彩内容