JVM 垃圾回收算法

一、如何确定为垃圾

引用计数法
  • 在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一,当引用为0,则认为对象可被回收。
  • 引用计数不能解决循环引用的问题
根可达算法
  • 通过一系列称为"GC Roots"的根对象作为起始节点,根据引用关系向下搜索,搜索过程所走过的路径称为"引用链"(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

  • 固定可作为GC Roots的对象包括以下几种:

    • 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
    • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
    • 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
    • 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
    • Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
    • 所有被同步锁(synchronized关键字)持有的对象。
    • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
JAVA中四种对象的引用
  • Object obj = new Object(); 只要引用关系在,gc 就不会回收。
  • SoftReference<String> softRef=new SoftReference<String>(str); 当内存不足时,会回收str对象,可用于缓存场景。
  • WeakReference<String> abcWeakRef=new WeakReference<String>(str),当GC扫描到str时就会回收,对象只会存活到下次GC。
  • PhantomReferenceˈfæntəm 必须关联一个引用队列,在回收之前会被加入到引用队列,用来管理堆外内存,虚引用不会对对象存活时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

堆外内存的回收 使用 Unsafe 类可以操作 Unsafe unsafe = Unsafe.getUnsafe(); unsafe.freeMemory();

java 复制代码
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> softRef=new PhantomReference<Object>(new M(),referenceQueue);
        System.gc();
        new Thread(()->{
            while (true){
                Reference<? extends Object> reference2 = referenceQueue.poll();
                if(reference2!=null){
                    System.out.println("虚引用对象被JVM回收了");
                }
            }
        }).start();
    }
  • 总结:JAVA中的 软、弱、虚引用都可以关联一个引用队列,保证在回收之前做必要的操作,编写和缓存相关的程序可以使用
java 复制代码
/* 使用引用对象来 引用需要的缓存信息,避免强引用保证GC能够回收 */
    public static void main(String[] args) throws InterruptedException {
        WeakReference<String> abcWeakRef = new WeakReference<>("这个是是缓存信息");
        if(abcWeakRef.get() == null){
            //重新获取
        }
        // 根据业务情况处理
    }
    

强引用 GC 回收的情况:1.线程运行结束,线程运行使用的相关对象。2.方法的局部变量

二、垃圾清除算法

Mark-Sweep (标记清除)

  • 两遍扫描,第一次标记,第二次清除
  • 容易产生碎片,对象清除后不进行内存整理,内存碎片多
  • 存活对象比较多的情况下效率高(比如适合在堆分区的XX区)

Mark-Copying (标记复制)

  • 内存空间一分为二,把存活有用的对象重新拷贝到另一块内存区域,然后扫描过的区域全部清除
  • 空间浪费
  • 需要调整对象
  • 适合存活对象较少的情况,只扫描一次,没有碎片

Mark-Compact(标记压缩)

  • 扫描找到需要回收的对象,然后移动到某一块区域
  • 扫描两次,效率偏低
  • 不会产生碎片
  • 方便对象分配
  • 不会产生内存减半

三、分代收集理论

收集器将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储,针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法

  • 部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集。
    • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
    • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。另外请注意"Major GC"这个说法现在有点混淆,在不同资料上常有不同所指,读者需按上下文区分到底是指老年代的收集还是整堆收集。
    • 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
    • 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
相关推荐
靠沿21 分钟前
贪心算法专题(二)
算法·贪心算法
猎头南楼1 小时前
具身智能模型部署方向的能力要求与技术栈梳理
人工智能·深度学习·算法
2501_937860941 小时前
Java多线程初阶(下)—— synchronized、volatile、wait/notify与经典并发工具
java·开发语言·jvm
啥都想学点的研究生2 小时前
一篇文章讲清楚:线性回归问题的求解——正规方程法
算法·机器学习·线性回归
青少儿编程课堂2 小时前
树状数组精讲:逆序对计数与离散化(附双语言解法)
算法·树状数组·离散化·信息学奥赛·逆序对
血小板要健康3 小时前
网格 dfs 与 FloodFill:从岛屿、区域到搜索路径
笔记·算法·leetcode·深度优先
VALENIAN瓦伦尼安教学设备3 小时前
设备状态检测振动分析实训台案例分析
大数据·数据库·人工智能·嵌入式硬件·算法
小小晓.3 小时前
C++ 值类别与完美转发深度剖析:从左值右值到 std::forward 源码解密
开发语言·c++·算法
怕浪猫3 小时前
用了两年 AI 编程工具后,我重新理解了什么是「资深工程师」
算法·面试·架构
hetao17338374 小时前
2026-08-27~29 hetao1733837 的刷题记录
c++·算法