当前位置: 首页 > article >正文

【JVM】一篇通关JVM垃圾回收

目录

  • 1. 如何判断对象可以回收
    • 1-1. 引用计数法
    • 1-2. 可达性分析算法
    • 1-3. 四种引用
      • 强引用
      • 软引用
      • 弱引用
      • 虚引用
      • 终结器引用
  • 2. 垃圾回收算法
    • 2-1. 标记清除
    • 2-2. 标记整理
    • 2-3. 复制
    • 2-4. 总结
  • 3. 分代垃圾回收
    • 3-1. 回收流程
    • 3-2. VM参数分析
      • VM参数
  • 4. 垃圾回收器
  • 5. 垃圾回收调优

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

1-1. 引用计数法

引用计数法

  • 只要一个对象被其他变量所引用,那么就让这个对象的计数+1
  • 如果其他变量不再引用,让这个对象的计数 -1
  • 让这个对象的引用计数为 0时,则说明没有变量引用了,就可以作为一个垃圾进行回收

引用计数法弊端

  • 循环引用问题,A对象与B对象循环引用,他们的引用计数始终为1,不能作为一个垃圾进行回收
  • 出现内存泄漏

在这里插入图片描述

1-2. 可达性分析算法

根对象:一些肯定不能作为垃圾的对象

可达性分析算法

  • 在垃圾回收之前,会对堆内存所有的对象进行扫描

  • 查看每一个对象是不是被根对象直接或间接引用

  • 如果是,则不能被垃圾回收

  • 如果不是,则可以被垃圾回收

  • Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象

  • 扫描堆中的对象,看是否能够沿着GC Root对象 (堆中对象) 为起点的引用链找到该对象,找不到,表示堆中对象可以回收

  • 哪些对象可以作为GC Root

    • 活动线程中局部变量所引用的堆中对象可以作为根对象

1-3. 四种引用

实线代表强引用

平时用的引用都是强引用,例如:赋值运算
在这里插入图片描述

强引用

  • 所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收。
  • Object obj = new Object(); //只要obj还指向Object对象,Object对象就不会被回收 obj = null; //手动置null后会被回收

软引用

  • 只有【软引用】引用该对象时,在垃圾回收后,内存仍不足 则会回收软引用对象
  • 可以配合【引用队列】来释放软引用自身,因为软引用自身也占用内存
  • 在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。

★应用场景

  • 非核心业务资源(比如:图片)被强引用特别多时,有可能报OOM异常,因为强引用是不会被回收的,内存满直接抛异常
  • 那么就可以用【软引用】来指向这些资源,当内存不足时,回收这些资源。
  • 以后如果再使用图片资源,重新读取一遍。
  • 这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。
  • 在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。

弱引用

  • 只有【弱引用】引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
  • 可以配合【引用队列】来释放弱引用自身,因为弱引用自身也占用内存
  • 在 JDK1.2 之后,用 java.lang.ref.WeakReference 来表示弱引用。

虚引用

  • 必须配合【引用队列】使用
  • 例如 ByteBuffer 对象不再【强引用】时, ByteBuffer 对象本身可以被垃圾回收,但是占用的直接内存 是属于操作系统的,无法被回收。
  • 那么就可以将【虚引用】放入【引用队列】, 由 Reference Handler 线程调用虚引用相关方法释放【直接内存】
  • 如上图,B对象不再引用ByteBuffer对象,ByteBuffer就会被回收。但是直接内存中的内存还未被回收。这时需要将虚引用对象Cleaner放入引用队列中,然后调用它的clean方法来释放直接内存
  • 总结:【虚引用】引用的对象被垃圾回收时,【虚引用】被放入【引用队列】,从而由一个线程可以调用【虚引用】对象中的方法执行一系列操作。
  • 在 JDK1.2 之后,用 PhantomReference 类来表示

终结器引用

  • Object 类中有 finallize() 方法
  • 当一个类重写了Object 类中有 finallize() 方法,并且该对象没有被【强引用】时,就可以进行垃圾回收
  • 第一次垃圾回收时,将【终结器引用】放入【引用队列】,并且由一个优先级很低的Finalizer线程去寻找【终结器引用】的对象,找到后执行该对象的 finallize() 方法。
  • 直到第二次垃圾回收时,才将该对象进行垃圾回收。

软引用使用:

public class Demo1 {
	public static void main(String[] args) {
		final int _4M = 4*1024*1024;
		//list是强引用,byte数组是软引用
		List<SoftReference<byte[]>> list = new ArrayList<>();
		SoftReference<byte[]> ref= new SoftReference<>(new byte[_4M]);
	}
}

软引用配合引用队列使用:

public static void main(String[] args) throws IOException {
    ///使用引用队列,用于移除引用为空的软引用
    ReferenceQueue<byte[]> queue=new ReferenceQueue<>();
	
    List<SoftReference<byte[]>> list = new ArrayList<>();
    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();
    }
    System.out.println("=============");
    System.out.println("循环结束:" + list.size());
    for (SoftReference<byte[]> ref : list) {
        System.out.println(ref.get());
    }
}

弱引用使用:

弱引用的使用和软引用类似,只是将 SoftReference 换为了 WeakReference

public static void main(String[] args) {
	//list是强引用,byte数组是弱引用
    List<WeakReference<byte[]>> list=new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        WeakReference<byte[]> ref=new WeakReference<>(new byte[_4MB]);
        list.add(ref);
        for (WeakReference<byte[]> w : list) {
            System.out.print(w.get()+" ");
        }
        System.out.println();
    }
    System.out.println("循环结束:"+list.size());
}

2. 垃圾回收算法

2-1. 标记清除

在这里插入图片描述
标记清除

在垃圾回收的过程中

  • 标记:确定哪些对象是可回收对象,
  • 清除:标记好之后,清除可回收对象的内存,并不是将内存空间字节清零,而是记录内存起始地址。

注意:这里的清除并不是将内存空间字节清零,而是记录这段内存的起始地址,下次分配内存的时候,会直接覆盖这段内存。

优点: 速度快

缺点: 容易产生内存碎片。一旦分配较大内存的对象,由于内存不连续,导致无法分配,最后就会造成内存溢出问题

2-2. 标记整理

在这里插入图片描述

标记整理

  • 先采用标记算法确定可回收对象
  • 然后整理剩余的对象内存,将可用的对象内存移动到一起,使内存更加紧凑,连续的空间就更多。

优点:不会有内存碎片

缺点:速度慢

2-3. 复制

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

复制算法

  • 将内存分为等大小的两个区域,FROM和TO(TO中为空)。
  • 将被GC Root引用的对象从FROM放入TO中,然后回收不被GC Root引用的对象。
  • 回收完之后交换FROM和TO两个区域。这样也可以避免内存碎片的问题,但是会占用双倍的内存空间。

优点:不会有内存碎片

缺点:会占用双倍的内存空间。速度慢

2-4. 总结

  • JVM会根据不同的情况来采用这3种算法
  • 不会只使用一种算法

3. 分代垃圾回收

堆内存分为新生代老年代,新生代有划分为伊甸园幸存区To幸存区From

新生代

  • 主要存放刚创建的对象

老年代

  • 主要存放长时间使用的对象

新生代和老年代会进行不同的垃圾回收算法

在这里插入图片描述

3-1. 回收流程

新创建的对象分配在伊甸园

在这里插入图片描述

如果新生代空间不足时,触发 Minor GC(minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行)回收伊甸园幸存区 中的垃圾,伊甸园from 存活的对象使用 copy 复制到 to 中,存活的对象年龄加 1并且交换 fromto,并让幸存区from中的对象寿命加1

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

当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit) 不同的垃圾回收器阈值会不同

在这里插入图片描述

如果老年代空间不足,会先尝试触发Minor GC,回收新生代的垃圾。如果之后空间仍不足,那么触发 Full GC,回收新生代和老年代的垃圾,stop the world的时间更长

在这里插入图片描述

如果 Full GC之后,空间仍不足。则触发OutOfMemory - Java heap space0000000

3-2. VM参数分析

VM参数

含义参数
堆初始大小-Xms
堆最大大小-Xmx 或 -XX:MaxHeapSize=size
新生代大小-Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )
幸存区比例(动态)-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy
幸存区比例-XX:SurvivorRatio=ratio
晋升阈值-XX:MaxTenuringThreshold=threshold
晋升详情-XX:+PrintTenuringDistribution
GC详情-XX:+PrintGCDetails -verbose:gc
FullGC 前 MinorGCXX:+ScavengeBeforeFullGC

在这里插入图片描述

4. 垃圾回收器

5. 垃圾回收调优


http://www.kler.cn/a/145498.html

相关文章:

  • 第五天 Labview数据记录(5.1 INI配置文件读写)
  • 问题修复记录:Linux docker 部署 dify,无法调用宿主机本地服务
  • Sharding-JDBC 5.4.1+SpringBoot3.4.1+MySQL8.4.1 使用案例
  • Linux系统下速通stm32的clion开发环境配置
  • windows平台通过命令行安装前端开发环境
  • 编写子程序
  • Leetcode—83.删除排序链表中的重复元素【简单】
  • 【自动驾驶】一些业内自动驾驶专业术语释义
  • STM32 默认时钟更改 +debug调试
  • Three.js 3D模型爆炸分解
  • 图书管理系统源码,图书管理系统开发,图书借阅系统源码SqlHelper数据库访问操作方法简述
  • 3DCAT为华东师大设计学院打造元宇宙数字虚拟学院
  • java: nio之DirectByteBuffer
  • java小游戏之【王者荣耀】
  • XML Schema 的extension 元素
  • 基于命令行模式设计退款请求处理
  • App 设计工具
  • Springmvc原理解析
  • C语言第三十弹---打印一个整数的每一位数
  • 数据结构 / day04 作业
  • 微服务-京东秒杀
  • 虹科分享 | AR世界揭秘:从二维码的起源到数据识别与位姿技术的奇妙融合!
  • 频剪辑软件Corel VideoStudio 会声会影2024最新7大新全新功能解析
  • 脚本自动化定制开发:实现高效工作的魔法钥匙
  • steam/csgo搬砖项目真能月入过万吗?到底真的假的
  • Qt手写ListView