Java进阶(垃圾回收GC)——理论篇:JVM内存模型 & 垃圾回收定位清除算法 & JVM中的垃圾回收器_java的内存模型以及gc算法

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

JDK1.7

在这里插入图片描述

Young区(年轻代区):Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区,其中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到Tenured区间

Tenured 年老区:Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别的缓存,缓存中的对象往往会被转移到这一区间。

Perm 永久区:Perm代主要保存class,method,filed对象,这部份的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到java.lang.OutOfMemoryError : PermGen space 的错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的class没有被卸载掉,这样就造成了大量的class对象保存在了perm中,这种情况下,一般重新启动应用服务器可以解决问题。

Virtual区:最大内存和初始内存的差值,就是Virtual区。

JDK1.8

jdk1.8的内存模型是由2部分组成,年轻代 + 年老代。

在这里插入图片描述

年轻代:Eden + 2*Survivor
年老代:OldGen
在jdk1.8中变化最大的Perm区,用Metaspace(元数据空间)进行了替换。

需要特别说明的是:Metaspace所占用的内存空间不是在虚拟机内部,而是在本地内存空间中,这也是与1.7的永久代最大的区别所在

在这里插入图片描述

1.7到1.8的变化

在这里插入图片描述

垃圾回收基础

栈空间和堆空间

栈空间:方法结束,自动回收!

堆空间:需要手动管理回收!

程序中最难调试的BUG:野指针(空指针),并发问题

在这里插入图片描述

所以,Java为什么不让程序员来手动删除对象,明白不?上述的情况,可能会出现在比如:C语言,C++ 的语言场景中!

在这里插入图片描述

垃圾回收的语言发展

在C/C++语言中,没有自动垃圾回收机制,是通过new关键字申请内存资源,通过delete关键字释放内
存资源。如果,程序员在某些位置没有写delete进行释放,那么申请的对象将一直占用内存资源,最终
可能会导致内存溢出。

C / C++ 语言:

  • 手动管理
  • 忘记释放
  • 释放多次
  • 开发效率低

Java Python Go语言:

  • 自动管理
  • 引入GC Garbage Collector
  • 大大减低程序员门槛

应用程序只管分配,垃圾回收器负责回收!

如何定位垃圾

垃圾:没有引用指向的对象,就是垃圾对象!

如何找到垃圾?算法:引用计数法可达性分析方法

引用计数法

假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器就-1,如果对象A的计数器的值为0,就说明对象A没有引用了,可以被回收。

在这里插入图片描述

Python使用的较为多,但是,这种算法缺陷在于:无法解决循环引用的问题

在这里插入图片描述

public class A {
    public static void main(String[] args) {
        TestA a = new TestA();
        TestB b = new TestB();
        a.b = b;
        b.a = a;
        a = null;
        b = null;
        }
    }

class TestA {
    public TestB b;
}
class TestB {
    public TestA a;
}

优缺点

优点:

实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
在垃圾回收过程中,应用程序无需挂起。如果申请内存时,内存不足,则立刻报out of memery 错误。局部更新对象的计数器时,只是影响到该对象,不会扫描全部对象。

缺点:

每次对象被引用时,都需要去更新计数器,有一点时间开销。浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。无法解决循环引用问题。(最大的缺点)

可达性分析方法

Java早期使用,但后期更喜欢采用:可达性分析方法

在这里插入图片描述

那什么是根呢?JVM线程栈,本地方法栈,运行时常量池,方法区的静态引用,类对象!

main()方法启动的哪些内容,就是根!

垃圾清除算法

标记-清除

标记-复制

标记-压缩

标记-清除

标记-清除算法:

在这里插入图片描述

但是这种算法:导致内存碎片化过于严重

优缺点

优点:

可以看到,标记清除算法解决了引用计数算法中的循环引用的问题,没有从root节点引用的对象都会被回收。

缺点:

效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的。通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的。

标记-复制


标记-复制算法:将内存一分为二,一份专门用于复制!

在这里插入图片描述

但是这种算法:浪费内存


标记-压缩

标记-压缩算法:边清理,边压缩

在这里插入图片描述

但是这种算法:效率太低

三种算法都有问题,JVM采用三种的综合运用,产生了各种各样的GC垃圾回收器!

垃圾回收器

总览

随着内存大小不断变化,演变出越来越多的垃圾回收器!

在这里插入图片描述

分代模型:新生代1、老年代2

新生代:eden, 幸存区1,幸存区2 比例:8:1

在这里插入图片描述

在新生代,至少就可以做到90%的对象,都会被回收掉!

Minor-GC触发的时机:Eden满!

Full-GC触发的时机:Old满!

串行Serial 和 Serial-Old

在这里插入图片描述

Serial垃圾回收器:内存在几M - 几十M的场景下

Serial GC是一种单线程的垃圾回收器,它在一个单独的线程上运行,不会使用多个线程来进行垃圾回收,在回收的过程中,会停止所有业务线程(stop-the-world简称:stw),用于Minor-GC!算法:标记-复制算法

在这里插入图片描述

Serial-Old垃圾回收器

Serial-Old GC也是一种单线程的垃圾回收器,它在一个单独的线程上运行,不会使用多个线程来进行垃圾回收,在回收的过程中,会停止所有业务线程(stop-the-world简称:stw),用于Full-GC!算法:标记-清除算法、标记-压缩算法

并行Parallel (jdk1.8默认)

并行垃圾收集器在串行垃圾收集器的基础之上做了改进,将单线程改为了多线程进行垃圾回收,这样可以缩短垃圾回收的时间。(这里是指,并行能力较强的机器)当然了,并行垃圾收集器在收集的过程中也会暂停应用程序,这个和串行垃圾回收器是一样的,只是并行执行,速度更快些,暂停的时间更短一些。

在这里插入图片描述

Parallel垃圾回收器:内存在几十M - 几G的场景下

在这里插入图片描述

所以区别在于:Serial是单线程的,而Parallel是多线程并行的,房子大了,一个人忙不过来!

JDK1.8 默认采用的就是:Parallel 的组合 ps+po

java -XX:+PrintCommandLineFlags -version #在cmd中查看当前使用的GC回收器

在这里插入图片描述

JDK11使用G1

在这里插入图片描述

并发ParNew,CMS

当内存继续增大到 几十个G呢?多线程有时也不能解决根本问题了,是否是多线程越多越好呢?

Concurrent GC垃圾回收器:内存在几十G以上的场景下

在这里插入图片描述

Serial & Parallel 在GC时,都会打断业务线程,让业务线程停止(STW)

并发标记清除GC:意思是 GC回收线程 & 业务线程 可以并行处理!业务线程在产生垃圾,GC回收线程尽可能的让“业务线程”不需要STW的情况下,就可以及时回收业务线程所产生的垃圾!

在这里插入图片描述

尽可能少的STW,可以并行回收,这便是:Concurrent GC垃圾回收器最厉害的地方!

上述的图中,也只有 初始标记 & 重新标记 阶段,是需要STW的!


CMS全称 Concurrent Mark Sweep,是一款并行的、使用标记-清除算法的老年代垃圾回收器!

可以搭配Serial / ParNew 这些新生代的GC回收器使用!

在这里插入图片描述

初始标记,只标记Root上的根&根直接引用的对象,所以尽管采用STW,它的时间也不会太长!

重新标记,毕竟误标的对象,不会太多,所以STW的时间,也不会太长!

三色标记算法,发生在初始标记并发标记重新标记阶段,主要由3种颜色组成:黑,灰,白

黑:对象被标记,且它的所有子对象也被标记完

灰:对象被标记,但是它的子对象还没有被标记或标记完

白:对象没有被标记到,标记阶段结束后,会被当做垃圾回收掉

三色标记法

三色标记法的标记过程可以分为三个阶段:初始标记(Initial Marking)、并发标记(Concurrent Marking)和重新标记(Remark),

在这里插入图片描述

  • 初始标记:遍历所有的根对象,将根对象和直接引用的对象标记为灰色。在这个阶段中,垃圾回收器只会扫描被直接或者间接引用的对象,而不会扫描整个堆因此,初始标记阶段的时间比校短。(Stop The World)
  • 并发标记:在这个过程中,垃圾回收器会从灰色对像开始遍历整个对象图,将被引用的对像标记为灰色,并将己经遍历过的对象标记为黑色。并发标记过程中,应用程序线程可能会修改对家图,因此垃圾回收器需要便用写屏障(Write Barrier)技术来保证并发标记的正确性,(不需要STW)
  • 重新标记:重新标记的主要作用足标记在并发标记阶段中被修改的对象以及未被遍历到的对象。这个过程中,垃圾回收器会从灰色对象重新开始遍历对象图,将被引用的对家标记为灰色,并将已经遍历过的对象标记为黑色。(Stop The World)

在重新标记阶段结束之后,垃圾回收器会执行清除操作,将未被标记为可达对象的对象进行回收,从而释放内存空间,这个过程中,垃圾回收器会将所有未被标记的对象标记为白色(White)

三色标记算法的BUG:可能会产生多标,漏标的问题!漏标是最严重的问题,当然CMS & G1他们分别采用的解决方案也不一样!

多标:产生浮动垃圾,浮动垃圾情况不严重,下次GC线程直接回收就好!

在这里插入图片描述

漏标

在这里插入图片描述

CMS垃圾回收算法,在JDK1.8之后,就被干掉了!

CMS在增量更新阶段,会根据可达性分析算法,重头到尾扫描重新标记一次堆对象,所以STW时间将会非常的长!

Garbage FirstGC回收器

G1回收器,是当下的最为主流的垃圾回收器!

Garbage First 垃圾优先

G1垃圾回收器,抛弃了传统从物理上进行的分代算法,而采用分区region + 逻辑分代的算法!

G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在DK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。

在这里插入图片描述

G1将Java堆划分为多个大小相等的独立区域(Region),JVM最多可以有2048个Region。 一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以 用参数"-XX:G1HeapRegionSize"手动指定Region大小,但是推荐默认的计算方式。 G1保留了年轻代和老年代的概念,但不再是物理隔阂了,它们都是(可以不连续)Region的集合

默认年轻代对堆内存的占比是5%,如果堆大小为4096M,那么年轻代占据200MB左右的内存, 对应大概是100个Region,可以通过“-XX:G1NewSizePercent”设置新生代初始占比,在系统运行中,JVM会不停的给年轻代增加更多的Region,但是最多新生代的占比不会超过60%,可以 通过“-XX:G1MaxNewSizePercent”调整。年轻代中的Eden和Survivor对应的region也跟之前 一样,默认8:1:1,假设年轻代现在有1000个region,eden区对应800个,s0对应100个,s1对应100个。

一个Region可能之前是年轻代,如果Region进行了垃圾回收,之后可能又会变成老年代,也就是说Region的区域功能可能会动态变化。

G1垃圾收集器对于对象什么时候会转移到老年代跟之前讲过的原则一样,唯一不同的是对大对象的处理,G1有专门分配大对象的Region叫Humongous区,而不是让大对象直接进入老年代的 Region中。在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,比如按照上面算的,每个Region是2M,只要一个大对象超过了1M,就会被放入Humongous中,而且 一个大对象如果太大,可
能会横跨多个Region来存放。

Humongous区专门存放短期巨型对象,不用直接进老年代,可以节约老年代的空间,避免因为老年代空间不够的GC开销。 Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收。

在这里插入图片描述

G1回收器,同样采用三色标记算法来完成 对象的标记,但是对于漏标的问题,它采用的解决方案是:原始快照

原始快照,GC线程在退出运行时,将所有灰色节点下的所有的引用,拍摄快照到Rset中;GC线程恢复后,直接使用快照数据;

“所有的灰色对家自己引用扫描完成之前册除了对白色对象的明引用”,这个条件如果被破坏了,那么就不会出现漏标的问题。所以:

如果灰色对象在扫描完成前到除了对白色对象的引用,那么我们就在灰色对象取消引用之前,先将灰色对象引用的白色对象记录下来在后续「重新标记] 阶段再以这些白色对象为根,对它的引用进行扫描,从而避免了漏标的问题。通过这种方式,原本漏标的对象就会被重新扫描变成灰色,从而变为存活状态。

但是这种方式可能会把本来真的要取消弱引用的对象给错的活了,从而产生浮垃级,但是就像前面说的,多标的问题是可以忽略的.

在jdk1.8用G1

ZGC(Oracle官方), Shenandoah(Redhat红帽) 这2种是:下一代的垃圾回收器(分页垃圾回收器),Epsilon 是空的回收器,主要用于开发JVM GC回收器的程序员Debug使用!

  • 在JDK1.7版本正式启用,移除了Experimental的标识,是JDK9以后的默认垃圾回收器,取代了CMs回收器以及Parallel+Parallel0ld组合。被Oracle官方称为全功能的垃圾收集器”。
  • 与此同时,CMS已经在JDK9中被标记为废弃(deprecated)。
  • G1在dk8中还不是默认的垃圾回收器,需要使用-XX:+UseG1GC来启用。

如果在jdk1.8用G1则:

java -jar -XX:+UseG1GC 某一个项目.jar

G1垃圾回收过程

G1垃圾回收3种模式:YoungGC、 Mixed GC和Full GC

G1GC的垃圾回收过程主要包括如下三个环节:

  • 年轻代GC(Young GC)
  • 老年代并发标记过程(Concurrent Marking)
  • 混合回收(Mixed GC)
  • Full GC(如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。)

在这里插入图片描述

顺时针,young gc一>young gc+concurrent mark一>Mixed GC)顺序,进行垃圾回收。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ung GC)

  • 老年代并发标记过程(Concurrent Marking)
  • 混合回收(Mixed GC)
  • Full GC(如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。)

在这里插入图片描述

顺时针,young gc一>young gc+concurrent mark一>Mixed GC)顺序,进行垃圾回收。

[外链图片转存中…(img-7nqatU9u-1715792130097)]
[外链图片转存中…(img-BIUFDTQn-1715792130097)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值