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

图解 JVM 垃圾回收(一)

  • 1.前言
    • 1.1 什么是垃圾
    • 1.2 内存溢出和内存泄漏
  • 2.垃圾回收的定义与重要性
  • 3.GC 判断策略
    • 3.1 引用计数算法
    • 3.2 可达性分析算法
  • 4.引用类型
  • 5.垃圾回收算法
    • 5.1 标记-复制(Copying)
    • 5.2 标记-清除(Mark-Sweep)
    • 5.3 标记-整理(Mark-Compact)
    • 5.4 分代收集理论
    • 5.5 垃圾回收阶段算法小结

1.前言

1.1 什么是垃圾

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

1.2 内存溢出和内存泄漏

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

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

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

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

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

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

  • 1️⃣ 查找内存中不再使用的对象(GC 判断策略)
  • 2️⃣ 释放这些对象占用的内存(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 作为起始点进行搜索,能够到达到的对象都是存活的,不可达的对象可被回收。

在这里插入图片描述
哪些对象可以作为 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.垃圾回收算法

在这里插入图片描述

5.1 标记-复制(Copying)

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

在这里插入图片描述

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

5.2 标记-清除(Mark-Sweep)

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

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

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

有两个明显的问题:

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

在这里插入图片描述

5.3 标记-整理(Mark-Compact)

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

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

在这里插入图片描述

5.4 分代收集理论

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

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

在这里插入图片描述
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 个条件:

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

5.5 垃圾回收阶段算法小结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/475708.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#上位机与三菱PLC的通信06--MC协议之QnA-3E报文测试

1、A-3E报文回顾 1、存储区分类及访问规则 2、命令类型 命令由主命令子命令组成 3、报文结构 2、启动mc服务器 3、创建VS项目 这节继续使用上节的VS2022的项目&#xff0c;增加一个方法 MCTestA3E()&#xff0c;具体怎么创建项目&#xff0c;见上节的过程。C#上位机与三菱…

2011-2021年商业银行财务指标面板数据

2011-2021年商业银行财务指标面板数据 1、时间&#xff1a;2011-2021年 2、来源&#xff1a;银行年报 3、指标&#xff1a;银行代码、银行中文简称、银行中文全称、银行英文全称、国家代码、银行性质、会计期间、会计年度、资产总计、净资产、负债总计、营业收入、营业利润、…

文物预防性保护在行动,株洲博物馆龙展文物安然无恙

一、龙文物收藏与甲辰龙年生肖文物联展 在株洲博物馆&#xff0c;50多个关于龙的文物悄悄的表述了几千年的故事。这类青铜器、金银、陶器不仅仅有着中华文化的历史文化&#xff0c;并且在岁月的长河中保存着独特的艺术价值。每一件文物似乎都是一条历史的龙脉&#xff0c;这将…

ClickHouse监控及备份

第1章 ClickHouse监控概述 第2章 Prometheus&Grafana的安装 第3章 ClickHouse配置 第4章 Grafana集成Prometheus 第5章 备份及恢复

哪款洗地机清洁力强?清洁力强的洗地机推荐

洗地机是一种结合了吸尘和洗地功能清洁机器&#xff0c;适用于硬质地面清洗同时吸干污水&#xff0c;并将污水带离现场。它具有高效节能且环保等优点&#xff0c;根据其结构和功能&#xff0c;洗地机可以分为半自动洗地机、全自动洗地机、手推式洗地机和驾驶式洗地机等类型。那…

【大厂AI课学习笔记】【2.2机器学习开发任务实例】(3)数据准备和数据预处理

项目开始&#xff0c;首先要进行数据准备和数据预处理。 数据准备的核心是找到这些数据&#xff0c;观察数据的问题。 数据预处理就是去掉脏数据。 缺失值的处理&#xff0c;格式转换等。 延伸学习&#xff1a; 在人工智能&#xff08;AI&#xff09;的众多工作流程中&#…

【JavaSE】类和对象

面向对象概述 面向对象编程&#xff08;简称POP&#xff09;&#xff0c;其核心思想就是参照现实中的事物&#xff0c;将事物的属性特征、行为特征抽象出来&#xff0c;使用类来表示&#xff0c;当涉及到一个具体的实例时&#xff0c;就将类进行实例化&#xff0c;使用一个对象…

2.18 day5 C++

以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系:比喻:动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有一位讲解员&#xff0c;他会为每种动物表演…

聊聊xinput1_3.dll丢失的几种修复方式,xinput1_3.dll文件的作用和重要性

xinput1_3.dll丢失是一个常见且令人困扰的问题。xinput1_3.dll是一个重要的动态链接库文件&#xff0c;用于支持在Windows操作系统上运行的许多游戏和应用程序。当这个文件丢失或损坏时&#xff0c;用户可能会面临无法启动游戏或应用程序的困境。接下来就和大家聊聊xinput1_3.d…

常用类与基础API-String的理解和不可变性

1.String类的理解 1.1类的声明 public final class String >final &#xff1a;String是不可继承的。 >Serializable :可序列化的接口,凡是实现此接口的类的对象就可以通过网络或本地流进行数据的传输 >comparable:凡是实现此接口的类,其对象都可以比较大小. 1.…

【数据结构与算法】手搓JDK底层ArrayList底层 - 动态数组

数组 在介绍数组之前&#xff0c;我们先来看一段chatGPT给出的对于数组描述&#xff1a; 数组&#xff08;Array&#xff09;是一种线性数据结构&#xff0c;它由一组连续的内存空间组成&#xff0c;用来存储相同类型的数据元素。数组具有固定的大小&#xff0c;一旦创建后&a…

Selenium Grid分布式测试环境搭建

Selenium Grid简介 Selenium Grid实际上是基于Selenium RC的&#xff0c;而所谓的分布式结构就是由一个hub节点和若干个node代理节点组成。Hub用来管理各个代理节点的注册信息和状态信息&#xff0c;并且接受远程客户端代码的请求调用&#xff0c;然后把请求的命令转发给代理节…