【从零开始学习JVM | 第七篇】深入了解 堆回收

前言:

       Java堆作为内存管理中最核心的一部分,承担着对象实例的存储和管理任务。堆内存的高效使用对于保障程序的性能和稳定性至关重要。因此,深入理解Java堆回收的原理、机制和优化策略,对于Java开发人员具有重要的意义。

本文旨在探讨Java堆回收的相关概念、工作原理以及常见的回收算法,帮助读者全面理解Java内存管理中的关键环节,并提供实用的建议和最佳实践,以便更好地应对内存管理方面的挑战,提升Java应用程序的性能和稳定性。

目录

前言:

堆回收的常见判断方法:

1.引用计数法:

2.可达性分析法:

可达性分析法: 

常见的GC Root对象:

JAVA的四种引用关系:

1.强引用:

2.软引用:

3.弱引用: 

4.虚引用: 

总结:


堆回收的常见判断方法:

1.引用计数法:

        每个对象维护一个引用计数器,记录被其他对象引用的次数。当计数器为0时,表示该对象不再被引用,可以被回收。然而,引用计数法难以解决循环引用的问题,即若存在循环引用,即使对象互相不可达,其引用计数也不为0,导致无法回收。

2.可达性分析法:

        通过一组称为"GC Roots"的根对象作为起点,从这些根对象出发,沿着引用链进行遍历,标记所有被引用的对象为可达对象。剩下的未被标记的对象即为不可达对象,可以被回收。在Java中,根对象包括虚拟机栈中引用的对象、方法区中静态变量引用的对象以及本地方法栈中JNI(Java Native Interface)引用的对象。

在这种对象区分中,根对象和普通对象是存在引用关系的

可达性分析算法是指:如果从某个对象到 GC Root对象是可达的,对象就不可以被回收。

简单的来说:我们利用 根对象 将 所有  需要判定是否回收 的对象分为了两类:根对象可达的对象根对象不可达的对象根对象可达的 对象 不可以 回收,根对象不可达的对象 可以进行回收

而Java进行堆回收的时候,使用的方法就是:可达性分析法,因此我们来深入学习一下可达性分析法:

可达性分析法: 

由上文我们简单的介绍可达性分析算法中,可以看出学习JVM中哪些对象是GC Root对象是比较重要的:

常见的GC Root对象:

  • 线程Thread对象。
  • 系统类加载器加载的Java.long.Class对象。
  • 监视器对象,用于保存同步锁synchronized 关键字所持有的对象。
  • 本地方法调用时使用的全局对象。 

 如何判断一个对象是否被GC Root对象引用:

  1. 使用工具:可以使用 Java 调试工具,如 jmap、jconsole、jvisualvm 等,来查看堆中的对象引用关系。这些工具可以提供实时的内存快照,并显示对象的引用链。如果发现某个类的引用链中没有 GC Root 对象,那么该类就不是 GC Root 对象。

  2. 分析代码:通过代码分析,可以判断一个类是否是 GC Root 对象。通常情况下,以下几种情况会使一个类成为 GC Root 对象:

    • 作为线程对象的局部变量或静态变量。
    • 作为系统类加载器加载的 Class 对象的局部变量或静态变量。
    • 作为锁对象进行同步操作。
    • 作为本地方法调用时使用的全局对象。

    可以检查代码中是否存在上述情况,如果不存在,则说明该类不是 GC Root 对象。

但是不是只有满足上述条件的对象,才会被回收呢?

其实并不是的,通过GC Root引用对象只是 Java的五种对象引用的一种,我们来学习一下 这五种对象引用,以及在这五种引用下是如何判断对象能否回收的。

JAVA的四种引用关系:

1.强引用:

        使用最常见的引用方式。当一个对象被强引用关联时,即使内存不足时也不会被垃圾回收器回收。只有当该对象没有任何强引用时,才可能被回收

可达性分析算法中描述的对象引用一般就是强引用。

Java 中 大部分场景 都用的是 强引用 :

  1. 对象的直接引用:当使用类似 Object obj = new Object(); 这样的语句时,obj 对象就是一个强引用,因为它明确指向了一个对象。

  2. 静态变量:静态变量通常存放在方法区中,在类加载的过程中初始化,其生命周期和类一样长,因此静态变量的引用是强引用。

  3. 实例变量:对象中的实例变量也具有强引用,只要对象存在,其实例变量的引用就会一直存在。

2.软引用:

        使用SoftReference类表示,通过SoftReference包装一个对象。当内存不足时,垃圾回收器可能会回收被软引用关联的对象,但这不是强制性的,只有当内存真正不足时才会回收

 软引用 常用在 缓存 当中。不会用软引用来关联代码中的核心类,因为核心类不应该被回收。

软引用的应用场景: 

缓存和高速缓存:软引用可以用于实现缓存或高速缓存,当内存不足时,垃圾回收器可能会回收已经被软引用关联的对象。这种方式可以避免内存溢出的问题。例如:

Map<Key, SoftReference<Value>> cache = new HashMap<>();
Value getValue(Key key) {
    SoftReference<Value> ref = cache.get(key);
    if (ref != null) {
        Value value = ref.get();
        if (value != null) {
            return value;
        }
    }
    Value value = ...; // 从文件、数据库等获取
    cache.put(key, new SoftReference<>(value));
    return value;
}

对象池:对象池可以使用软引用来实现对象的复用,当内存不足时,垃圾回收器可能会回收已经被软引用关联的对象。这种方式可以避免创建过多的对象,提高系统性能。例如:

class ObjectPool<T> {
    private final List<SoftReference<T>> pool = new ArrayList<>();
    private final Supplier<T> supplier;
    ObjectPool(Supplier<T> supplier) {
        this.supplier = supplier;
    }
    T acquire() {
        for (int i = 0; i < pool.size(); i++) {
            SoftReference<T> ref = pool.get(i);
            T object = ref.get();
            if (object != null) {
                pool.remove(i);
                return object;
            }
        }
        T object = supplier.get();
        return object;
    }
    void release(T object) {
        pool.add(new SoftReference<>(object));
    }
}

需要注意的是,软引用不是绝对可靠的,垃圾回收器并不保证在内存不足时一定会回收被软引用关联的对象,因此在使用软引用时,需要根据具体场景进行合理的设计和配置。

软引用的使用就是:创建一个软引用对象 SoftReference 把 需要设置软引用的对象包装起来:

如图所示:我们就把一个 HashMap对象 包装在了一个软引用中,当系统的内存不足的时候,就会考虑回收这个HashMap对象。

3.弱引用: 

        使用WeakReference类表示,通过WeakReference包装一个对象。弱引用的对象在垃圾回收时,无论内存是否充足,都可能被回收

弱引用的使用场景:

ThreadLocal 是一个线程级别的变量存储工具,在多线程环境下可以实现线程间的数据隔离。每个线程都有自己独立的 ThreadLocal 变量副本,可以在不同线程中存储不同的值,而不会相互干扰。

通常情况下,ThreadLocal 的实现会使用一个 Map 来存储每个线程对应的变量副本。为了避免内存泄漏,ThreadLocal 对这些变量副本使用弱引用进行引用。

我们可以看到:当我们在构造Map用的Entry的时候,就用的是 虚引用 。 

 当一个线程结束或被回收时,对应的 ThreadLocal 弱引用会被垃圾回收器回收,进而导致对应的变量副本也会被回收。这样可以有效地释放线程相关的资源,避免内存泄漏问题。

4.虚引用: 

        使用PhantomReference类表示,通过PhantomReference包装一个对象。虚引用主要用于检测对象是否已经被垃圾回收器标记为可回收,在实际使用中很少直接操作虚引用对象。

虚引用(Phantom Reference)是Java中最弱的引用类型之一,它几乎没有实际的用途。虚引用主要用于监控对象是否被垃圾回收器回收,无法通过虚引用来获取对象的实例。

虚引用与其他引用类型不同,它的主要作用在于提供了一种在对象被垃圾回收时,能够在对象被销毁之前执行一些必要的清理操作的机制。虚引用通常会和引用队列(ReferenceQueue)一起使用。

在使用虚引用时,需要创建一个引用队列,并将虚引用对象与引用队列关联起来。当对象被垃圾回收器回收时,会将虚引用放入引用队列中,通过检查引用队列中的对象,可以得知对象已经被回收。

总结:

        本文我们首先介绍了为什么需要堆回收以及它的作用。我们了解到,Java堆是存储对象实例的地方,而垃圾对象的存在可能导致内存泄漏和性能下降。因此,堆回收是确保内存使用效率和应用程序性能的关键步骤。

其次介绍了一些与堆回收相关的重要概念,如引用类型(强引用、软引用、弱引用和虚引用)、内存分配和对象生命周期。这些概念对于理解垃圾回收机制和如何优化内存使用至关重要。

在了解到如何判断堆中的 类 是否能被回收之后,在下篇文章我们就要学习 如何 进行垃圾回收了,也就是垃圾回收算法和垃圾回收器。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springcloud-分布式缓存

文章目录 一.Redis持久化1.RDB持久化2.AOF持久化 二.Redis主从1.搭建主从架构2.全量同步3.增量同步 三.Redis哨兵1.哨兵的作用和原理2.搭建哨兵架构3.RedisTemplate的哨兵模式 四.Redis分片集群1.搭建分片集群2.散列插槽3.集群伸缩4.故障转移5.RedisTemplate访问分片集群 为什么…

树莓派(Raspberry Pi)4B密码忘记了,怎么办?

树莓派长时间不用&#xff0c;导致密码忘记了&#xff0c;这可咋整&#xff1f; 第1步&#xff1a;取出SD卡 将树莓派关机&#xff0c;移除sd卡&#xff0c;使用读卡器&#xff0c;插入到你的电脑。 第2步&#xff1a;编辑 cmdline.txt 在PC上打开SD卡根目录&#xff0c;启动…

Kotlin ArrayList类型toTypedArray转换Array

Kotlin ArrayList类型toTypedArray转换Array data class Point(val x: Float, val y: Float)fun array_test(points: ArrayList<Array<Point>>) {points.forEachIndexed { idx, ap ->ap.forEach {print("$idx $it ")}println()} }fun main(args: Arra…

2697. 字典序最小回文串

2697. 字典序最小回文串 难度: 简单 来源: 每日一题 2023.12.13 给你一个由 小写英文字母 组成的字符串 s &#xff0c;你可以对其执行一些操作。在一步操作中&#xff0c;你可以用其他小写英文字母 替换 s 中的一个字符。 请你执行 尽可能少的操作 &#xff0c;使 s 变…

RTX 40 SUPER发布时间定了!价格也有了

快科技12月16日消息&#xff0c;NVIDIA RTX 40 SUPER系列显卡基本确定将在2024年1月8日正式发布&#xff0c;也就是CES 2024大展期间&#xff0c;随后在1月中下旬陆续解禁上市。 RTX 4070 SUPER 1月16日解禁公版/原价丐版&#xff0c;1月17日解禁高价高配版&#xff0c;上市开…

鸿蒙开发编辑器设置

首先需要知道如何打开设置页面&#xff0c;以下所有设置都需要在设置界面中进行修改&#xff0c;有三种方式可以打开&#xff0c; 1、编辑器左上角file菜单下的Setting菜单。 2、编辑器右上角的设置按钮 3、按快捷键 ctrlalts 注意不要和其他软件案件重复。 一、设置每次打开…

制作一个简单 的maven plugin

流程 首先&#xff0c; 你需要创建一个Maven项目&#xff0c;推荐用idea 创建项目 会自动配置插件 pom.xml文件中添加以下配置&#xff1a; <project> <!-- 项目的基本信息 --> <groupId>com.example</groupId> <artifactId>my-maven-plugi…

腾讯云服务器优惠活动大全页面_全站搜优惠合集

腾讯云推出优惠全站搜页面 https://curl.qcloud.com/PPrF9NFe 在这个页面可以一键查询所需云服务器、轻量应用服务器、数据库、存储、CDN、网络、安全、大数据等云产品优惠活动大全&#xff0c;活动打开如下图&#xff1a; 腾讯云优惠全站搜 腾讯云优惠全站搜页面 txybk.com/go…

springboot笔记

尚硅谷SpringBoot3零基础教程&#xff0c;springboot入门到实战_哔哩哔哩_bilibili SpringBOOT 只会扫描在主程序下的包!!!!!!!!!!!!写在其他包上面会有问题 //SpringBootApplication(scanBasePackages "com") //也可以自己设置扫描路径 &#xff33;&#xff50…

【Qt开发流程】之UDP

概述 UDP (User Datagram Protocol)是一种简单的传输层协议。与TCP不同&#xff0c;UDP不提供可靠的数据传输和错误检测机制。UDP主要用于那些对实时性要求较高、对数据传输可靠性要求较低的应用&#xff0c;如音频、视频、实时游戏等。 UDP使用无连接的数据报传输模式。在传…

白日门引擎传奇手游架设教程-GM的成长之路

准备工具 服务器一台&#xff08;Windows系统&#xff09;白日门引擎服务端版本一个 前言&#xff1a; 此次教程使用的是版本是一个决战斗罗的一个版本、服务器使用的是驰网科技的游戏高频系列服务器。 教程开始 在我们拿到版本之后、我们需要先把版本解压到服务器D盘的根目录…

Mac安装Typora实现markdown自由

一、什么是markdown Markdown 是一种轻量级标记语言&#xff0c;创始人为约翰格鲁伯&#xff08;John Gruber&#xff09;。 它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。这种语言吸收了很多在电子邮…

RocketMQ系统性学习-SpringCloud Alibaba集成RocketMQ以及顺序消费实战

顺序消费实战 顺序消费分为两种&#xff1a; 全局有序&#xff1a;适用于并发度不大&#xff0c;并且对消息要求严格一致性的场景下 通过创建一个 topic&#xff0c;并且该 topic 下只有一个队列&#xff0c;那么生产者向着一个队列中发消息&#xff0c;消费者也在这一个队列中…

Redis-数据结构

参考资料 极客时间Redis&#xff08;亚风&#xff09; Redis数据结构 SDS sds(Simple Dynamic String) 字符串接结构体: struct --attribute_- ((-_packed__)) sdshdr8{uint8_t len&#xff1b;/* buf已保祥的字符串字节数&#xff0c;不包含结束标示*/uint8_t alloc&#…

正式开通运营!“一应黔行”服务网点进驻贵阳地铁3号线

今天下午14:00&#xff0c;贵阳地铁3号线正式进入初期运营。为进一步实现一卡通在贵阳市全区域覆盖&#xff0c;不断提升“一应黔行一卡通”业务办理效率&#xff0c;贵阳市信捷科技有限公司在贵阳地铁3号线明珠大道站、顺海站、皂角井站3个站点设立“一应黔行”服务网点&#…

[Spring ~松耦合的设计神器]`SPI`

Java SPI&#xff08;Service Provider Interface&#xff09;是一种Java的服务提供者接口机制。它允许在运行时动态加载实现服务接口的类。 文章目录 基本概念最简单的实例使用 jar 包通过 spi动态实现接口功能 基本概念 SPI 机制的基本思想是&#xff0c;定义一个服务接口&a…

基于ssm线上学习网站论文

线上学习网站 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;作为学校以及一些培训机构&#xff0c;都在用信息化战术来部署线上学习以及线上考试&#xff0c;可以与线下的考试有机的结合在一起&#xff0c;实现线上学习网站在技术上已成熟。本文介绍了线上学…

【Android开发-25】Android中多线程编程用法介绍

1&#xff0c;线程基本用法 在Android中&#xff0c;线程的使用主要有两种方法&#xff1a;一种是扩展java.lang.Thread类&#xff0c;另一种是实现Runnable接口。 1.1以下是一个简单的Android线程继承Thread的用法示例&#xff1a; public class MyThread extends Thread {…

maven+spock

pom配置 话说JunitMockito的组合用起来是真难用&#xff0c;还是Spock的简单&#xff0c;尤其是参数化的测试。junit的Parameter是鸡肋&#xff0c;杂恶心&#xff1b;Theories用来也不爽。 <?xml version"1.0" encoding"UTF-8"?><project xm…

SoloLinker第一次使用记录,解决新手拿到板子的无所适从

本文目录 一、简介二、进群获取资料2.1 需要下载资料2.2 SDK 包解压 三、SDK 编译3.1 依赖安装3.2 编译配置3.3 启动编译3.4 编译后的固件目录 四、固件烧录4.1 RV1106 驱动安装4.2 打开烧录工具4.3 进入boot 模式&#xff08;烧录模式&#xff09;4.4 烧录启动固件4.5 烧录升级…