【JVM】JVM03(图解垃圾回收机制)上

⭐️写在前面


  • 这里是温文艾尔の学习之路
  • 如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ
  • 博客主页 温文艾尔の学习小屋
  • ⭐️更多文章‍请关注温文艾尔主页
  • 文章发布日期:2021.12.29
  • java学习之路!
  • 欢迎各位点赞评论收藏⭐️
  • 新年快乐朋友们
  • jvm学习之路!

文章笔记参考黑马程序员

文章目录

  • ⭐️1.如何判断对象可以回收
  • ⭐️1.1引用计数法
  • ⭐️1.2可达性分析算法
  • ⭐️2.五种引用
  • ⭐️2.1强引用
  • ⭐️2.2 软引用
  • ⭐️2.3 弱引用
  • ⭐️引用队列
  • ⭐️2.4虚引用
  • ⭐️2.5终结器引用
  • ⭐️2.6引用总结
  • ⭐️3.垃圾回收算法
  • ⭐️3.1标记清除
  • ⭐️3.2标记整理
  • ⭐️3.3复制
  • ⭐️4分代垃圾回收
  • 大对象处理策略
  • ⭐️4.1分代垃圾回收总结


【JVM】JVM03(图解垃圾回收机制)上_第1张图片

⭐️1.如何判断对象可以回收

⭐️1.1引用计数法

一个对象收到引用时计数+1,一个对象失去引用时计数-1,一个对象的引用计数为0时,该对象被当作垃圾进行回收
但是这样会有一个问题

循环引用
【JVM】JVM03(图解垃圾回收机制)上_第2张图片
当A对象和B对象相互引用时,他们的引用计数都是1,但是他们却没有被其他对象所使用,这样因为他们的引用计数不能归0导致两个对象不能作为垃圾被回收,导致内存泄漏

⭐️1.2可达性分析算法

此算法为java虚拟机采用的一种垃圾回收算法,首先要确定一个肯定不会被当成垃圾回收的对象GC Root(根对象)

  • java虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
  • 扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以回收

可以作为GC Root的对象

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态变量引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象

⭐️2.五种引用

  1. 强引用
  2. 软引用
  3. 弱引用
  4. 虚引用
  5. 终结器引用

下面我们分别进行描述

⭐️2.1强引用

【JVM】JVM03(图解垃圾回收机制)上_第3张图片
我们平时所使用的引用便是强引用,比如我们new一个对象A,把这个对象通过等号(赋值运算符)赋值给一个变量m,那么就称这个变量m强引用了刚刚的对象A

强引用的特点:

只要沿着GC root的引用链能够找到该对象,他就不会被垃圾回收,就比如上图中的C对象能找到A1对象,那么A1对象就不能被回收,当B对象和C对象对A1对象的引用都断开时,A1对象才能被垃圾回收

⭐️2.2 软引用

【JVM】JVM03(图解垃圾回收机制)上_第4张图片
如图,A2对象使用过软引用对象被C对象间接引用到,只要满足垃圾回收时,并且回收完内存也不够时,那么软引用所引用的A2对象就会被释放掉

所以软引用引用对象被释放的条件是

  • 没有强引用对象引用它
  • 发生垃圾回收
  • 垃圾回收之后内存不够

软引用的应用案例

    public static void soft(){
        ArrayList<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }
        System.out.println("循环结束:"+list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }
list先引用软引用对象,软引用对象间接的引用byte数组

list和SoftReference之间是强引用,SoftReferencebyte数组之间是软引用

内存充足的时候软引用引用的对象保留,但是当内存不足的时候,软引用引用的对象就会被清除

⭐️2.3 弱引用

【JVM】JVM03(图解垃圾回收机制)上_第5张图片
和软引用的区别是,当弱引用引用该对象时,发生垃圾回收不管内存是否充足,弱引用所引用的对象都会被回收

public class Demo03 {
    private static final int _4MB = 4*1024*1024;
    public static void main(String[] args) throws IOException {

        List<WeakReference<byte[]>> list = new ArrayList<>();
        
        for (int i = 0; i < 10; i++) {
            //关联了软引用对象和引用队列,当软引用所关联的byte[]被回收时,软引用自己会加入到引用队列queue中去
            WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);
            list.add(ref);
            for (WeakReference<byte[]> w: list){
                System.out.println(w.get()+"");
            }
            System.out.println();
        }
        System.out.println("循环结束:"+list.size());
    }
}

通过list集合引用WeakReference,通过WeakReference间接的引用byte[]数组,这样进行垃圾回收,就会尝试把WeakReference运用的byte数组所占用的内存做释放

⭐️引用队列

【JVM】JVM03(图解垃圾回收机制)上_第6张图片
当软引用所引用的对象或弱引用引用的对象被清除之后,软引用和弱引用本身并不会消失,软引用和弱引用本身也是一个对象,此时软引用和弱引用会进入引用队列

软引用和弱引用自身也要占用一定内存,如果想释放软引用和弱引用的内存,需要使用引用队列找到它们,对其做进一步的处理

引用队列操作代码

public class Demo02 {
    private static final int _4MB = 4*1024*1024;
    public static void main(String[] args) throws IOException {

        ArrayList<SoftReference<byte[]>> list = new ArrayList<>();

        //引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();


        for (int i = 0; i < 5; i++) {
            //关联了软引用对象和引用队列,当软引用所关联的byte[]被回收时,软引用自己会加入到引用队列queue中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB],queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        Reference<? extends byte[]> poll = queue.poll();
        while (poll != null){
            list.remove(poll);
            poll = queue.poll();
        }

        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }
}

关联软引用对象和引用队列,当软引用所关联的byte[]被回收时,软引用自己会加入到引用队列queue中去,之后查看引用队列中是否有软引用,有就将其移出

⭐️2.4虚引用

【JVM】JVM03(图解垃圾回收机制)上_第7张图片
ByteBuffer被回收的时候,他分配的直接内存并不能被java的垃圾回收管理,所以我们将虚引用对象Cleaner进入引用队列,虚引用所在队列会有一个叫RefrenceHandlel的线程,来定时到这个引用队列中找新入队的Cleaner,如果有就调用Cleaner中的clean方法,根据记录的直接内存的地址调用Unsafe.freeMemary方法来清理直接内存

【JVM】JVM03(图解垃圾回收机制)上_第8张图片

⭐️2.5终结器引用

【JVM】JVM03(图解垃圾回收机制)上_第9张图片
所有的java对象都继承自Object类,Object类有finallize()终结方法,当对象重写了终结方法并且没有强引用,他就可以被当成垃圾回收,终结方法的调用依靠终结器引用

【JVM】JVM03(图解垃圾回收机制)上_第10张图片
如上图当A4对象被垃圾回收时,在A4对象被真正清除之前,终结期引用进入引用队列,再由一个优先级很低的线程(finallizeHandler)查看引用队列中的终结器引用,通过终结器引用找到将要垃圾回收的对象,调用对象的finallize方法,调用之后,该对象就可以被垃圾回收了

⭐️2.6引用总结

【JVM】JVM03(图解垃圾回收机制)上_第11张图片

⭐️3.垃圾回收算法

⭐️3.1标记清除

【JVM】JVM03(图解垃圾回收机制)上_第12张图片
第一个阶段:标记
顺着GC Root查找没有引用的对象,将其进行标记

【JVM】JVM03(图解垃圾回收机制)上_第13张图片
第二个阶段:清除
将垃圾对象所占用的空间释放
【JVM】JVM03(图解垃圾回收机制)上_第14张图片

释放不意味着将每一个字节进行清理操作,清除只是把对象所占用内存的起始与结束地址记录下来,放在一个较空闲的地址链表里,下次分配内存的时候会直接覆盖这部分内存

标记清除算法的优点:

  • 速度快

标记清除算法的缺点:

  • 容易产生内存碎片,无法满足大对象的内存分配,造成内存溢出问题

⭐️3.2标记整理

第一步:标记,和标记删除算法相同
【JVM】JVM03(图解垃圾回收机制)上_第15张图片
顺着GC Root查找没有引用的对象,将其进行标记
第二步:整理
【JVM】JVM03(图解垃圾回收机制)上_第16张图片
清除垃圾的过程中,它会把可用的对象向前移动,使内存更为紧凑,连续的空间更多

优点:

  • 没有内存碎片,空间连续

缺点

  • 整理的过程牵扯到对象的移动,效率就会变低

⭐️3.3复制

【JVM】JVM03(图解垃圾回收机制)上_第17张图片

将内存区域化成大小相等的两块区域,首先将没有被引用的对象进行标记,然后将from区存活的对象复制到to区,复制的过程中完成碎片的整理

【JVM】JVM03(图解垃圾回收机制)上_第18张图片

复制完成后清空from区,并且交换from和to

【JVM】JVM03(图解垃圾回收机制)上_第19张图片
优点

  • 不会产生碎片

缺点

  • 会占用双倍的内存空间

⭐️4分代垃圾回收

【JVM】JVM03(图解垃圾回收机制)上_第20张图片
在java中长时间使用的对象就会放在老年代当中(垃圾回收耗时较长。且频率低),用完就会丢弃的对象就会被放入新生代(垃圾回收比较频繁),这样可以针对对象生命周期的不同特点进行不同的垃圾回收策略

分代垃圾回收的流程

当我们创建一个新的对象时,新对象默认采用伊甸园的空间
【JVM】JVM03(图解垃圾回收机制)上_第21张图片
当伊甸园中存放的对象达到上限,新产生的对象无处安放就会触发一次垃圾回收,我们称为Minor GC
【JVM】JVM03(图解垃圾回收机制)上_第22张图片
随后沿GC Root引用链,标记未引用的对象,随后采用我们上面说到的复制算法,将存活的对象复制到幸存区To中,并使其寿命+1
【JVM】JVM03(图解垃圾回收机制)上_第23张图片

回收伊甸园中的对象,随后交换幸存区From和幸存区To的位置
【JVM】JVM03(图解垃圾回收机制)上_第24张图片
此后产生的新对象会再次放入伊甸园
【JVM】JVM03(图解垃圾回收机制)上_第25张图片
当又经过了一段时间,伊甸园又满了,就会触发第二次垃圾回收,第二次垃圾回收除了要把伊甸园中存活的对象找到以外,还会寻找幸存区中继续存活的对象,第二次垃圾回收会把伊甸园中幸存的对象转移到幸存区To中

并且该对象寿命+1,原from幸存区中上一次存活的对象转移到to中,并且寿命+1

【JVM】JVM03(图解垃圾回收机制)上_第26张图片
回收伊甸园和幸存区中剩余的的垃圾对象
【JVM】JVM03(图解垃圾回收机制)上_第27张图片
交换幸存区From和幸存区To
【JVM】JVM03(图解垃圾回收机制)上_第28张图片
随后产生的新对象就可以放入伊甸园
【JVM】JVM03(图解垃圾回收机制)上_第29张图片
幸存区中的对象会有一个默认阈值,例如默认阈值为15次
【JVM】JVM03(图解垃圾回收机制)上_第30张图片
一旦达到15次,且对象依然存活,我们就认为该对象价值较高,该对象就会晋升到老年代中
【JVM】JVM03(图解垃圾回收机制)上_第31张图片
如果新生代和老年代内存都达到极限
【JVM】JVM03(图解垃圾回收机制)上_第32张图片
会先触发一次minor gc,如果此后空间仍不足,会触发一次Full GC,从新生代到老年代都会做一次清理,清理回收新生代和老年代中所有无用对象

大对象处理策略

当向伊甸园内添加一个非常大的对象时,达到即使伊甸园为空也无法容纳该对象,那么这个对象将直接晋升至老年代

⭐️4.1分代垃圾回收总结

- 对象首先分配在伊甸园区域
- 
- 伊甸园空间不足时,触发minor gc,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄+1,
  并且交换from,to
  
- minor gc会引发stop the world,暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行
- 
- 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15次(4bit)
- 
- 当老年代空间不足,会先尝试触发minor gc,如果之后空间仍不足,那么触发full gc,STW的时间更长

【JVM】JVM03(图解垃圾回收机制)上_第33张图片

你可能感兴趣的:(jvm,java,后端,jvm,jvm.gc,面试)