JVM学习笔记(2)-垃圾回收


typora-copy-images-to: csdn\jvm


1.垃圾回收概述

java语言提供自动垃圾回收功能,c++没有自动垃圾回收,垃圾回收不是java开创,但java一直在对升级垃圾回收

1.1什么是垃圾回收

简单来说,在java中,没有被任何引用指向的对象都是垃圾回收的对象

例如

Object object = new Object();
       object = null;
//这个时候就没有引用指向Object对象,因此它将作为垃圾被回收 

1.2为什么要垃圾回收

垃圾对象如果不回收,就会一直占用内存空间,垃圾对象越积越多,从而导致内存溢出

对内存记录碎片进行整理,如果不及时整理回收,拿就无法存储类似数组那样的空间中连续对象

1.3早期如何垃圾回收

在早起时候的c++垃圾回收代码需要开发人员啊进行手动回收,通过使用关键字进行内存申请,并使用delete关键字进行内存释放

这种方式肯定会有许多不足,比如说:内存泄漏,内存溢出等等;

内存泄漏:有一些对象已经不再被使用,但是垃圾回收对象没有回收它,这种悄悄占用内存资源的现象称为内存泄漏

内存溢出:内存大小不够用,报内存溢出,造成内存泄漏

1.4自动内存管理的优劣势

优势:自动内存管理的引入大大降低了程序员的工作量,减少了内存溢出和内存泄露的风险

不足:过于自动化的内存管理,会让开发人员的内存管理能力下降,一旦出现问题,可能无法及时定位异常日志出现的位置

2.垃圾标记阶段算法

垃圾回收可以会分为标记阶段和回收阶段,在这两个时间段会有各自不同的算法进行标记和回收

在垃圾标记阶段主要是为了区分存活对象与垃圾对象,在堆中的对象只有被判定死亡后才会被gc回收,释放掉被占用的内存

JVM中判断对象存活的一般有两种方式:引用技术算法与可达性分析算法

2.1引用计数算法

这个算法会对对象保存一个整型的计数器属性,用于记录对象被引用的情况

列入有一个对象A,如果有对象引用它,则A的计数器+1,引用失效时计数器则-1,当进行GC时,若对象的计数器值为0则表示它不能再被使用,可以进行回收

这种算法的优点是:实现简单,垃圾对象便于识别,判定效率高

缺点同样明显:

1.它需要单独的字段存储计数器,需要占用额外的空间

2.每次赋值都要改变计数器的值,会有计算消耗,会增加时间开销

3.最重要的问题是,这种算法无法解决对象之间的循环引用问题.当有几个对象之间互相引用,这种算法无法将他们标记为垃圾就会一直存在,因此造成内存泄漏,这也是为什么这种算法没有被java语言用在垃圾回收中

在这里插入图片描述

2.2根可达性算法

根可达性算法与计算引用算法相比同样也高效简洁,更为关键的是它可以解决互相引用问题,防止内存泄漏,所以它也是java的选择

实现思路

在这里插入图片描述

那么,哪些对象才能被称为Gcroots对象呢?

1.虚拟机栈中引用的对象

比如:各个线程被调用的方法中使用到的参数、局部变量等。

2.本地方法栈内 JNI(通常说的本地方法)引用的对象

3.方法区中类静态属性引用的对象,比如:Java 类的引用类型静态变量

4.方法区中常量引用的对象,比如:字符串常量池(StringTable)里的引用

5.所有被同步锁 synchronized 持有的对象

6.Java 虚拟机内部的引用。

总结一句话就是,除了堆空间的周边,如:虚拟机栈,本地方法栈,方法区,字符串常量池等地方对堆空间进行引用的,都可以作为根节点

2.3finalization机制

这个方法是垃圾回收机制允许开发人员对垃圾回收最后做一次自定义处理.再对象被回收前有垃圾回收机制调用,只调用一次.

通常用这个方法进行资源释放例如:关闭文件、套接字和数据库的终止连接等.finalize()是object类中的方法,子类可重写

由于finalize()方法的存在,所以虚拟机中的对象会有三种可能的状态

  • **可触及的:**从根节点开始,可以到达这个对象
  • 可复活的:已经被标记为垃圾,但是finalize方法还没有被调用
  • **不可触及的:**finalize方法被调用,并且没有被复活

只有对象在不可触及状态才会被回收

具体过程

1.当对象没有引用链,则进行第一次标记

2.判断这个对象是否调用finalize方法:

  • 如果finalize方法没有被重写或者已经被调用则进入不可触及状态
  • 如果重写了finalize方法且还没有调用那么该对象会被插入到F-Queen队列中,由虚拟机创建的低优先级的线程调用其finalize方法.之后GC会对队列中的方法进行二次标记,如果对象在finalize方法中复活,就会被移出这个队列.当后面该对象再次被标记为垃圾对象是会成为不可触及状态,不可在调用finalize方法.

3.垃圾回收阶段算法

当对象被区分出存活还是垃圾后,接下来就是垃圾回收,释放内存,去给新对象分配空间

在垃圾回收阶段有三个常见的垃圾回收算法:

  • 复制算法(Copying)
  • 标记-清除算法(Mark-Sweep)
  • 标记-压缩算法(Mark-Compact)

3.1复制算法(Copying)

它将可用内存按照容量划分为大小相等的两个空间,将存活对象复制一份放到一块空间中,之后将另一块空间的所有对象.

优点:不会出现内存碎片问题,保证内存空间连续性,实现简单运行高效

缺点:要用到两块内存空间,对于复制对象来说,要记录对象地址,时间开销较大

3.2标记-清除算法

在根可达性算法标记后,会对垃圾对象进行清除,但是注意这里的清除并不是直接删除而是把垃圾对象的地址记录下来保存在空闲的地址列表里,有新对象需要加载时若垃圾对象的空间足够,则直接用新对象将其覆盖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pdf4uu9w-1665846533696)(C:\Users\24625\Desktop\csdn\jvm\1665814606317.png)]

优点:实现简单,不用移动对象

缺点:有可能会产生垃圾碎片

3.3标记-压缩算法

在根可达性算法标记后,将存活对象按顺序压缩到内存的一端后清理外边界的所有垃圾

在这里插入图片描述

优点:与标记清除算法相比,压缩算法在分配新对象时只用持有一个地址的起始地址即可,不用考虑内存碎片,比维护一个空闲地址列表要减少许多开销.与复制算法相比节省大量空间

缺点:效率低于复制算法,移动过程中会暂停所有用户线程即为:stw(stop the word,在某个时间点让所有用户线程暂停,保证判定对象是否为垃圾对象时的准确性)

3.4分代收集

在这些算法中各有利弊,都不可替代不可或缺.因此为了提高回收效率以应对不同生命周期的对象从而采取分代收集思想

在这里插入图片描述

年轻代区域较小,对象生命周期短,存活率低,回收频繁,因此效率较高的复制算法就适用于年轻代使用

老年代区域较大,对象生命周期长,存活率高,回收不太频繁,因此这时候就需要清除和压缩算法交替配合使用

4.垃圾回收器

如果说垃圾回收算法是内存回收的方法论,那么,java发展至今垃圾回收器就是内存回收的实践者今已经衍生了众多的 垃圾回收器。从不同角度分析垃圾收集器,可以将 GC 分为不同的类型。 实际使用时,可以根据实际的使用场景选择不同的垃圾回收器,这也是 JVM 调 优的重要部分

4.1垃圾回收器分类

一.按线程数分,可以分为(单线程)串行垃圾回收器和(多线程)并行垃圾回收器。

单线程垃圾回收器(Serial)
只有一个线程进行垃圾回收,使用于小型简单的使用场景,垃圾回收时,其他用户线程会暂停.

在这里插入图片描述

多线程垃圾回收器(Parallel)

多线程垃圾回收器内部提供多个线程进行垃圾回收,在多 cpu 情况下大大提升垃 圾回收效率,但同样也是会暂停其他用户线程

在这里插入图片描述

二.按照工作模式分,可以分为独占式和并发式垃圾回收器

并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿时间。

独占式垃圾回收器(stop the world)一旦运行,就停止应用程序中的所有 用户线程,直到垃圾回收过程完全结束。

三.按工作的内存区间分,又可分为年轻代垃圾回收器和老年代垃圾回收器。

串行回收器:Serial ; Serial old ;
并行回收器:ParNew ; Parallel scavenge ; Parallel old ;
并发回收器:CMS ; G1 ;

新生代收集器:Serial ; ParNew ; Parallel scavenge;
老年代收集器:Serial old ; Parallel old ; CMS;
整堆收集器:G1;

图中展示了 7 种作用于不同分代的收集器,如果两个收集器之间存在连线, 则说明它们可以搭配使用。虚拟机所处的区域则表示它是属于新生代还是老年代收集器。

在这里插入图片描述

4.2GC性能指标

吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运 行时间+内存回收的时间)

垃圾收集开销:垃圾收集所用时间与总运行时间的比例。

暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。

内存占用:Java 堆区所占的内存大小。

快速:一个对象从诞生到被回收所经历的时间

4.3CMS回收器

cms (Concurrent Mark Sweep 并发标记清除),以获取最短回收停顿时间为目标的收集器(追求低停顿),它可以在垃圾回收线程与用户线程并发执行,在它之前无论单线程还是多线程垃圾收集器都是独占式的.

CMS的GC过程

1.初始标记:stw,GC线程独自占有,标记与GCroots直接关联的对象进行标记

2.并发标记:垃圾回收线程和用户并发执行,这个过程进行根可达性算法标记废除对象

3.重新标记:stw,使用多线程独占式并发执行,将并发标记过程中出现的对象在此标记

4.并发清除:只用一条GC线程,与用户线程并发执行,清除刚才标记的对象,过程及其耗时

优点:可以做到并发编程

缺点:

1.用户线程和垃圾回收线程并发执行会占用一部分线程让程序变慢,导致吞吐量降低

2.cms是基于标记清除算法来实现的,会产生内存碎片

3.无法处理浮动垃圾,并发清除的时候线程不暂停还是会有垃圾对象产生

4.4三色标记算法

为了提高 JVM 垃圾回收的性能,从 CMS 垃圾收集器开始,引入了并发标记的概念。引入并发标记的过程就会带来一个问题,在业务执行的过程中,会对 现有的引用关系链出现改变

CMS将对象标记为三种颜色:

在这里插入图片描述

标记的过程大致如下:

  1. 刚开始,所有的对象都是白色,没有被访问。
  2. 将GC Roots直接关联的对象置为灰色。
  3. 遍历灰色对象的所有引用,灰色对象本身置为黑色,引用置为灰色。
  4. 重复步骤3,直到没有灰色对象为止。
  5. 结束时,黑色对象存活,白色对象回收。

这个过程正确执行的前提是没有其他线程改变对象间的引用关系,然而,并发标记的过程中,用户线程仍在运行,因此就会产生漏标和错标的情况。

漏标

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBiarLSN-1665846533700)(C:\Users\24625\Desktop\csdn\jvm\1665845418840.png)]假设GC已经在遍历对象B了,而此时用户线程执行了A.B=null的操作,切断了A到B的引用。

本来执行了A.B=null之后,B、D、E都可以被回收了,但是由于B已经变为灰色,它仍会被当做存活对象,继续遍历下去。
最终的结果就是本轮GC不会回收B、D、E,留到下次GC时回收,也算是浮动垃圾的一部分。

错标

在这里插入图片描述

假设 GC 线程已经遍历到 B 了,此时用户线程执行了以下操作:

B.D=null;//B 到 D 的引用被切断

A.xx=D;//A 到 D 的引用被建立

B 到 D 的引用被切断,且 A 到 D 的引用被建立。

此时 GC 线程继续工作,由于 B 不再引用 D 了,尽管 A 又引用了 D,但是因 为 A 已经标记为黑色,GC 不会再遍历 A 了,所以 D 会被标记为白色,最后 被当做垃圾回收。

可以看到错标的结果比漏表严重的多,浮动垃圾可以下次 GC 清理,而把不该 回收的对象回收掉,将会造成程序运行错误。

解决方法

错标只有在满足下面两种情况下才会发生:

在这里插入图片描述

原始快照和增量更新

原始快照打破的是第一个条件:当灰色对象指向白色对象的引用被断开时,就将 这条引用关系记录下来。当扫描结束后,再以这些灰色对象为根,重新扫描一次。

增量更新打破的是第二个条件:当黑色指向白色的引用被建立时,就将这个新的引用关系记录下来,等扫描结束后,再以这些记录中的黑色对象为根,重新扫描 一次。相当于黑色对象一旦建立了指向白色对象的引用,就会变为灰色对象。

4.5G1

原因就在于应用程序所应对的业务越来越庞大、复杂,用户越来越多,没有

GC 就不能保证应用程序正常进行,而经常造成 STW 的 GC 又跟不上实际的需 求,所以才会不断地尝试对 GC 进行优化。G1(Garbage-First)垃圾回收器是 在 Java7 update 4 之后引入的一个新的垃圾回收器,是当今收集器技术发展的 最前沿成果之一.

与此同时,为了适应现在不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间(pause time),同时兼顾良好的吞吐量。

官方给 G1 设定的目标是在延迟可控的情况下获得尽可能高的吞吐量,所以 才担当起“全功能收集器”的重任与期望。

G1 是一款面向服务端应用的垃圾收集器

G1也是使用并发标记和清除的.

在这里插入图片描述

将整个堆的每个区域又划分为更小的区间,回收时可以根据每个区间的优先级(由里面的垃圾数量),先回收优先级较高的区间.

降低了用户线程的停顿,提高了吞吐量.

对整堆进行统一的管理,没有年轻代和老年代.

适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值