【实战JVM】-基础篇-04-自动垃圾回收


自动垃圾回收

1 多语言内存管理

1.1 C/C++的内存管理

在这里插入图片描述

1.2 Java的内存管理

在这里插入图片描述

1.3 自动与手动对比

在这里插入图片描述

1.4 应用场景

在这里插入图片描述

2 方法区的回收

在这里插入图片描述

2.1 回收条件

在这里插入图片描述

3 堆回收

在这里插入图片描述

在这里插入图片描述

3.1 判断是否被引用

3.1.1 引用计数法

在这里插入图片描述

C++智能指针就采用引用计数法,但是存在缺点:

  • 每次引用和取消引用都需要维护计数器,对系统性能会有一定的影响。
  • 存在循环引用问题,所谓循环引用就是当A引用B,B同时引用A时会出现对象无法回收的问题。

在这里插入图片描述

查看垃圾回收信息,采用-verbose:gc参数,但是java并没有采用引用计数法。使用的是可达性分析算法。

3.1.2 可达性分析算法

在这里插入图片描述

注:GC Root对象不可被回收

3.1.2.1 GC Root

在这里插入图片描述

  1. 线程Thread对象

    包括主线程,子线程等等。

  2. java.lang.Class对象

    系统类加载器加载的java.lang.*中的Class对象,其中包括sun.misc.Launcher类,再由sun.misc.Launcher类加载扩展类和应用程序类加载器。
    在这里插入图片描述

  3. 监视器对象

    在这里插入图片描述

3.1.2.2 监视GC Root
public class ReferenceCounting {
    private static A a2=null;

    public static void main(String[] args) throws IOException {
        A a1=new A();
        B b1=new B();
        a1.b=b1;
        b1.a=a1;
        a2=a1;
        System.in.read();

    }
}
class B {
    public A a;
}
class A {
    public B b;
}

启动arthas

java -Dfile.encoding=UTF-8 -jar arthas-boot.jar

进入ReferenceCounting,对内存进行快照

heapdump "D:\File\StudyJavaFile\JavaStudy\JVM\low\day03\resource\test2.hprof"

先把环境变量的jdk设为17再启动memoryanalyzer

在这里插入图片描述

打开刚刚的内存快照,canel取消引导

在这里插入图片描述

在这里插入图片描述

选择GC Root

在这里插入图片描述

可以看到成员变量实际在堆中存放的是直接引用

在这里插入图片描述

所有的局部变量和成员变量已经看过了,我们来找静态变量a2

private static A a2=null;
public static void main(String[] args) throws IOException {
    A a1=new A();
    B b1=new B();
    a1.b=b1;
    b1.a=a1;
    a2=a1;
    System.in.read();

}

静态变量通过应用程序加载器加载到堆中,A类应该还有个应用程序类的加载器的GCroot指向A,我们通过path to gcroot来找引用

在这里插入图片描述

在这里插入图片描述

我们可以看到有三个引用,亦可以看到应用程序加载器的父类都有哪些。

在这里插入图片描述

3.1.2.3 总结

在这里插入图片描述

3.2 五种对象引用

在这里插入图片描述

3.2.1 软引用

在这里插入图片描述

使用软引用必须创建两个对象:

  1. 软引用自身SoftReference对象
  2. 在软引用自身SoftReference对象中创建第二个对象A,才是程序真正使用的对象

在这里插入图片描述

3.2.1.1 软引用使用
public class SoftReferenceDemo2 {
    public static void main(String[] args) throws IOException {

        byte[] bytes = new byte[1024 * 1024 * 100];
        SoftReference<byte[]> softReference = new SoftReference<byte[]>(bytes);
        bytes = null;
        System.out.println(softReference.get());

        byte[] bytes2 = new byte[1024 * 1024 * 100];
        System.out.println(softReference.get());

//        byte[] bytes3 = new byte[1024 * 1024 * 100];
//        softReference = null;
        System.gc();

        System.in.read();
    }
}

设置最大堆内存-Xmx200m,启动,总共200M实际能用不到200M,自然第一个放进去第二个放不进去,然后软引用就被释放回收了,打印null

[B@7ba4f24f
null

设置最大堆内存-Xmx400m,都能发进去,并且指向同一个软引用空间

[B@7ba4f24f
[B@7ba4f24f

修改回200m,添加

byte[] bytes3 = new byte[1024 * 1024 * 100];

已经把软引用释放了,就不能添加了,自然报outofmemory

[B@7ba4f24f
null
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at chapter04.soft.SoftReferenceDemo2.main(SoftReferenceDemo2.java:20)
3.2.1.2 软引用回收

在这里插入图片描述

public class SoftReferenceDemo3 {

    public static void main(String[] args) throws IOException {

        ArrayList<SoftReference> softReferences = new ArrayList<>();
        ReferenceQueue<byte[]> queues = new ReferenceQueue<byte[]>();
        for (int i = 0; i < 10; i++) {
            byte[] bytes = new byte[1024 * 1024 * 100];
            SoftReference studentRef = new SoftReference<byte[]>(bytes,queues);
            softReferences.add(studentRef);
        }

        SoftReference<byte[]> ref = null;
        int count = 0;
        while ((ref = (SoftReference<byte[]>) queues.poll()) != null) {
            count++;
        }
        System.out.println(count);

    }
}

设置堆内存200M,只够存一个,后一个盒子把前一个盒子覆盖了,并且把前一个盒子放进queue中。所以最后一个存在,没有被回收,因此输出9

3.2.1.3 软引用使用场景-缓存

在这里插入图片描述

在这里插入图片描述

通过在软引用对象中添加一个属性 _key来方便后面的清理HashMap,如果内存不足,软引用自动清除,然后再根据引用队列中queue中存在的软引用对象,再通过软引用的属性 _key来清理HashMap完成闭环的清理缓存的操作。

public class StudentCache {

    private static StudentCache cache = new StudentCache();

    public static void main(String[] args) {
        for (int i = 0; ; i++) {
            StudentCache.getInstance().cacheStudent(new Student(i, String.valueOf(i)));
        }
    }

    private Map<Integer, StudentRef> StudentRefs;// 用于Cache内容的存储
    private ReferenceQueue<Student> q;// 垃圾Reference的队列

    // 继承SoftReference,使得每一个实例都具有可识别的标识。
    // 并且该标识与其在HashMap内的key相同。
    private class StudentRef extends SoftReference<Student> {
        private Integer _key = null;

        public StudentRef(Student em, ReferenceQueue<Student> q) {
            super(em, q);
            _key = em.getId();
        }
    }

    // 构建一个缓存器实例
    private StudentCache() {
        StudentRefs = new HashMap<Integer, StudentRef>();
        q = new ReferenceQueue<Student>();
    }

    // 取得缓存器实例
    public static StudentCache getInstance() {
        return cache;
    }

    // 以软引用的方式对一个Student对象的实例进行引用并保存该引用
    private void cacheStudent(Student em) {
        cleanCache();// 清除垃圾引用
        StudentRef ref = new StudentRef(em, q);
        StudentRefs.put(em.getId(), ref);
        System.out.println(StudentRefs.size());
    }

    // 依据所指定的ID号,重新获取相应Student对象的实例
    public Student getStudent(Integer id) {
        Student em = null;
// 缓存中是否有该Student实例的软引用,如果有,从软引用中取得。
        if (StudentRefs.containsKey(id)) {
            StudentRef ref = StudentRefs.get(id);
            em = ref.get();
        }
// 如果没有软引用,或者从软引用中得到的实例是null,重新构建一个实例,
// 并保存对这个新建实例的软引用
        if (em == null) {
            em = new Student(id, String.valueOf(id));
            System.out.println("Retrieve From StudentInfoCenter. ID=" + id);
            this.cacheStudent(em);
        }
        return em;
    }

    // 清除那些所软引用的Student对象已经被回收的StudentRef对象
    private void cleanCache() {
        StudentRef ref = null;
        while ((ref = (StudentRef) q.poll()) != null) {
            StudentRefs.remove(ref._key);
        }
    }

//    // 清除Cache内的全部内容
//    public void clearCache() {
//        cleanCache();
//        StudentRefs.clear();
//        //System.gc();
//        //System.runFinalization();
//    }
}

class Student {
    int id;
    String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3.2.2 弱引用

在这里插入图片描述

public class WeakReferenceDemo2 {
    public static void main(String[] args) throws IOException {

        byte[] bytes = new byte[1024 * 1024 * 100];
        WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes);
        bytes = null;
        System.out.println(weakReference.get());
        System.gc();
        System.out.println(weakReference.get());
    }
}

打印,不管有没有数据,全部回收

[B@7ba4f24f
null

3.2.4 虚引用和终结器引用

在这里插入图片描述

3.3 垃圾回收算法

在这里插入图片描述

释放不再存活对象的内存,使得程序能再次利用这部分空间。

3.3.1 垃圾回收算法标准

在这里插入图片描述

  • 最大吞吐量
  • 最大暂停时间
  • 堆使用效率

3.3.2 标记清除算法

在这里插入图片描述

  • 优点:实现简单,只需要在一阶段给每个对象维护一个标志位,第二阶段删除对象即可。
  • 缺点:碎片化问题(外部碎片),分配速度慢(扫描空闲链表,速度慢)

3.3.3 复制算法

在这里插入图片描述

在这里插入图片描述

3.3.4 标记整理算法

在这里插入图片描述

在这里插入图片描述

3.3.5 分代GC

在这里插入图片描述

在jdk8中,添加-XX:+UserSerialGC参数使用分代回收的垃圾回收器,运行程序

在arthas中使用memory命令查看内存,显示三个区域内存情况。

在这里插入图片描述

public class GcDemo0 {

    public static void main(String[] args) throws IOException {
        List<Object> list = new ArrayList<>();
        int count = 0;
        while (true){
            System.in.read();
            System.out.println(++count);
            //每次添加1m的数据
            list.add(new byte[1024 * 1024 * 1]);
        }
    }
}

添加参数 -XX:+UseSerialGC -Xms60m -Xmn20m -Xmx60m -XX:SurvivorRatio=3 -XX:+PrintGCDetails

应当是总共60M,老年代40M,新生代20M,新生代中,伊甸园12M,S0,S1各4M,满足SurvivorRatio=3的3:1:1

在这里插入图片描述

3.3.5.1 分代垃圾回收算法

在这里插入图片描述

在复制算法中,S0和S1会发生互换,第一次MinorGC全放到To中,然后To和From交换名字。

在这里插入图片描述

在这里插入图片描述

FullGC对新生代和老年代一起回收

3.4 垃圾回收器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.4.1 Serial-Serial Old

新生代采用复制算法,老年代采用标记整理算法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.4.2 ParNew-CMS

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.4.3 Parallel Scavenge-Parallel Old

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.4.4 G1

在这里插入图片描述

3.4.4.1 内存结构

在这里插入图片描述

在这里插入图片描述

3.4.4.2 回收策略

在这里插入图片描述

在这里插入图片描述

年轻代选择的复制算法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.4.4.3 使用G1

在这里插入图片描述

3.4.5 选择组合策略

在这里插入图片描述

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AAA码农宝哥.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值