JVM专题八:JVM如何判断可回收对象

在JVM专题七:JVM垃圾回收机制中提到JVM的垃圾回收机制是一个自动化的后台进程,它通过周期性地检查和回收不可达的对象(垃圾),帮助管理内存资源,确保应用程序的高效运行。今天就让我们来看看JVM到底是怎么定义“垃圾”对象的。

JVM垃圾对象判断方法

JVM在垃圾回收(Garbage Collection简称GC)过程中定义“垃圾”对象,主要是根据对象是否还可达来判断。以下是一些判断对象是否成为垃圾的常见标准:

  1. 引用计数法:这是一种简单的垃圾回收方法,每个对象都有一个引用计数器,每当有引用指向该对象时,计数器加一;每当引用离开作用域或被赋值为null时,计数器减一。当引用计数器为零时,对象被认为是垃圾。但这种方法无法处理循环引用的问题。

  2. 可达性分析:这是现代JVM中常用的垃圾回收方法。从一系列的“GC Roots”开始,所有能够通过引用链到达的对象都被认为是存活的,而那些无法到达的对象则被认为是垃圾。GC Roots通常包括:

    • 虚拟机栈(栈帧中的本地变量表)中的引用对象
    • 方法区中的类静态属性引用的对象
    • 运行时常量池中引用的对象
    • JNI(Java Native Interface)的引用对象
  3. 不可达的对象:即使对象不可达,JVM也不会立即回收它们。JVM会将这些对象标记为“即将回收”的状态。在下一次垃圾回收周期中,这些对象会被真正回收。

  4. 对象的finalize()方法:如果对象在被标记为垃圾之前定义了finalize()方法,JVM会给予对象最后一次“自救”的机会。在下一次垃圾回收时,JVM会尝试调用这个对象的finalize()方法。如果对象在finalize()方法中重新与GC Roots建立了连接,它将被移出即将回收的列表。

  5. 分代收集:JVM通常采用分代收集算法,将对象分为新生代和老年代。新生代中的对象通常存活时间较短,而老年代中的对象存活时间较长。JVM会根据对象的年龄来决定它们应该在哪个区域。

垃圾回收是一个复杂的过程,JVM会根据当前的内存使用情况和对象的生命周期来决定何时以及如何回收对象。

JVM回收对象示例

局部变量作为GC Roots

public class App {
    public static void main(String[] args) {
        runApp(args);
        System.out.println("====");

    }

    private static void runApp(String[] args) {
        SpringApplication sApp = new SpringApplication();
        sApp.run(App.class, args);
//        sApp.getRunListeners(args);

    }
}



public class SpringApplication {

    public String run(Class appClass, String[] args) {
        System.out.println("my Spring Application ");
        return "run args";
    }

    public String getRunListeners( String[] args) {
        System.out.println("My  getRunListeners ");
        String myRunListenersVar = "myRunListenersVar";
        this.getSpringFactoriesInstances(args);
        return "run args";
    }

    public String getSpringFactoriesInstances( String[] args) {
        System.out.println("My  getSpringFactoriesInstances ");
        String mySpringFactoriesInstancesVar = "mySpringFactoriesInstances";

        return "run args";
    }


}

上述代码中,最关键的一段是 SpringApplication sApp = new SpringApplication();代码分析如下图所示:

JVM垃圾回收线程扫描到Spring Appliedcation发现它有一个GC Roots,此时就不能回收了。

静态变量作为GC Roots

除了上述代码外,咱们在看一个常见的代码。

public class App {
    public static SpringApplication sApp = new SpringApplication();

    public static void main(String[] args) {
        runApp(args);
        System.out.println("====");

    }

    private static void runApp(String[] args) {
        SpringApplication sApp = new SpringApplication();
        sApp.run(App.class, args);

    }
}

上述 public static SpringApplication sApp = new SpringApplication() 代码分析如下图所示:

JVM垃圾回收线程扫描到Spring Appliedcation发现它有一个GC Roots,此时就不能回收了。相信有细心的小伙伴注意到这块代码与上述代码在示意图上的区别了,他们GC Roots 所在区域不一样,一个是在JVM栈中,一个是在JVM的方法区且特意把颜色换成同堆内存一样的颜色,其实就是想说这块区域是共享内存,而局部变量是线程内存。

因此这里留一个小小的思考问题:大家直观感受下这两个引用谁引用持有的时间会更长一点?

简单总结,只要对象被局部变量或静态变量引用就不会被回收。

Java中不同对象类型

我们在进行可达性分析的时候常常提到局部变量持有某个对象的引用,或者静态变量持有某个对象引用,但是我们脑海里需要清楚一个概率那就是java中有不同的引用类型,他们分别是强引用、软引用、弱引用和虚引用。

强引用

强引用是最常见的引用类型,它使得对象在任何情况下都不会被垃圾回收,除非显式地将引用设置为null或超出作用域。如下代码所示:

public class App {
    public static SpringApplication app = new SpringApplication();
}

软引用

软引用提供了一种在内存不足时可以被回收的机制。它们通常用于缓存,当内存足够时,对象不会被回收,但如果内存不足,垃圾回收器会尝试回收这些对象。使用软引用可以提高应用的内存效率,但需要权衡对象被回收的概率.

import java.lang.ref.SoftReference;
// ...

public class ConfigurationPropertyUtils {

    // ...

    private static final SoftReference<ConfigurationPropertyCache> cache = 
        new SoftReference<>(null);

    public static <T> T getCached(BeanFactory beanFactory, Class<T> cacheType) {
        // ...
        SoftReference<ConfigurationPropertyCache> ref = cache;
        ConfigurationPropertyCache cpCache = (ref != null ? ref.get() : null);
        if (cpCache == null) {
            cpCache = new ConfigurationPropertyCache(beanFactory, cacheType);
            cache = new SoftReference<>(cpCache);
        }
        // ...
        return cpCache.getProperty();
    }

    // ...
}

在这段代码中,ConfigurationPropertyUtils类使用了一个静态的软引用cache来存储配置属性的缓存。当调用getCached方法时,如果缓存不存在或已被垃圾回收,就会创建一个新的ConfigurationPropertyCache实例,并用软引用包装后存储起来。由于软引用对象在内存不足时会被回收,这有助于减少垃圾回收器的压力,避免长时间的GC停顿,提高应用的响应性。

所以在软引用放到缓存这个场景就非常实用,想想内存充足我就不回收,内存不够了我回收而缓存基本上都是为了提升性能的,现在内存都不足了还有啥自行车,能运行就不错啦 !

弱引用

弱引用不会阻止垃圾回收器回收对象,即使内存充足也会被回收。它们通常用于监听或观察对象的状态,而不阻止对象的回收。弱引用在内存不足时更有可能被回收,因为它们对对象的生命周期没有任何控制。

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
		implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {

	private final Object poolSizeMonitor = new Object();

	private int corePoolSize = 1;

	private int maxPoolSize = Integer.MAX_VALUE;

	private int keepAliveSeconds = 60;

	private int queueCapacity = Integer.MAX_VALUE;

	private boolean allowCoreThreadTimeOut = false;

	@Nullable
	private TaskDecorator taskDecorator;

	@Nullable
	private ThreadPoolExecutor threadPoolExecutor;

	// Runnable decorator to user-level FutureTask, if different
// 聚焦这里哈
	private final Map<Runnable, Object> decoratedTaskMap =
			new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.WEAK);

}

上述代码是Spring的ThreadPoolTaskExecutor节选部分,注意ThreadPoolTaskExecutor本身并不直接使用弱引用来引用任务对象。它主要使用弱引用来缓存线程池中的Future任务,以便在内存不足时可以回收这些不再需要的Future对象。这样做可以避免因缓存大量已完成或取消的任务的Future引用而导致的内存泄漏问题。

        一个对象被线程池持有强引用,那么即使这个对象不再被其他地方使用,它也不会被垃圾回收器回收,因为线程池中的引用阻止了垃圾回收。使用弱引用可以确保当没有其他强引用存在时,线程池中的任务可以被垃圾回收。

虚引用

虚引用是最弱的引用类型,它们几乎不提供任何保护,对象可以随时被回收。虚引用的主要作用是跟踪对象被回收的状态,通过ReferenceQueue来接收通知。

因为虚引用接触比较少,暂时就不讨论了,下面对他们四个应用简单总结下:

  • 强引用:只要强引用存在,对象就不会被垃圾回收。
  • 软引用:内存不足时,软引用对象会被垃圾回收,适用于缓存等场景。
  • 弱引用:弱引用对象在下一次垃圾回收时会被回收,适合用于监听或观察对象状态。
  • 虚引用:虚引用几乎不阻止对象回收,主要用于跟踪对象被垃圾回收的状态

到这里,我们已经Java介绍完垃圾对象的判断方法,并举出例子演示给大家看;同样提出几个问题:我们在介绍完GC Roots的时候分为不同类型的时候提出问题,他们引用时间的长短对于引用时间长短不同可以等价不同的对象实例存活时间。不同对象回收的机制一样嘛?如果不一样根据什么特点区分呢?回收以后内存空间要怎么整理呢?本章结束:有时间小伙伴可以回头看看,前面的文章回忆下各个区域的特点和整个分配的过程。从下一章我们将开始慢慢介绍GC算法,需要前面的铺垫。

 专题汇总

JVM专题一:深入分析Java工作机制

JVM专题二:Java如何进行编译的

JVM专题三:Java代码如何运行

JVM专题四:JVM的类加载机制

JVM专题五:类加载器与双亲委派机制

JVM专题六:JVM的内存模型

JVM专题七:JVM垃圾回收机制

JVM专题八:JVM如何判断可回收对象

JVM专题九:JVM分代知识点梳理

JVM专题十:JVM中的垃圾回收机制

JVM专题十一:JVM 中的收集器一

JVM专题十二:JVM 中的收集器二

JVM专题十三:总结与整理(面试常用)

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

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

相关文章

基于QT开发的气体成分检测数据记录软件

1、软件概述 气体成分检测数据记录软件用于实现多种气体分析仪及相关设备实时数据的获取、存储和传送。目前支持的设备主要有气体分析仪、多通道进样阀箱、冷阱处理系统和气体采样处理系统。   气体成分检测数据记录软件可以根据实际应用需要进行配置&#xff0c;以实现不同应…

制造企业用AI可以做什么?

ChatGPT横空出世&#xff0c;有关AI的浪潮席卷全球&#xff0c;比起人工智能如何改变世界&#xff0c;可能你更感兴趣我能用AI可以做的事情是什么&#xff1f; 制造企业用AI可以做的事情是什么?人工智能在制造企业中可以做很多事情&#xff0c;以下是一些主要的应用&#xff1…

【Nginx】源码安装

nginx官网&#xff1a;nginx: download 选择文档版本安装即可 1.安装依赖包 //一键安装上面四个依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel 2.下载并解压安装包 //创建一个文件夹 cd /usr/local mkdir nginx cd nginx //将下载的nginx压缩…

Linux(Ubuntu20.04)系统中安装deb软件包错误(依赖关系问题-仍未被配置)解决的办法

在Ubuntu16.04下采用如下dpkg命令安装deb软件安装包时&#xff1a; sudo dpkg -i XXXX.deb 发生安装失败&#xff0c;返回信息为&#xff02;正处理时有错误发生&#xff02;&#xff0c;并且在安装过程中出现&#xff02;依赖关系问题-仍未被配置&#xff02;的提示&#xff0…

240629_昇思学习打卡-Day11-Vision Transformer中的self-Attention

240629_昇思学习打卡-Day11-Transformer中的self-Attention 根据昇思课程顺序来看呢&#xff0c;今儿应该看Vision Transformer图像分类这里了&#xff0c;但是大概看了一下官方api&#xff0c;发现我还是太笨了&#xff0c;看不太明白。正巧昨天学SSD的时候不是参考了太阳花的…

编程开发不能不懂的世界协调时UTC的由来

在各种时间标准出现之前&#xff0c;各地都是根据太阳来进行计时的。把太阳连续2次经过地球同一位置所经历的时间间隔称为真太阳日&#xff0c;然后再把这个太阳日划分为更小的时间单位&#xff0c;例如中国古代使用日晷记录时间&#xff0c;把一个太阳日分为12个时辰。因为地球…

海康+libtorch的血泪教训

一、LibTorch使用&#xff0c; 详见&#xff1a; /INCLUDE:?warp_sizecudaatYAHXZ 二、海康二次开发&#xff0c; 目前选4.31&#xff0c;只能c14。 三、做dll注意&#xff1a;

【MongoDB】分布式数据库入门级学习

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;为祖国的科技进步添砖Java 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f8;友情提供 凯楠&#x1f4f8; - 不夜长安 目录 MongoDB 相关 数据库排行榜单 MongoDB 中文官网 菜鸟…

[Open-source tool]Uptime-kuma的簡介和安裝於Ubuntu 22.04系統

[Uptime Kuma]How to Monitor Mqtt Broker and Send Status to Line Notify Uptime-kuma 是一個基於Node.js的開軟軟體&#xff0c;同時也是一套應用於網路監控的開源軟體&#xff0c;其利用瀏覽器呈現直觀的使用者介面&#xff0c;如圖一所示&#xff0c;其讓使用者可監控各種…

【探索Linux】P.35(传输层 —— UDP协议)

阅读导航 引言一、UDP协议端格式二、UDP的特点三、UDP的缓冲区四、基于UDP的应用层协议温馨提示 引言 在上一篇文章中&#xff0c;我们深入探讨了网络协议的应用层&#xff0c;揭示了各种协议如何协同工作以确保信息在网络中正确、高效地传递。从HTTP到FTP&#xff0c;每一层协…

【分布式计算框架 MapReduce】MapReduce 初级编程

目录 一、MapReduce 示例程序的导入并运行测试 二、准备 4 个小文件&#xff08;文件大小分别为 1.7M&#xff0c;5.1M&#xff0c;3.4M&#xff0c;6.8M&#xff09; 1. 第一种情况&#xff0c;默认分片&#xff1a;不修改程序代码&#xff0c;直接使用 WordCount 源程序 2…

火了10年的电脑监控软件有哪些?盘点8款热门的电脑监控软件

电脑监控软件领域经历了多年的发展&#xff0c;一些软件因为其稳定的功能、良好的用户体验和不断更新的技术支持&#xff0c;得以在市场上保持长期的热度和用户基础。以下是几款在过去十年里广受好评且持续流行的内网监控软件&#xff1a; 1.安企神&#xff1a;由河北安企神网络…

c++ 子类继承父类

这个是子类继承父类 是否重写从父类那里继承来的函数 这个例子的路径 E盘 demo文件夹 fatherChildfunc

【C++ | 委托构造函数】委托构造函数 详解 及 例子源码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

周边美食小程序系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;美食店铺管理&#xff0c;菜品分类管理&#xff0c;标签管理&#xff0c;菜品信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;美食店铺&#…

Python 面试【★★★】

欢迎莅临我的博客 &#x1f49d;&#x1f49d;&#x1f49d;&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

springboot实习管理系统的设计与实现 LW +PPT+源码+讲解

第三章系统分析与设计 3.1 可行性分析 一个完整的系统&#xff0c;可行性分析是必须要有的&#xff0c;因为他关系到系统生存问题&#xff0c;对开发的意义进行分析&#xff0c;能否通过本系统来补充线下实习管理模式中的缺陷&#xff0c;去解决其中的不足等&#xff0c;通过对…

Java基础(五)——ArrayList

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

蓝卓出席“2024C?O大会”,探讨智能工厂建设新路径

6月29日&#xff0c;“2024C?O大会”在金华成功举办。此次大会由浙江省企业信息化促进会主办&#xff0c;与以往CIO峰会不同&#xff0c;“C?O”代表了企业数字化中的核心决策者群体&#xff0c;包括传统的CIO、CEO、CDO等。 本次大会围绕C?O、AIGC与制造业、数据价值、未来…

[NSSCTF]-Reverse:[SWPUCTF 2021 新生赛]easyapp(安卓逆向,异或)

无壳 把后缀名改为zip&#xff0c;找到apk 查看jadx 这里调用了MainActivity的lambda$onCreate$0$MainActivity&#xff0c;然后又调用了Encoder进行异或。 exp&#xff1a; result棿棢棢棲棥棷棊棐棁棚棨棨棵棢棌 key987654321 flag for i in range(len(result)):flagchr(…