JVM如何找到并清理垃圾?

如何找到垃圾

若一个对象不被任何对象或变量引用,那么它就是垃圾,需要被回收。 如何找到这个垃圾呢?

•引用计数法(Reference Counting)

•可达性分析法(GCRooting Tracing)

引用计数法

在对象头维护着一个 counter 计数器,对象被引用一次则计数器 +1;若引用失效则计数器 -1。当计数器为 0 时,就认为该对象无效了。

堆内存中主要存在三种引用关系:

•单一引用

•循环引用

•无引用

引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法。但是主流的 Java 虚拟机里没有选用引用计数算法来管理内存,主要是因为它很难解决对象之间循环引用的问题。(虽然循环引用的问题可通过 Recycler 算法解决,但是在多线程环境下,引用计数变更也要进行昂贵的同步操作,性能较低,早期的编程语言会采用此算法。)

举个栗子 :对象 objA 和 objB 都有字段 instance,令 objA.instance = objB 并且 objB.instance = objA,由于它们互相引用着对方,导致它们的引用计数都不为 0,于是引用计数算法无法通知 GC 收集器回收它们。

可达性分析法

所有和 GC Roots 直接或间接关联的对象都是有效对象,和 GC Roots 没有关联的对象就是无效对象。

GC Roots是指:

•Java 虚拟机栈(栈帧中的本地变量表)中引用的对象

•本地方法栈中引用的对象

•方法区中常量引用的对象

•方法区中类静态属性引用的对象

•虚拟机内部的引用

GC Roots 并不包括堆中对象所引用的对象,这样就不会有循环引用的问题。

引用的种类

判定对象是否存活与“引用”有关。在 JDK 1.2 以前,Java 中的引用定义很传统,一个对象只有被引用或者没有被引用两种状态,我们希望能描述这一类对象:当内存空间还足够时,则保留在内存中;如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为了以下四种。不同的引用类型,主要体现的是对象不同的可达性状态reachable和垃圾收集的影响。

强引用(Strong Reference)

类似 "Object obj = new Object()" 这类的引用,就是强引用,只要强引用存在,垃圾收集器永远不会回收被引用的对象。但是,如果我们错误地保持了强引用,比如:赋值给了 static 变量,那么对象在很长一段时间内不会被回收,会产生内存泄漏。

软引用(Soft Reference)

软引用是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象。JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。 SoftReference<Object> sf = new SoftReference<Object>(obj)

弱引用(Weak Reference)

弱引用的强度比软引用更弱一些。当 JVM 进行垃圾回收时,无论内存是否充足,都会回收只被弱引用关联的对象。

WeakReference<Object> wf = new WeakReference<Object>(obj)

虚引用(Phantom Reference)

虚引用也称幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响。它仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制,比如,通常用来做所谓的 Post-Mortem 清理机制。

PhantomReference<Object> pf = new PhantomReference<Object>(obj,new ReferenceQueue<>())

回收堆中无效对象

对于可达性分析中不可达的对象,也并不是没有存活的可能。

是否有必要执行

JVM 会判断此对象是否有必要执行 finalize() 方法,如果对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,那么视为“没有必要执行”。那么对象基本上就真的被回收了。

如果对象被判定为有必要执行 finalize() 方法,那么对象会被放入一个 F-Queue 队列中,虚拟机会以较低的优先级执行这些 finalize()方法,但不会确保所有的 finalize() 方法都会执行结束。如果 finalize() 方法出现耗时操作,虚拟机就直接停止指向该方法,将对象清除。

对象重生或死亡

如果在执行 finalize() 方法时,将 this 赋给了某一个引用,那么该对象就重生了。如果没有,那么就会被垃圾收集器清除。

任何一个对象的 finalize() 方法只会被系统自动调用一次,如果对象面临下一次回收,它的 finalize() 方法不会被再次执行,想继续在 finalize() 中自救就失效了。

回收方法区内存

方法区中存放生命周期较长的类信息、常量、静态变量,每次垃圾收集只有少量的垃圾被清除。方法区中主要清除两种垃圾:

•废弃常量

•无用的类

判定废弃常量

只要常量池中的常量不被任何变量或对象引用,那么这些常量就会被清除掉。比如,一个字符串 "bingo" 进入了常量池,但是当前系统没有任何一个 String 对象引用常量池中的 "bingo" 常量,也没有其它地方引用这个字面量,必要的话,"bingo"常量会被清理出常量池。

判定无用的类

判定一个类是否是“无用的类”,条件较为苛刻。

•该类的所有对象都已经被清除

•加载该类的 ClassLoader 已经被回收

•该类的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

一个类被虚拟机加载进方法区,那么在堆中就会有一个代表该类的对象:java.lang.Class。这个对象在类被加载进方法区时创建,在方法区该类被删除时清除。

如何清理垃圾

学会了如何判定无效对象、无用类、废弃常量之后,剩余工作就是回收这些垃圾。常见的垃圾收集算法有以下几个:

标记-清除算法(Mark-Sweep)

标记的过程是:遍历所有的 GC Roots,然后将所有 GC Roots 可达的对象标记为存活的对象。

清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。与此同时,清除那些被标记过的对象的标记,以便下次的垃圾回收。

这种方法有两个不足:

•效率问题:标记和清除两个过程的效率都不高。

•空间问题:标记清除之后会产生大量不连续的内存碎片,碎片太多可能导致以后需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法(Copying)

为了解决效率问题,“复制”收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完,需要进行垃圾收集时,就将存活者的对象复制到另一块上面,然后将第一块内存全部清除。这种算法有优有劣:

•优点:不会有内存碎片的问题。

•缺点:内存缩小为原来的一半,浪费空间。

为了解决空间利用率问题,可以将内存分为三块: Eden、From Survivor、To Survivor,比例是 8:1:1,每次使用 Eden 和其中一块 Survivor。回收时,将 Eden 和 Survivor 中还存活的对象一次性复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才使用的 Survivor 空间。这样只有 10% 的内存被浪费。

但是我们无法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够,需要依赖其他内存(指老年代)进行分配担保。

分配担保

为对象分配内存空间时,如果 Eden+Survivor 中空闲区域无法装下该对象,会触发 MinorGC 进行垃圾收集。但如果 Minor GC 过后依然有超过 10% 的对象存活,这样存活的对象直接通过分配担保机制进入老年代,然后再将新对象存入 Eden 区。

标记-整理算法(Mark-Compact)

标记:它的第一个阶段与标记-清除算法是一模一样的,均是遍历 GC Roots,然后将存活的对象标记。

整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。

这是一种老年代的垃圾收集算法。老年代的对象一般寿命比较长,因此每次垃圾回收会有大量对象存活,如果采用复制算法,每次需要复制大量存活的对象,效率很低。

•优点:空间没有浪费,没有内存碎片化问题

•缺点:性能较低

分代收集算法

根据对象存活周期的不同,将内存划分为几块。一般是把 Java 堆分为新生代和老年代,针对各个年代的特点采用最适当的收集算法。

•新生代:复制算法,弱分代假说

•老年代:标记-清除算法、标记-整理算法,强分代假说

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

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

相关文章

浏览器无网

目录 1.运行网络诊断&#xff0c;确认原因 原因A.远程计算机或设备将不接受连接(该设备或资源(Web 代理)未设置为接受端口“7890”上的连接 原因B.DNS服务器未响应 场景A.其他的浏览器可以打开网页&#xff0c;自带的Edge却不行 方法A&#xff1a;关闭代理 Google自带翻译…

iptables命令详解

简介 iptables 是 Linux 系统中用于配置 IPv4 数据包过滤规则的工具。它是 Linux 内核中 Netfilter 框架的一部分&#xff0c;通过设置规则&#xff0c;可以实现网络包的过滤、NAT 转发、端口映射等功能。 基本概念 表&#xff08;Tables&#xff09;&#xff1a; filter 表…

【牛客】几何糕手、国际裁判带师、数位dp?、灵异背包、矩阵快速幂签到、第一次放学

文章目录 《几何糕手》题目描述思路代码 《国际裁判带师》题目描述思路代码 《数位dp?》题目描述思路代码 《灵异背包》题目描述思路代码 《矩阵快速幂签到》题目描述思路代码 《第一次放学》题目描述思路代码 《几何糕手》 题目链接 题目描述 “芝士肾么&#xff1f;” 地…

Python学习03—Python语法元素分析

一、程序的格式框架 1.1 代码高亮 代码高亮是Python编程环境根据代码不同含义&#xff0c;给予不同色彩标注的一种色彩辅组体系。在不同的代码编程环境中&#xff0c;代码高亮的表现形式各有不同。 1.2 缩进 缩进是一行代码开始前的空白区域&#xff0c;它用来表达程序的格式…

php比较运算,强相等(===)弱相等(==)表

弱相等&#xff08;&#xff09; 符号为&#xff1a; 规则为&#xff1a;只比较值&#xff0c;不比较类型&#xff0c;只要值对就为true 样例&#xff1a;比较整型123和字符串"123"&#xff0c;运行结果给出了true 弱相等表&#xff1a;* 代表在 PHP 8.0.0 之前为…

使用trace工具分析Mysql如何选择索引

背景说明 工作中,可能会遇到执行一个SQL,明明有索引,但是采用explain分析后发现执行结果并未走索引。甚至还有部分SQL语句相同就只是查询条件不一样也会出现有的走索引,有的不走索引情况。比如: 我的示例环境有个employees表,并有个idx_name_age_position的联合索引…

Dirichlet Process 4

每一个样本都有自己对应的&#xff0c;有多少个样本就有多少个 如果有a个相等&#xff0c;那么我们能够相信这a个对应的样本x属于同一类的 要保证能够相等&#xff0c;所以要从一个离散的分布&#xff0c;即G中产生 所以有如下关系 图模型如下&#xff1a; &#xff0c;这里面…

顺序表和链表【数据结构】【基于C语言实现】【一站式速通】

线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表和链表的物理结构&#xff1a; 线性表在逻辑上是线性结构&…

【数据结构】二叉树算法讲解(定义+算法原理+源码)

博主介绍&#xff1a;✌全网粉丝喜爱、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦&#xff01; &#x1f345;附上相关C语言版源码讲解&#x1f345; &#x1f44…

Lingo数学建模基础

1.基本运算符 1.1算数运算符 1.2逻辑运算 #not# 否定操作数的逻辑值&#xff0c;一元运算符 #eq# 若两运算数相等&#xff0c;则为true,否则为false #ne# 若两运算数不相等&#xff0c;则为true,否则为false #gt# 若左边运算数严格大于右边&#xff0c;则为true,否则为…

Nacos源码下载与运行

早先在linux环境下搭建过nacos环境 即Centos安装部署nacos实战&#xff0c;本次是从官网上下载源码&#xff0c;本地运行看看&#xff0c;记录过程&#xff0c;方便备查。 第一步、Nacos源码下载 推荐到nacos官网下载 Github地址&#xff0c;本次选择最新版&#xff0c;1.4.7…

x-cmd pkg | ascii-image-converter - 图像转 ASCII 艺术照工具

目录 简介首次用户功能特点竞品和相关作品进一步阅读 简介 ascii-image-converter 是图像转换工具&#xff0c;用于将图像转换为 ascii art 图片并在控制台上打印。 首次用户 使用 x env use ascii-image-converter 即可自动下载并使用 在终端运行 eval "$(curl https:/…

智能机器人与旋量代数(9)

Chapt 3. 螺旋运动与旋量代数 3.1 螺旋运动 螺旋运动是关于一条空间直线的一个旋转运动&#xff0c;并伴随沿此直线的一个平移。是一种刚体绕空间轴 s s s旋转 θ \theta θ角&#xff0c;再沿该轴平移距离 d d d的复合运动&#xff0c;类似螺母沿螺纹做进给运动的情形。 一…

NQA网络质量分析

概念 网络质量分析是设备上集成网络测试功能,不仅可以实现对网络运行情况的准确测试,还可以输出统计信息,有效的节约成本。 NQA可以检测网络上运行的各种协议的性能,使运营商能够实时采集到各种网络运行指标。 例如:HTTP的总时延、TCP连接时延、DNS解析时延、文件传输速…

【好用的AI工具Kimi Chat】帮助提高面试效率

一、背景 年前裁员潮&#xff0c;不少人离职找工作&#xff0c;以及年后金三银四&#xff0c;也是求职高峰期。如何更高效的复习技术知识&#xff0c;以及特别是横纵向比对有总结性的问题。本文以面试【测试开发】的岗位为例&#xff0c;对面试题进行拓展&#xff0c;让AI帮助…

MMagic调试(训练)dreambooth

时间&#xff1a;2024.1.23 1.dreambooth配置文件 dreambooth在mmagic中的路径&#xff1a; configs/dreambooth本文以dreambooth.py 为例 configs/dreambooth/dreambooth.py2.下载数据集 下载数据集并保存至data/dreambooth/&#xff0c;数据集&#xff1a; https://dri…

buffer pool和查询缓存的区别

在学习buffer pool的时候我产生了疑问&#xff0c;buffer pool和查询缓存是一个东西吗&#xff1f; 结论&#xff1a;不是一回事。 buffer pool buffer pool我之前介绍过&#xff0c;它的出现是为了提高查找效率&#xff0c;缓存磁盘上的数据页。 buffer pool虽说是内存中的一…

【GitHub项目推荐--不错的 C++开源项目】【转载】

01 C 那些事 这是一个适合初学者从入门到进阶的仓库&#xff0c;解决了面试者与学习者想要深入 C及如何入坑 C的问题。 除此之外&#xff0c;本仓库拓展了更加深入的源码分析&#xff0c;多线程并发等的知识&#xff0c;是一个比较全面的 C 学习从入门到进阶提升的仓库。…

Java面试题之基础篇

文章目录 一&#xff1a;谈谈你对面向对象的理解二&#xff1a;JDK、JRE、JVM三者区别和联系三&#xff1a;和equals比较四&#xff1a;hashCode与equals五&#xff1a;final六&#xff1a;String、StringBuffer、StringBuilder七&#xff1a;重载与重写的区别&#xff1f;八&a…

Leetcode27-判断一个数的数字技术是否等于数位的值(2283)

1、题目 给你一个下标从 0 开始长度为 n 的字符串 num &#xff0c;它只包含数字。 如果对于 每个 0 < i < n 的下标 i &#xff0c;都满足数位 i 在 num 中出现了 num[i]次&#xff0c;那么请你返回 true &#xff0c;否则返回 false 。 示例 1&#xff1a; 输入&am…