JVM 根可达算法

Java中的垃圾

Java中"垃圾"通常指的是不再被程序使用和引用的对象,具体表现在没有被栈、JNI指针和永久代对象所引用的对象。Java作为一种面向对象的编程语言,它使用自动内存管理机制,其中垃圾收集器负责检测和回收不再被程序引用的对象,以释放它们占用的内存空间。以下是一些导致对象成为垃圾的常见情况:

  1. 无引用对象: 当一个对象没有任何引用指向它时,它就变得不可达,成为垃圾,Java的垃圾收集器会识别这样的对象,并将它们回收。

  2. 引用循环: 如果一组对象彼此引用形成一个循环,而这个循环与程序的其他部分没有引用相连,那么这个循环中的对象就会成为垃圾。Java的垃圾收集器通过识别引用循环并处理它们来防止内存泄漏。

  3. 显式置空引用: 如果程序员显式地将一个引用置空(null),而没有其他引用指向相同的对象,那么该对象就变成了垃圾。

垃圾收集器周期性地运行,并识别和回收这些垃圾对象,释放其内存中对应的区域以确保内存能够得到有效利用,这种自动的内存管理机制就叫做垃圾回收。

如何寻找垃圾?

引用计数(Reference Count)
引用计数算法是一种垃圾标记,其核心思想是通过维护对象的引用计数来判断对象是否可以被回收。每个对象都有一个关联的引用计数,表示当前有多少个指针引用它。当引用计数为零时,意味着没有指针再引用该对象,因此可以安全地回收该对象的内存。

其实引用计数算法的核心思想就是,只要有对象引用我,那么就说明我是有用的,我还不需要被回收,反正,我就是没有用的对象,那么我和我的子对象都应该被回收掉。这里我们说的对象都是堆上的对象,一般是堆上的内存空间需要程序员手动回收,而栈上的内存空间则由操作系统自行回收。由于栈上的对象是操作系统自行管理和回收的,因此栈上的对象以及一些静态对象始终都是出于存活的状态,因此,堆中存活的对象至少会有一个引用(指针)指向它。

但是这样会存在着一个问题,就是对象中的引用关系形成了环状——循环引用,这种情况下环内所有对象的引用都是>1的,这样一来环内的所有都无法被回收从而造成“内存泄漏”。这是引用算法最主要的局限性,也是为什么JVM不采用循环计数的方法来标记垃圾的原因。

根可达算法(Root Search)

由于引用计数算法无法解决“循环引用”的问题,无可避免的会造成内存泄露,因此,Java没有采用引用计数算法来寻找垃圾。而是采用了一种从GC Roots开始搜索存活对象的垃圾标记算法——根可达算法。
在这里插入图片描述

哪些是GC Root?

  1. 线程栈 (Thread Stacks) :活动线程的栈帧中的本地变量引用的对象。每个线程都有一个栈,栈中的引用对象是潜在的存活对象。
  2. 静态变量 (Static Variables) :类的静态成员变量引用的对象。静态变量随着类的加载而初始化,它们的引用可能使对象保持存活。
  3. 常量池 (Constant Pool) :常量池中的引用,包括字符串常量等。这些常量在类加载时被创建,它们的引用也可能使对象保持存活。
  4. JNI 引用 (JNI References) :通过 JNI 在本地代码中创建的对象引用。如果 Java 代码通过 JNI 调用了本地方法,并在本地方法中创建了对象,这些对象的引用也是 GC Roots。
  5. 监控与管理 MBeans (JMX) :活动的监控、管理 MBeans 等通过 JMX 等管理工具注册的对象。这些对象的引用也被视为 GC Roots。

线程栈 (Thread Stacks)
在Java中,当程序运行的时候线程会将一个个方法放到栈上来执行,并且对于方法局部的一些小的对象和变量也会被分配在栈空间上,而栈空间是由操作系统本身来控制什么时候进行释放和分配的。因此,基于这个逻辑我们可以认为对于当前线程来说,存在于栈空间上的变量都是存活的,而且栈空间一般比较小只有几MB的大小,里面存活的变量和对象都是有限的作为GC Roots来说搜索起来也是非常高效的。

静态变量 (Static Variables)
在Java中静态变量一般是随着类加载的时候被创建和初始化的,和Java字节码一样,静态变量也会被加载到元空间(Meta Space,Java 8之前叫做方法区(Method Area)或叫做永久代(Permanent Generation),Java 8之后叫做元空间)。

元空间的对象是不会轻易被释放的,而静态变量会随着整个类被释放的时候才会被释放,因此静态变量可以作为GC Root来寻找垃圾。

常量池 (Constant Pool)
常量池(Constant Pool)是Java中一种存放常量的数据结构,用于存储编译期生成的字面量和符号引用。常量池属于元空间(Meta Space,Java 8之前叫做方法区(Method Area)或叫做永久代(Permanent Generation),Java 8之后叫做元空间),具体说是类加载后存放在元空间的一部分内存。

在Java程序的编译阶段,常量池会保存各种字面量和符号引用,包括字符串、类和接口的全限定名、字段和方法的名称和描述符等,这些信息在编译后会被存放在class文件的常量池中,在运行期间这些常量池依旧会存在并且Java根据常量池来映射参数。

所以,处于常量池中的变量也可以作为GC Roots来寻找垃圾

JNI 引用 (JNI References)
JNI(Java Native Interface)引用是指在Java程序中通过JNI创建的与本地代码(C++代码,调用平台相关函数)相互调用的引用。JNI引用包括本地引用(Local Reference)、全局引用(Global Reference)和弱全局引用(Weak Global Reference)。

本地引用(Local Reference): 本地引用是一种短期的引用,用于限定其生命周期。当Java方法调用本地方法时,本地引用会被创建,但在本地方法返回后,这些引用将被自动释放。本地引用不能作为GC Roots。

全局引用(Global Reference): 全局引用是一种长期有效的引用,可以在整个程序的生命周期内使用。全局引用可以防止被引用对象被垃圾回收,因此它可以作为GC Roots。

弱全局引用(Weak Global Reference): 弱全局引用也是一种全局引用,但它对被引用对象的生命周期没有强制影响。如果一个对象只被弱全局引用引用,那么它在垃圾回收时可能被回收。弱全局引用不能作为GC Roots。

JNI引用之所以能作为GC Roots,是因为它们可以在本地方法(C++方法,调用平台相关函数)中持有Java对象的引用,防止这些对象在本地方法执行期间被垃圾回收。全局引用在整个程序的生命周期内有效,因此它们有可能成为根引用,即GC Roots。

根可达算法原理

知道了什么是GC Roots那么根可达算法理解起来就相对来说会简单一些。GC Roots我们可以简单理解为和Java程序的生命周期强关联、和JVM生命周期强关联或者和当前线程强关联的一些对象。这些对象至少说在发生GC这一时刻是不应该被当成垃圾回收掉的,否则会影响程序的正常使用,因此,我们标记存活对象的时候从GC Roots开始,认为被GC Roots 引用或者间接引用的对象就是存活对象。因此,根可达算法的基本原理和流程如下:

  1. 初始根集合(Initial Roots): 根可达算法从程序的初始根集合开始,这些根是一组特殊的引用,如线程栈中的引用、静态变量、JNI(Java Native Interface)引用等。

  2. 标记阶段(Mark Phase): 算法通过追踪根引用,递归遍历对象图,标记所有可以从根引用访问到的对象。在这个过程中,被标记的对象被认为是可达的,而未被标记的对象被认为是不可达的。

  3. 标记-清除阶段(Mark-Sweep Phase): 在标记完成后,算法执行清除操作,即移除未被标记的对象。这些未被标记的对象被认为是不可达的,可以被垃圾回收器回收。这个阶段的目标是回收不再被程序使用的内存空间。

  4. 压缩(Compaction)或整理(Compaction): 在某些情况下,为了优化内存布局,可能会执行进一步的操作,如将存活对象整理到一起,以减少内存碎片。这个步骤通常与标记-清除阶段结合使用。

  5. 可选的再标记阶段(Optional Re-Mark Phase): 有些算法可能会在标记-清除后执行可选的再标记阶段,以处理在清除阶段可能发生的并发引用更新。这一步确保在垃圾回收过程中引用关系的一致性。

  6. 结束(Finish): 垃圾回收算法完成后,内存中只留下了可达对象,而不可达的对象已被清理。程序可以继续执行。

实际上来说,如CMS和G1之类比较流行的垃圾回收器都是采用的“三色标记”算法,而非直接采用的根可达算法来对垃圾进行标记的.

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

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

相关文章

Golang | Leetcode Golang题解之第145题二叉树的后序遍历

题目&#xff1a; 题解&#xff1a; func reverse(a []int) {for i, n : 0, len(a); i < n/2; i {a[i], a[n-1-i] a[n-1-i], a[i]} }func postorderTraversal(root *TreeNode) (res []int) {addPath : func(node *TreeNode) {resSize : len(res)for ; node ! nil; node n…

通过文本指令生成3D模型纹理贴图

在3D建模的广阔领域中,我们总是追求更高效、更直观的方法来创建和编辑模型。今天,我要向大家介绍一种革新性的技术,它能够通过文本指令来精确地控制3D模型的细节,包括纹理贴图的生成。 1. 技术定位 这项技术主要定位于交互式3D建模领域,它为用户提供了一种全新的方式来创…

可变参数以及不可变集合

可变参数&#xff1a; 格式&#xff1a; public class ArgsDemo {public static void main(String[] args) {System.out.println(getSum(1,2,3,4,5));}//可变参数public static int getSum(int...args){int sum 0;for (int arg : args) {sum arg;}return sum;} }可变参数的…

极致深耕,打造核心竞争壁垒——探寻蓝思科技穿越周期的密码

作者 | 曾响铃 文 | 响铃说 一家企业&#xff0c;如何才能在时代变幻的风云中不计较一时得失&#xff0c;长期稳健发展&#xff0c;穿越周期&#xff1f;本期主题就来探寻一家在湖南的国际化企业的发展密码。 穿越周期的企业&#xff0c;都在坚持一个驱动发展的“原点” 细…

✔️Vue基础++

✔️Vue基础 组件通信 什么是组件通信&#xff1f; 组件通信就是指 组件与组件 之间的 数据传递 组件的数据是独立的&#xff0c;无法直接访问其他组件的数据想使用其他组件的数据&#xff0c;就需要组件通信 组件之间如何通信&#xff1f; 组件关系 父子关系非父子关系 …

苹果WWDC 2024 带来的 AI 风暴:从生产力工具到个人助理,AI 将如何融入我们的生活?

2024年6月5日&#xff0c;苹果WWDC 2024全球开发者大会如约而至&#xff0c;带来了众多令人兴奋的新功能和新产品。其中&#xff0c;AI 技术的全面融入无疑是最引人注目的亮点。从 iOS、iPadOS 到 macOS&#xff0c;再到 Siri 和开发者工具&#xff0c;苹果正在将 AI 融入到其生…

HTML静态网页成品作业(HTML+CSS)—— 兰蔻化妆品网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

微软在Windows上做了个安卓子系统…

前言 曾经小白想着如果Windows在不安装模拟器的情况下&#xff0c;可以安装并运行安卓软件&#xff0c;那这个功能一定很香。 在2021年&#xff0c;微软面向开发者推出WSA支持。在第二年的时候&#xff0c;用户就可以在Windows上使用安卓软件。 这个功能可把我乐坏了&#x…

mysql中 什么是锁

大家好。上篇文章我们讲了事务并发执行时可能带来的各种问题&#xff0c;今天我们来聊一聊mysql面试必问的问题–锁。 一、解决并发事务带来问题的两种基本方式 1. 并发事务访问相同记录的情况 并发事务访问相同记录的情况大致可以划分为3种&#xff1a; 读-读情况&#xf…

23种设计模式之桥接模式

桥接模式 1、定义 桥接模式&#xff1a;将抽象部分与它的实现部分解耦&#xff0c;使得两者都能独立变化 2、桥接模式结构 Abstraction&#xff08;抽象类&#xff09;&#xff1a;它是用于定义抽象类的&#xff0c;通常是抽象类而不是接口&#xff0c;其中定义了一个Imple…

网络安全扫盲篇名词解释之“挖矿“

1. 什么是挖矿&#xff1f; 在数字世界的深处&#xff0c;有着一项神秘而引人入胜的活动&#xff0c;那就是——挖矿&#xff01;说到挖矿&#xff0c;就不得不提到比特币&#xff0c;即使你不懂的计算机相关术语&#xff0c;但是这个名词在生活中你肯定听到过。 比特币&…

【源码】16国语言交易所源码/币币交易+期权交易+秒合约交易+永续合约+交割合约+新币申购+投资理财/手机端uniapp纯源码+PC纯源码+后端PHP

测试环境&#xff1a;Linux系统CentOS7.6、宝塔面板、Nginx、PHP7.3、MySQL5.6&#xff0c;根目录public&#xff0c;伪静态laravel5&#xff0c;开启ssl证书 语言&#xff1a;16种&#xff0c;看图 这套带前端uniapp纯源码&#xff0c;手机端和pc端都有纯源码&#xff0c;后…

基于WPF技术的换热站智能监控系统02--标题栏实现

1、布局划分 2、准备图片资源 3、界面UI控件 4、窗体拖动和关闭 5、运行效果 走过路过不要错过&#xff0c;点赞关注收藏又圈粉&#xff0c;共同致富&#xff0c;为财务自由作出贡献

Dapr分布式应用运行时初探1

文章目录 一、Dapr是什么&#xff1f;二、使用步骤1.安装Dapr CLI2.Dapr初始化 总结 一、Dapr是什么&#xff1f; Dapr is a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless, and stateful applications that run on th…

[工具探索]英寸vs毫米下常见尺寸排版

文章目录 常见尺寸1. 照片尺寸2. 纸张尺寸3. 显示器和电视屏幕尺寸4. 手机屏幕尺寸5. 笔记本电脑屏幕尺寸6. 其他设备尺寸 换算公式换算方法常见照片尺寸对比表国际标准ISO&#xff08;216&#xff09;纸张尺寸 什么是英寸&#xff1f; 英寸&#xff08;英语&#xff1a;inch&a…

PowerInfer-2:第一个智能手机上高速推理大型语言模型

大型语言模型&#xff08;LLMs&#xff09;以其卓越的理解和生成类人文本的能力&#xff0c;从根本上增强了我们的日常生活&#xff0c;并改变了我们的工作环境。当今最先进的LLMs&#xff0c;如GPT4和Claude-3&#xff0c;托管在数据中心&#xff0c;配备了最先进的GPU&#x…

决策树原理实现

决策树学习算法包括三部分&#xff1a;特征选择、树的生成和树的剪枝。常用的算法有ID3、C4.5和CART。 特征选择的目的在于选取对训练数据能够分类的特征。特征选择的关键是其准则。常用的准则如下&#xff1a; &#xff08;1&#xff09;特征 A A A对训练数据集 D D D的信息…

智能驾驶时代:车联网需要怎样的智能网络底座?

2024年&#xff0c;智能驾驶市场火热&#xff0c;无论是造车新势力还是老牌车企纷纷发力智能驾驶&#xff0c;他们深知&#xff0c;新能源汽车的下半场已到&#xff0c;再不发力智能驾驶&#xff0c;可能真的有些来不及了。车企不断加码单车智能的同时&#xff0c;政府也在稳步…

IP服务器代理如何设置使用?

IP服务器代理&#xff08;通常称为代理IP或代理服务器&#xff09;的设置和使用方法可以根据不同的需求和场景而有所不同。以下是一个清晰的步骤指南&#xff0c;帮助你设置和使用IP服务器代理&#xff1a; 1. 选择合适的代理IP类型 根据使用目的的不同&#xff0c;可以选择不…

MFC案例:利用SetTimer函数编写一个“计时器”程序

一、希望达成效果 利用基于对话框的MFC项目&#xff0c;做一个一方面能够显示当前时间&#xff1b;另一方面在点击开始按钮时进行读秒计时&#xff0c;计时结果动态显示&#xff0c;当点击结束时读秒结束并保持最后结果。 二、编程步骤及相关代码、注释 1、启动VS…