• JVM垃圾回收机制



    提示:以下是本篇文章正文内容,Java系列学习将会持续更新

    一、什么是垃圾?

     在 JVM 进行垃圾回收之前,首先就是判断哪些对象是垃圾,也就是说,要判断哪些对象是可以被销毁的,其占有的空间是可以被回收的。

     根据 JVM 的架构划分,我们知道在 Java 中,几乎所有的对象实例都在堆中存放,所以垃圾回收也主要是针对来进行的。

     在 JVM 的眼中,垃圾就是指那些在堆中存在的,已经“死亡”的对象。我们可以理解为 “不可能再被任何途径使用的对象”。那如何判断对象的存活和死亡呢?这就涉及到了垃圾判断算法,其主要包括引用计数法可达性分析法

    二、垃圾判断算法

    2-1 引用计数法

    在这种算法中,假设堆中每个对象中都有一个引用计数器。
    在这里插入图片描述

     当一个对象被创建并且初始化赋值后,该对象的计数器 ref_count = 1,每当有一个地方引用它时,ref_count ++。例如将对象 b 赋值给对象 a,那么 b 被引用,则将 b 引用对象的计数器累加 1。

    Person p1 = new Person(18); // ref_count = 1;
    
    Person p2 = p1; // ref_count ++ = 2;
    
    method(p1);
    void method(Person s) { // ref_count ++ = 3;
    } // ref_count -- = 2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

     反之,当引用失效时,例如一个对象的某个引用超过了生命周期(出作用域后)或者被设置为一个新值时,则ref_count --。

     当一个对象的 ref_count = 0 时,就可以称之为垃圾,可以被回收。

    // 当 原来的引用 指向一个新对象时
    p1 = new Person(20); // ref_count -- = 1;
    p2 = null; // ref_count -- = 0;
    // 原来的 Person(18) 被回收
    
    • 1
    • 2
    • 3
    • 4

    当一个对象死亡,对象中的引用失效,导致该引用指向的对象的ref_ count–,可能引起其它对象(它内部引用的)继续死亡。

    优点:引用计数法实现起来比较简单,对程序不被长时间打断的实时环境比较有利。
    缺点:需要额外的空间来存储计数器,难以检测出对象之间的循环引用。

    2-2 可达性分析法

    目前主流的JVM中使用的是另一种方式 : 可达性分析法。

    1. 首先把Java中管理的所有对象,看做一张图(数据结构中的图)管理。
      在这里插入图片描述
    2. 思考图中这些对象,哪些是肯定没用的,哪些是可能有用的?
      原则 : Java 应用是没有资格直接访问对象,只能通过引用简介的访问对象。
      所以问题变成了 :Java 应用手上有哪些引用?从这些引用,可以达到哪些对象?无法到达的对象就是没用的、可回收的。—— 可达性分析法
      在这里插入图片描述
      这些 GC Roots 引用从哪里收集?
      1.静态属性中的引用(类存在),我们可以随时用到。
      2.各个线程的栈帧中的引用。

    回到目录…

    三、垃圾回收

    3-1 回收和整理

    我们现在已经找到了垃圾对象了,所以探讨如何进行垃圾回收(内存空间的回收)。

    我们站在内存管理器 GC 的角度,把自己看出一个房东,有很多房间。
     分配内存(把房子租出去)。
    回收内存(收回房子,只需要作状态变更:已被出租->空闲状态)。
    整理内存打扫房间 (把空间中数据清空)。当下次需要该房间时才会去打扫房间,或者 定期将所有房间重新整理,防止内存碎片化

    在这里插入图片描述

    3-2 分代收集算法

    分代收集(Generational Collector)算法就是其中一种针对GC性能优化算法,将堆内存划分为新生代老年代
    在这里插入图片描述
    对象的生命周期:每经历一次GC,对象年龄 + 1。
    在这里插入图片描述
    伊甸区:是对象诞生的地方。经过经验的推算,大部分对象的生命基本活不过1岁。
    生存区:活过了1岁,但大部分也活不到成年15岁。这些对象每经历一次 GC,会在 SA 和 SB 之间反复横跳。
    老年区:极少数成年的对象 或者 大对象会被一开始放在这儿。

    对象的一生:

    1. 诞生于伊甸区
    2. 年关已至(发生了GC) ,大部分(99%) 对象都死去了,活下来的对象被复制到生存区。
      由于活下来的对象很少,所以这样复制,只需要很小的代价就可以完成。
      同时伊甸区就没有活对象了。所以整个视为完全空闲,不需要进行整理。
    3. 直到一个对象成年之前,一直持续这个操作,SA《 = 》SB
      当遇到一次GC时,SA中存活的对象复制到SB中,将SA置为可用、SB置为不可用。等待下一次GC,再次移动对象位置。直到对象成年。
    4. 当我们的对象14岁时,再遇到了GC变成15岁,视为成年。
      如果还活着,则从生存区移动到老年代。新生代的过程就和我们没关系了
    5. 成年之后的对象就不用记年龄了。如果遇到GC,老年代的GC按照笨办法 : 先回收、再整理。直到死去。

    GC被分为三种:

    1. 只针对新生代的GC,被称为新生代GC (Minor GC) 。
      大部分情况下,只会进行Minor GC。
    2. 针对老年代的GC,被称为老年代GC (Major GC) 。
      由于老年代的GC成本较大,所以一般会尽量减少老年代GC,等老年对象达到一定阈值时,才会进行一次。
    3. 针对整个堆的GC,称为全GC (Full GC)。
      因为事实上每次Major GC都是由于Minor GC引起的,所以也会说 Major GC = Full GC

    在分代收集算法中,对象的存储具有以下特点:

    1. 对象优先在 Eden 区分配。
    2. 大对象直接进入老年代。
    3. 长期存活的对象将进入老年代,默认为 15 岁。

    回到目录…


    总结:
    提示:这里对文章进行总结:
    以上就是今天的学习内容,本文是JVM的学习,了解了JVM垃圾回收机制的原理,以及分代收集的GC算法。之后的学习内容将持续更新!!!

  • 相关阅读:
    面试专区|【32道接口API测试基础高频题整理(附答案背诵版)】
    01.jvm介绍
    C++入门教程||C++while循环
    栈Stack
    实现一个简单的哈希映射功能
    Excel中使用ROW函数自动更新行号或编号
    二、PHP函数
    Nginx负载均衡&反向代理&动静分离
    SpringBoot实现WebSocket
    【MindSpore易点通机器人-04】MLOps 环境搭建过程
  • 原文地址:https://blog.csdn.net/qq15035899256/article/details/126286440