【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法

这篇具有很好参考价值的文章主要介绍了【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.前言

1.1 什么是垃圾

垃圾 是指运行程序中 没有任何引用指向的对象,需要被回收。

1.2 内存溢出和内存泄漏

内存溢出:经过垃圾回收之后,内存仍旧无法存储新创建的对象,内存不够溢出。

内存泄漏:又叫 “存储泄漏”,对象不会再被程序使用了,但是 GC 又不能回收它们。例如:IO 流不适用了但是没有被 Close、数据库连接 JDBC 没有被 Close。这些对象不会被回收就会占据内存,大量的此类对象存在,也是导致内存溢出的原因。

2.垃圾回收的定义与重要性

垃圾回收Garbage Collection,简称 GC)是内存管理的核心组成部分,它负责自动回收不再使用的内存空间。在 Java 中,程序员不需要手动释放对象占用的内存,一旦对象不再被引用,垃圾回收器就会在适当的时机回收它们所占用的内存。这样可以避免 内存泄漏野指针,从而大大减轻了程序员的负担,也使得 Java 成为一个相对安全、易于开发的编程语言。

  • 防止内存泄漏:手动管理内存容易导致内存泄漏,而 GC 可以自动回收不再使用的对象,防止内存泄漏的发生。
  • 提高开发效率:程序员不再需要关心内存释放的问题,可以更加集中精力在业务逻辑的实现上。
  • 系统性能和稳定性:通过有效的垃圾回收策略,可以保证系统的性能和稳定性。

垃圾回收的基本步骤分两步:

  • 1️⃣ 查找内存中不再使用的对象(GC 判断策略)
  • 2️⃣ 释放这些对象占用的内存(GC 收集算法)

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法,# Java编程,# 图解系列,jvm,java,垃圾回收,垃圾回收算法,GC

3.GC 判断策略

3.1 引用计数算法

给对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。正因为循环引用的存在,因此 Java 虚拟机不使用引用计数算法。

public class ReferenceCountingGC {

    public Object instance = null;

    public static void main(String[] args) {
        ReferenceCountingGC objectA = new ReferenceCountingGC();
        ReferenceCountingGC objectB = new ReferenceCountingGC();
        objectA.instance = objectB;
        objectB.instance = objectA;
    }
}

3.2 可达性分析算法

通过 GC Roots 作为起始点进行搜索,能够到达到的对象都是存活的,不可达的对象可被回收。

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法,# Java编程,# 图解系列,jvm,java,垃圾回收,垃圾回收算法,GC
哪些对象可以作为 GC Roots 呢?

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象
  • 本地方法栈(Native 方法)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有被同步锁持有的对象
  • JNIJava Native Interface)引用的对象
public void method() { 
	Object localVariable = new Object(); // localVariable 是 GC Roots 
}

public class MyClass { 
	private static Object staticObject = new Object(); // staticObject 是 GC Roots 
}

public class MyClass { 
	private static final String CONSTANT_STRING = "constant"; // CONSTANT_STRING 是 GC Roots 
}

public synchronized void synchronizedMethod() { 
	// 当前对象(this)在执行同步方法时是 GC Roots 
}

4.引用类型

无论是通过引用计算算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。

Java 中有四种类型的引用,它们对垃圾回收的影响不同:

  • 强引用Strong Reference):最常见的引用类型,只要对象有强引用指向,它就不会被垃圾回收。
  • 软引用Soft Reference):软引用可以帮助垃圾回收器回收内存,只有在内存不足时,软引用指向的对象才会被回收。
  • 弱引用Weak Reference):弱引用指向的对象在下一次垃圾回收时会被回收,不管内存是否足够。
  • 虚引用Phantom Reference):虚引用的主要用途是跟踪对象被垃圾回收的状态,虚引用指向的对象总是可以被垃圾回收。
import java.lang.ref.*;

public class ReferenceTypes {
    public static void main(String[] args) {
        Object strongRef = new Object();  // 强引用
        SoftReference<Object> softRef = new SoftReference<>(new Object());  // 软引用
        WeakReference<Object> weakRef = new WeakReference<>(new Object());  // 弱引用
        PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), new ReferenceQueue<>());  // 虚引用

        System.gc();  // 触发垃圾回收

        System.out.println("Strong Reference: " + strongRef);
        System.out.println("Soft Reference: " + softRef.get());
        System.out.println("Weak Reference: " + weakRef.get());
        System.out.println("Phantom Reference: " + phantomRef.get());
    }
}

5.垃圾回收算法

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法,# Java编程,# 图解系列,jvm,java,垃圾回收,垃圾回收算法,GC

5.1 标记-复制(Copying)

它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法,# Java编程,# 图解系列,jvm,java,垃圾回收,垃圾回收算法,GC

  • ✅ 优点: 减少内存碎片,提高空间利用率。
  • ⭕ 缺点: 减半了可用的堆内存,可能增加垃圾回收的频率。

5.2 标记-清除(Mark-Sweep)

算法分为“标记”和“清除”阶段:

标记清除算法分为两个主要步骤:标记清除

  • 标记阶段: 在标记阶段,垃圾回收器会从 GC Roots 开始,遍历所有可达的对象,并标记它们为活动对象。
  • 清除阶段: 在清除阶段,垃圾回收器会遍历整个堆,回收所有未被标记的对象的内存。

有两个明显的问题:

  • 效率问题:如果需要标记的对象太多,效率不高。
  • 空间问题:标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致在运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集。

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法,# Java编程,# 图解系列,jvm,java,垃圾回收,垃圾回收算法,GC

5.3 标记-整理(Mark-Compact)

标记整理算法是标记清除算法的改进版本。它在标记和清除的基础上增加了整理阶段,将所有活动对象向一端移动,从而消除内存碎片。

  • ✅ 优点: 解决了内存碎片化问题,提高了空间利用率。
  • ⭕ 缺点: 移动对象增加了额外的开销。

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法,# Java编程,# 图解系列,jvm,java,垃圾回收,垃圾回收算法,GC

5.4 分代收集理论

当前虚拟机的垃圾收集都采用分代收集算法,根据对象存活周期的不同将内存分为几块。一般将 Java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

  • 新生代Young Generation):使用复制算法,因为新生代中的对象生命周期较短。
  • 老年代Tenured Generation):使用标记整理或标记清除算法,因为老年代中的对象生命周期较长,且数量较少。

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法,# Java编程,# 图解系列,jvm,java,垃圾回收,垃圾回收算法,GC
1️⃣ 新生代的回收算法(以 复制 算法为主)

  • 所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。
  • 新生代内存按照 8 : 1 : 1 8:1:1 8:1:1 的比例分为一个 eden 区和两个 survivorsurvivor0survivor1)区(一般而言)。大部分对象在 eden 区中生成。回收时先将 eden 区存活对象复制到一个 survivor0 区,然后清空 eden 区,当这个 survivor0 区也存放满了时,则将 eden 区和 survivor0 区存活对象复制到另一个 survivor1 区,然后清空 eden 和这个 survivor0 区,此时 survivor0 区是空的,然后将 survivor0 区和 survivor1 区交换,即保持 survivor1 区为空, 如此往复。
  • survivor1 区不足以存放 edensurvivor0 的存活对象时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次 Full GCMajor GC),也就是新生代、老年代都进行回收。
  • 新生代发生的 GC 也叫做 Minor GCMinor GC 发生频率比较高(不一定等 eden 区满了才触发)。

2️⃣ 老年代的回收算法(以 标记-清除标记-整理 为主)

  • 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代中。因此,可以认为老年代中存放的都是一些生命周期较长的对象。
  • 内存比新生代也大很多(大概比例是 1 : 2 1:2 1:2),当老年代内存满时触发 Major GCMajor GC 发生频率比较低,老年代对象存活时间比较长,存活率标记高。

3️⃣ 永久代(Permanet Generation)的回收算法

JDK 1.8 及以后方法区的实现变成了元空间。

用于存放静态文件,如Java类、方法等。永久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些 class,例如 Hibernate 等,在这种时候需要设置一个比较大的永久代空间来存放这些运行过程中新增的类。永久代也称 方法区。方法区主要回收的内容有:废弃常量和无用的类。对于废弃常量也可通过根搜索算法来判断,但是对于无用的类则需要同时满足下面 3 个条件:文章来源地址https://www.toymoban.com/news/detail-826893.html

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
  • 加载该类的 ClassLoader 已经被回收。
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

5.5 垃圾回收阶段算法小结

标记复制 标记清除 标记压缩
速率 最快 最慢
空间开销 两个大小相同的空间 少(会堆积碎片) 少(不会碎片堆积)
移动对象

到了这里,关于【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM:垃圾回收机制(GC)

    引用计数算法:         在对象中添加一个引用计数器,当每有一个地方引用它时,计数器值加一。当引用失效时,计数器值就减一。当一个对象的计数器为零时,表示该对象没有被任何其他对象引用,因此可以被释放。 优点 :是可以及时回收垃圾对象,避免内存泄漏,且

    2024年01月19日
    浏览(48)
  • JVM----GC(垃圾回收)详解

    Automatic Garbage Collection (自动垃圾回收)是JVM的一个特性,JVM会启动相关的线程,该线程会轮训检查heap memeory,并确定哪些是未被引用的(unreferenced),即未被使用的;哪些是被引用的(referenced),即正在使用的。 在C/C++语言中,对象内存的分配与回收,是手动进行分配与回收

    2024年02月09日
    浏览(46)
  • 02JVM_垃圾回收GC

    在 堆 里面存放着java的所有对象实例,当对象为“死去”,也就是不再使用的对象,就会进行垃圾回收GC 1.1引用计数器 介绍 在对象中添加一个引用计数器,当一个对象被其他变量引用时这个对象的引用计数器加1。当某个变量不再引用这个对象时引用计数器减1。当这个引用计

    2024年02月09日
    浏览(39)
  • 【JVM】垃圾回收机制详解(GC)

    可以看jvm详解之后,再来理解这篇文章更好 堆和方法区,主要发生在堆中,然后主要发生在堆的伊甸园区(Eden)。 Java中的垃圾回收是根据 可达性分析算法(Reachability Analysis) 和 引用计数算法 来判断对象是否存活的。 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:

    2024年02月13日
    浏览(55)
  • 【JVM】JVM垃圾回收GC相关参数说明

    -XX:+PrintCommandLineFlags : 输出JVM启动参数 -XX:+UseSerialGC :在新生代和老年代使用串行收集器 -XX:SurvivorRatio :设置eden区大小和survivior区大小的比例 -XX:NewRatio :新生代和老年代的比 -XX:+UseParNewGC :在新生代使用并行收集器 -XX:+UseParallelGC :新生代使用并行回收收集器 -XX:+UseParallelO

    2024年02月04日
    浏览(46)
  • 深入学习JVM —— GC垃圾回收机制

            前面荔枝已经梳理了有关JVM的体系结构和类加载机制,也详细地介绍了JVM在类加载时的双亲委派模型,而在这篇文章中荔枝将会比较详细地梳理有关JVM学习的另一大重点——GC垃圾回收机制的相关知识,重点了解的比如对象可达性的判断、四种回收算法、分代回收

    2024年02月14日
    浏览(47)
  • 【JAVAEE】JVM中垃圾回收机制 GC

      博主简介:想进大厂的打工人 博主主页: @xyk: 所属专栏: JavaEE初阶   上篇文章我们讲了java运行时内存的各个区域。 传送门:【JavaEE】JVM的组成及类加载过程_xyk:的博客-CSDN博客 对于程序计数器、虚拟机栈、本地方法栈这三部分区域而言,其生命周期与相关线程有关,随线

    2024年02月16日
    浏览(44)
  • java面经03-虚拟机篇-jvm内存结构&垃圾回收、内存溢出&类加载、引用&悲观锁&HashTable、引用&finalize

    要求 掌握 JVM 内存结构划分 尤其要知道方法区、永久代、元空间的关系 结合一段 java 代码的执行理解内存划分 执行 javac 命令编译源代码为字节码 执行 java 命令 创建 JVM,调用类加载子系统加载 class,将类的信息存入 方法区 创建 main 线程,使用的内存区域是 JVM 虚拟机栈 ,

    2024年02月09日
    浏览(57)
  • JVM解密: 解构类加载与GC垃圾回收机制

    JVM 其实是一个 Java 进程,该进程会从操作系统中申请一大块内存区域,提供给 Java 代码使用,申请的内存区域会进一步做出划分,给出不同的用途。 其中最核心的是栈,堆,方法区这几个区域: 堆,用来放置 new 出来的对象,类成员变量。 栈,维护方法之间的调用关系,放

    2024年02月10日
    浏览(44)
  • JVM基础知识(内存区域划分,类加载,GC垃圾回收)

    目录 内存区域划分 JVM中的栈 JVM中的堆 程序计数器 方法区(元数据区) 给一段代码,某个变量在哪个区域上? 类加载 类加载时机 双亲委派模型 GC 垃圾回收机制 GC 实际工作过程 1.找到垃圾/判定垃圾 1.可达性分析(Java中的做法) 2.引用计数 2.清理垃圾 1.标记清除 2.复制算法 3.标记整

    2024年02月07日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包