• JVM 垃圾回收机制(可达性分析、引用计数)


    1 什么是垃圾

    垃圾是指在运行程序中没有任何指针指向的对象,就是需要被回收的。

    2 为什么需要回收

    • 执行程序会不断地分配内存空间,如果不进行回收,内存迟早都会被消耗完。

    • 除了释放没用的对象,垃圾回收也可以清除内存里的记录碎片。碎片整理将所占用的堆内存移到堆的一端,以便JVM将整理出来的内存分配给新的对象

    3 哪些对象被判定为垃圾呢

    在堆空间和元空间中,GC这条守护线程会对这些空间开展垃圾回收⼯作,那么GC如何判断这些空间的对象是否是垃圾,有两种算法引用计数法可达性分析算法

    3.1 引用计数法

    ​ 每个对象有个引用计数器属性,记录被引用的情况。对象被引⽤,则计数器+1,如果计数器是0,那么对象将被判定为是垃圾,于是被回收。

    这种算法,实现简单,判定效率高。但缺点是需要需要单独的字段存储计数器,每次赋值都需要更新计数器,增加了空间和时间的开销。缺最严重的是无法解决循环依赖问题。因此JVM⽬前的主流⼚商Hotspot没有使⽤这种算法。

    注:什么是循环依赖问题?

    如下图所示,p引用了A,对象A间接引用了C,C又引用了A。方法执行完p不再需要引用A,但A和C的引用没有消失,引用计数器还都是1,不会被回收。
    在这里插入图片描述

    3.2 可达性分析算法:GC Roots根

    该算法的基本思想就是:

    通过一系列被称为「GC Roots」的根对象作为起始节点集,从这些节点开始,通过引用关系向下搜寻,搜寻走过的路径称为「引用链」,如果某个对象到GC
    Roots没有任何引用链相连,就说明该对象不可达,即可以被回收。

    哪些会被认定为GC Roots根呢?

    • 方法区静态属性引用的对象
      全局对象的一种,Class对象本身很难被回收,回收的条件非常苛刻,只要Class对象不被回收,静态成员就不能被回收。
    • 方法区常量池引用的对象
      也属于全局对象,例如字符串常量池,常量本身初始化后不会再改变,因此作为GC Roots也是合理的。
    • 方法栈中栈帧局部变量表引用的对象
      属于执行上下文中的对象,线程在执行方法时,会将方法打包成一个栈帧入栈执行,方法里用到的局部变量会存放到栈帧的局部变量表中。只要方法还在运行,还没出栈,就意味这局部变量表的对象还会被访问,GC就不应该回收,所以这一类对象也可作为GC Roots。
    • 本地方法栈中引用的对象
      和上一条本质相同,无非是一个是Java方法栈中的变量引用,一个是native方法(C、C++)方法栈中的变量引用。
    • 被同步锁持有的对象
      synchronized锁住的对象也是绝对不能回收的,当前有线程持有对象锁呢,GC如果回收了对象,锁不就失效了。

    五种变量的位置如下:

    静态变量会在方法区中存一个引用,市级指向堆内存,局部变量表也是如此。

    在这里插入图片描述

    总结

    可达性分析就是JVM首先枚举根节点,找到一些为了保证程序能正常运行所必须要存活的对象,然后以这些对象为根,根据引用关系开始向下搜寻,存在直接或间接引用链的对象就存活,不存在引用链的对象就回收。

    GC再扫描堆空间的某个节点时,会向上遍历,看看能不能遍历到gc roots根节点,如果不能,那么意味着这个对象是垃圾。

    例如下图,对象4、5、6都没有和GC Root根节点相连,会被判定为垃圾回收。
    在这里插入图片描述

  • 相关阅读:
    uniapp实战项目 (仿知识星球App) - - tabBar配置
    一篇快速教你如何创建专业级数据可视化库
    重温数据结构与算法之并查集
    Hive安装配置笔记
    [apue] 标准 I/O 库那些事儿
    C2. k-LCM (hard version)-Codeforces Round #708 (Div. 2)
    Sonatype Nexus: Recommended file descriptor limit is 65536 but count is 4096
    muduo网络库编程
    逍遥自在学C语言 | 位运算符&的高级用法
    Unity资源无法下载 反复提示需同意Terms of Service和EULA 同意后无效的解决方案
  • 原文地址:https://blog.csdn.net/qq_43331014/article/details/133847076