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堆和方法区的垃圾收集。
相关推荐
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
Swift社区7 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman8 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
阿龟在奔跑8 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
IT 青年8 小时前
数据结构 (1)基本概念和术语
数据结构·算法
王佑辉8 小时前
【jvm】方法区常用参数有哪些
jvm
王佑辉8 小时前
【jvm】HotSpot中方法区的演进
jvm
Dong雨8 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
Domain-zhuo8 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
SoraLuna9 小时前
「Mac玩转仓颉内测版24」基础篇4 - 浮点类型详解
开发语言·算法·macos·cangjie