JVM -- ② GC

本文详细介绍了Java的垃圾回收机制,包括引用计数和可达性分析两种判断对象可回收的方法,以及静态属性引用和常量引用的对象回收规则。接着,讲解了四种垃圾回收算法:标记-清除、标记-整理、复制和分代算法,以及它们各自的特点和应用场景。最后,讨论了JVM的几种垃圾收集器,如Serial、ParallelScavenge、CMS和G1,并分析了CMS收集器的优缺点。

一、垃圾回收机制(GC)

  • 在Java程序中,程序员不需要显示的去释放一个对象的内存,而是由虚拟机自动执行。
  • 在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有当虚拟机空闲/当前堆内存不足时,才会触发执行。
  • 程序员不能实时的对某个对象或所有对象调用垃圾回收器进行垃圾回收
  • 可以建议JVM进行垃圾回收 --> System.gc(),但JVM不一定会执行

二、垃圾回收器工作原理

垃圾回收器用以下两种方式来判断一个对象是否可回收。

1. 引用计数法

  • 原理:为每个对象创建一个引用计数,有对象引用时计数+1,引用被释放时计数-1,当计数器为0时就可以被回收
  • 缺点:不能解决对象间循环引用问题

// 对象objA 和 objB 都有字段 name
// 两个对象相互进行引用,除此之外这两个人对象没有任何引用
objA.name = objB;
objB.name = objA;

// 实际上这两个对象已经不可能再被访问,应该要被垃圾收集器进行回收
// 但因为他们相互引用,所以导致计数器不为0,这导致引用计数算法无法通知垃圾收集器回收该两个对象

2. 可达性分析算法

  • 原理:从GC Roots开始向下搜索,搜索走过的路径称为引用链。当一个对象到达GC Roots没有任何引用链的时候,则证明此对象是可以被回收的。
    在这里插入图片描述
  • GC Roots分析
    主要有以下4种对象可以作为GC Roots对象(两栈两方法)
    1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
      例如:a是栈帧中的一个本地变量,它作为GC Roots,指向堆中的一个实例对象。当a=null时,堆中的实例对象与GC Roots(也就是a)断开了链接,所以对象会被回收
    2. 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
      理解:当调用 Java 方法时,虚拟机会创建一个栈桢并压入 Java 栈,而当它调用的是本地方法时,虚拟机会保持 Java 栈不变,不会在 Java 栈祯中压入新的祯,虚拟机只是简单地动态连接并直接调用指定的本地方法。当 java 调用本地方法时,jc 会被本地方法栈压入栈中, jc 就是我们说的本地方法栈中 JNI 的对象引用,因此只会在此本地方法执行完成后才会被释放
      在这里插入图片描述
    3. 方法区中类静态属性引用的对象
      例如:当栈帧中的本地变量 a = null 时,由于 a 原来指向的对象与 GC Root (变量 a) 断开了连接,所以 a 原来指向的对象会被回收,而由于我们给 s 赋值了变量的引用,s 在此时是类静态属性引用,充当了 GC Root 的作用,它指向的对象依然存活!
    4. 方法区中常量引用的对象
      常量的指向并不会因为对象a指向的对象被回收而回收
// 3. 静态属性引用对象示例
public class Test {
    public static Test s;
    public static  void main(String[] args) {
	Test a = new Test();
	a.s = new Test();
	a = null;
    }
}
---------------------------------------
// 4. 常量引用对象示例
public class Test {
	public static final Test s = new Test();
        public static void main(String[] args) {
	    Test a = new Test();
	    a = null;
        }
}

3. 对象回收规则

  • GC发生之后,如果判断一个对象可回收(不可达)时,会判断该对象是否执行过finalize方法
    • 未执行:执行finalize方法,然后回收对象
    • 已执行:直接回收该对象
  • 在finalize方法执行的过程中,可以将可回收(不可达)对象与GC Roots重新关联,这样执行完finalize方法之后,GC会再次判断对象是否可达。如果不可达,则会被回收;如果可达,则不回收。
    注意:finalize方法只会被执行一次,也就是说上述的这个关联拯救过程只能使用一次。如果第一次执行finalize方法此对象变成了可达确实不会回收,但如果对象被再次GC,则会直接回收该对象。

4. 对象的引用类型

(1)强引用:发生GC时不会被回收
(2)软引用:发生内存溢出之前会被清除(有用但不是必须的对象)
(3)弱引用:下次GC一定会被回收(有用但不是必须的对象)
(4)虚引用:无法通过虚引用获得对象,虚引用的主要作用是跟踪垃圾的回收,在对象被GC时返回一个通知。

三、 垃圾回收算法

1. 标记-清除算法

  • 分为两个阶段,首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
    • 标记阶段:标记出可以被回收的对象
    • 清除阶段:回收被标记对象所占用的空间
  • 缺点:
    1. 产生大量的内存碎片
    2. 标记和清除的效率不高

2. 标记-整理算法:

  • 分为两个阶段,首先标记出所有存活对象,然后将存活对象都往内存的一端移动,然后直接清理掉边界外的内存。
  • 优点:不产生内存碎片
  • 缺点:进行对象的局部移动,降低了效率

3. 复制算法:

  • 把内存划分为两个区域,每次只使用其中的一个区域;垃圾回收时,将当前区域的存活对象都复制到另外一个区域中,然后清空本区域。
  • 优点:简单高效,不产生内存碎片
  • 缺点:
    1. 内存使用率不高
    2. 存活率高的对象会频繁复制
      在这里插入图片描述

4. 分代算法:

  • 分代算法是一种思想,根据对象的存活周期将内存划分为几块。一般包括年轻代、老年代、永久代。

  • 一般年轻代占总空间的1/3左右,剩下的是老年代
    注意:JDK8后永久代被元空间代替,元空间的内存为本地内存。
    在这里插入图片描述

    1. 年轻代:复制算法(Minor GC)

      • 年轻代分为三个区域:Eden,From Survivor,To Survivor(8:1:1)
      • 对象优先在Eden区分配
      • 每次进行年轻代GC(Minor GC)的时候,会将Eden和From Survivor中的存活对象标记,将其复制到To Survivor中,然后清空Eden和From区域的内存。
      • From/To Survivor每进行一次GC会交换身份一次,哪个空哪个就是To Survivor
      • 对象每次在 Minor GC后存活,它的年龄都会+1,到15之后进入老年代
    2. 老年代:标记整理算法/标记清除(Major GC)

      • 大对象直接进入老年代(因为对大对象进行复制算法会降低效率)
      • 当老年代空间占用达到某个值时就会出发全局垃圾回收(Full GC)
  • 三种回收触发:

    1. Minor GC:只回收年轻代
    2. Major GC:只回收老年代
    3. Full GC:回收整个Java堆和方法区
  • 方法区的垃圾回收:
    (1) 方法区和堆一样,都是线程共享的内存区域,被用于存储已被虚拟机加载的类信息(字段等)、即时编译后的代码(方法字节码)、静态变量和常量等数据。

    (2) 方法区的垃圾回收主要有两种,分别是对废弃常量的回收(常量池的回收)和对无用类的回收(类的卸载)。

    1. 当一个常量对象不再任何地方被引用的时候,则被标记为废弃常量,这个常量可以被回收。
    2. 方法区中的类需要同时满足以下三个条件才能被标记为无用的类:
      1. Java堆中不存在该类的任何实例对象;
      2. 加载该类的类加载器已经被回收;
      3. 该类对应的java.lang.Class对象不在任何地方被引用,且无法在任何地方通过反射访问该类的方法。

四、JVM的垃圾回收器

  • JVM提供了7个垃圾收集器
    在这里插入图片描述

  • 年轻代:

    1. Serial(复制算法):
      标记和清理都是单线程,优点是简单高效

    2. ParNew(复制算法):
      并行收集器,实际上是Serial收集器的多线程版本,在多核情况下表现更好

    3. Parallel Scavenge(复制算法):
      并行收集器,追求高吞吐量,高效利用CPU,可以尽快完成回收。适合后台应用等对交互相应要求不高的场景。

  • 老年代:

    1. Serial Old(标记-整理算法):
      单线程收集器(Serial的老年版本)

    2. Parallel Old(标记-整理算法):
      并行收集器(Parallel Scavenge的老年版本),追求高吞吐量

    3. CMS(标记-清除算法):
      并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点。追求最短的GC停顿时间

  • 整堆:

    • G1(标记-整理算法):
      并行收集器,回收整个Java堆的内存。
  • CMS回收器解析

    • CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。
    • 对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。
    • 在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
    • CMS 使用的是标记-清除算法实现的,所以在 gc 的时候回产生大量的内存碎片当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值