程序员登高之路——JAVA篇——2.JVM的垃圾回收

如何判断对象死亡?

目前主流的判断对象死亡的方法有两种:

1.引用计数法:

每个对象对象包含一个引用计数器,每当对象被引用,引用计数器便加一,引用失效就减一。当对象的引用计数器为0时,则表示对象可被回收。此方法无法解决解决对象循环引用的情况,如:

// 产生循环引用的代码 A objectA = new A(); B objectB = new B(); A.b = objectB; B.a = objectA;

若采用引用计数法,对象A和B的引用计数器值永远不会小于1,那么就产生了内存泄漏。(据说Python使用的就是引用计数法,关于如何解决的循环引用问题,感兴趣的朋友可以去查一查)。

2.可达性分析算法:

通过一系列可以被看做GCRoots的根节点出发,向下搜索,构成引用链,未在引用链之内的则会被视为可回收对象。

GCRoots:

(1)虚拟机栈中的引用
(2)方法区中的静态变量
(3)方法区中的常量对象
(4)本地虚拟机栈中的引用
除此之外还有synchronized持有的对象,minjorGC时存在跨代引用的老年代对象等。

垃圾回收算法

上面说了如何判断对象可否回收,接下来说一说JVM如何回收这些对象。

1.标记清除法:

首先扫描并标记出对象是否需要清除,扫描完成后一次清除需要清除的对象。

上图红色方块表示垃圾,绿色方块表示存活对象,左图为垃圾清除之前的内存,右边为垃圾清除之后的内存。标记清除算法的缺点就是会产生大量的内存碎片,比如在垃圾回收之后,系统需要创建一个占4个小格的对象,此时内存内剩余空间明明大于4,系统却只会报内存溢出的异常。

2.复制算法:

复制算法将内存分为了两部分,每次仅使用一部分,当使用那部分满了的时候,就会将所有存活的对象移到另一个区域。

上图可以看出,在经过复制算法之后,所有存活的对象都被移到了另一半内存中,之后清空了之前使用的内存区。复制算法的弊端也很容易看出来,就是虚拟机每次仅能使用一半的内存,对于"寸土寸金"的RAM来说,这真是用着肉疼。

3.标记整理算法:

标记整理算法会在判断完垃圾之后,将存活的对象向一侧移动,之后清除掉剩余的内存。

在上图中,存活对象都像左侧移动,移动后需要占用7格内存,最后将边界外(7格之后)的内存全部清除。与标记清除算法相比,它不会产生内存碎片,与复制算法相比,它不会浪费内存。不过移动对象的花费仍然无法避免。

JVM中的垃圾收集器

上面的三种垃圾回收算法,并没有最优解,只是各自适用于不同场景,为此JVM也实现了各种垃圾收集器。

1.Serial

新生代垃圾收集器,采用复制算法。
特点:单线程垃圾收集器,在垃圾收集的时候会停止所有其他的用户线程。

2.SerialOld

老年代垃圾收集器,采用标记整理算法。可以看作老年代版本的Serial,垃圾收集的时候也会停止所有其他用户线程

3.ParNew

新生代垃圾收集器可以看作多线程版的Serial收集器,垃圾回收的时候也会停止其他所有用户线程。默认线程数与CPU数相等,所以在单核CPU的情况下甚至可以看成Serial。

4.Parallel Scavenge

新生代垃圾收集器,采用复制算法,垃圾清除时会停止其他用户线程,存在的目的是为了控制垃圾收集的吞吐量

5.Parallel Old

老年代垃圾收集器,采用标记整理算法,垃圾清除时会停止其他用户线程,存在的目的是为了控制垃圾收集的吞吐量。

6.CMS *******

老年代垃圾收集器,采用标记清除。减少了垃圾回收时停止用户线程的时间。CMS将标记分为了三步:
1.初始标记:单线程标记所有GCRoots直接指向的对象,因为不会涉及到可达树的遍历,所以非常快,此时会停止其他用户线程。
2.并发标记:与其他用户线程一起执行,根据步骤一获取的结果遍历可达树并标记。
3.重新标记:步骤2时,系统可能会产生新的对象,这一步的目的就是标记这些新产生的对象,此时会停止其他线程。
4.清除垃圾:与其他用户线程一起执行。

可以看出CMS就是将最费时间的全局可达树遍历与用户线程一起执行,从而减少用户线程暂停时间,不过缺点也显而易见:CPU敏感,会产生浮动垃圾,有内存碎片。

7.G1 *******

G1收集器与以往的垃圾收集器不同,他并没有直接将堆区分成了新生代老年代,而是将堆划分成了一个一个的内存块,内存块可能是新生代,也可能是老年代。并且额外增加了humongous用来保存大对象。这样做的好处是新生代与老年代的大小不再固定,并且若某一内存块很多对象需要进入老年代,直接将内存块标记为老年代即可,减少了对象复制的开销。
算法:新生代采用复制算法,老年代采用标记整理算法。
特点:可以设置最大暂停时间,每次GC会选择暂停时间左右效率最高的内存区域。

步骤:
1.初始标记:单线程标记所有GCRoots直接指向的对象,因为不会涉及到可达树的遍历,所以非常快,此时会停止其他用户线程。
2.并发标记:与其他用户线程一起执行,标记1可达的对象。同时标记并发标记中产生的对象(认为不是垃圾),划一块内存区域,用来保存并发标记产生对象的指针。
3.多线程最终标记:停止其他用户线程
4.多线程并发清除:停止其他用户线程

CMS与G1的选择

G1优点较多,但CMS不一定不G1差,例如G1为了存放并发标记产生的对象需要占用内存进行存放。

并发标记中的三色标记

黑色:根对象,以及当前对象和子对象都标记完成,或者没有子对象,则当前对象为黑色,表示扫描完成且不会被GC
灰色:扫描完当前对象,但仍有子对象没有进行标记,
白色:所有对象初始为白色,扫描后仍为白色表示对象没有被可达,可以回收。

如何解决并发标记的漏标问题?

场景:再并发标记时,线程A已经完成了标记,线程B仍在标记。此时用户线程将B正在标记的可达树下B未标记的对象置为null,并又将此对象的指针交给了A扫描过的对象。此时因线程A已经扫描结束,线程B扫描不到这个对象,那这个不应该回收的对象就会被回收。

CMS解决办法:增量更新,若发现一个白色对象被黑色对象引用,则将黑色对象置为灰,垃圾回收器发现节点为灰,则会从头再次扫描。

G1解决办法:STAB(快照),在并发标记前拍一个快照信息,若在标记的时候发先有一个引用消失了,则将快照信息推送到GC的堆栈内,则快照内的引用还会存在。

特点:G1的解决方式会产生更多的浮动垃圾,不过不需要像CMS一样重新扫描。.

安全点和安全区域

无论什么垃圾收集器,都会出现需要暂停用户线程的时间段,为了让程序正确运行,用户线程只有运行到安全点,才会被暂停。安全点包括:
1.方法调用前
2.方法返回后
3.循环末尾
4.抛出异常未知。
安全区域:若线程进入了sleep或blocked,此时线程无法进入安全点,则认为此时线程处于安全区域,处于安全区域的线程,只有再用户线程的暂停结束后,才能继续执行。

来源:https://www.icode9.com/content-1-842151.html

(0)

相关推荐

  • Java开发之虚拟机八股文分享

    简述JVM内存模型 线程私有的运行时数据区: 程序计数器.Java 虚拟机栈.本地方法栈. 线程共享的运行时数据区:Java 堆.方法区. 简述程序计数器 程序计数器表示当前线程所执行的字节码的行号指 ...

  • 搞定这24道JVM面试题,要价30k都有底气~

    回复"面试"获取全套面试资料 1.什么是JVM? JVM 的全称是 「Java Virtual Machine」,也就是我们耳熟能详的 Java 虚拟机.它能识别 .class后缀 ...

  • 7种jvm垃圾回收器,这次全部搞懂

    前言 之前我们讲解了jvm的组成结构与垃圾回收算法等知识点,今天我们来讲讲jvm最重要的堆内存是如何使用垃圾回收器进行垃圾回收,并且如何使用命令去配置使用这些垃圾回收器. 堆内存详解 上面这个图大家应 ...

  • Java虚拟机(JVM)面试题(2020最新版)

    大家好,我是CSDN的博主ThinkWon,"2020博客之星年度总评选'开始啦,希望大家帮我投票,每天都可以投多票哦,点击下方链接,然后点击'最大",再点击'投TA一票'就可以啦 ...

  • Java虚拟机垃圾回收(三) 7种垃圾收集器

    主要特点 应用场景 设置参数 基本运行原理 在<Java虚拟机垃圾回收(一) 基础>中了解到如何判断对象是存活还是已经死亡?在<Java虚拟机垃圾回收(二) 垃圾回收算法>了解 ...

  • 应该是全网最全的JVM知识点总结

    应该是全网最全的JVM知识点总结

  • 图解 | 搞定分布式,程序员进阶之路

    (给程序员零距离加星标,了解项目开发.) 编程是一门艺术,它的魅力在于创造. 65 哥已经工作两年了,一直做着简单重复的编程工作,活活熬成了一个只会 CRUD 的打工 boy. 65 哥:总是听大佬讲 ...

  • 这些编程语言程序员工资最高!Java才第四

    在众多行业中,程序员属于高薪职业.无论是在国外还是国内,程序员的薪金水平普遍高于其他行业的工作岗位. 高薪的诱惑和充满挑战性的工作,令程序员一直成为备受欢迎的职业.在今年年初,Glassdoor发布的 ...

  • 程序员的修仙之路-筑基篇

    也许很多人都被这个文章的标题吓到.吸引或者迷惑,那我告诉你,这篇文章不是一个程序员的穿越玄幻,不是一个程序员的无聊之谈,里面没有算法公式,亦无程序员的心路历程.它只是一套学习方法与学习工具的使用.这只 ...

  • 成为一名优秀的Java程序员9+难以置信的公式

    成为一名优秀的Java程序员 成为一名优秀的Java程序员并不重要,但是首先您应该了解基本的编程语言. 好吧,你知道那太好了.我们应该一步一步地精通Java编程,并应遵循所有说明,改进Java的编程逻 ...

  • 程序员如果不能干到退休,那程序员的路何去何从?

    很多程序员在30岁开始谋划后面的职业生涯了,和国外不一样,目前国内的行情如果仅仅只是一线程序员,那么35岁就到天花板了,因为这一类人的要求门槛比较低,不需要科班出生,稍微做点培训也能入行.如果你现在是 ...

  • 一个合格的程序员应该读过哪些书(偏java)

    很多程序员响应,他们在推荐时也写下自己的评语. 以前就有国内网友介绍这个程序员书单,不过都是推荐数 Top 10的书. 其实除了前10本之外,推荐数前30左右的书籍都算经典,笔者整理编译这个问答贴,同 ...

  • 程序员之路——一个老程序员对刚上大学的学弟学妹的忠告

    始终认为,对一个初学者来说,IT界的技术风潮是不可追赶. 我时常看见自己的DDMM们把课本扔了,去买些价格不菲的诸如C#, VB.Net 这样的大部头,这让我感到非常痛心. 而许多搞不清指针是咋回事的 ...

  • 程序员吞噬零售业,成也中台败也中台 | 零售十年变迁路

    [CSDN 编者按]<程序员>于 2000 年创刊,其理念为技术改变世界,创新驱动中国.2021 年,全新的<程序员>2.0 重新起航,以专业的内容为立足点,以音视频.图文专栏 ...

  • 自从用上了代码生成,程序员就没什么事了吗?gpt3开启商业化之路 | Mixlab智能产品

    当程序员还在和产品经理争论需求到底合不合理的时候,gpt3已经帮他们把代码生成了-- 早些时候,<麻省理工科技评论>发布了2021年10大突破性技术,就已经把GPT-3列为十大突破性技术之 ...