死磕P7: JVM垃圾回收那点事,轻松拿捏不是事儿(二)

这是「死磕P7」系列第 004 篇文章,欢迎大家来跟我一起 死磕 100 天,争取在 2025 年来临之际,给自己一个交代。(公&号:新质程序猿,更新会更及时,内容也会更全面丰富,欢迎大家关注互动)

上一篇给大家介绍了 JVM 为了垃圾回收的方便将 堆 空间划分为了 年轻代老年代,并且给年轻代又划分为 Eden,S0, S1 三个区域,那到底什么是垃圾,如何确认对象是否还需要继续留存呢?

本节课就来学习一下垃圾回收的考点之一:什么是垃圾?

在 JVM 语境下,“垃圾”指的是死亡的对象所占据的空间

所谓“垃圾收集”,就是将已分配出去、但不再使用的内存回收回来,以便能再次分配

对象是否死亡?

如何判断一个对象是否死亡(即不可能再被任何途径使用)?通常有以下两种方法:

  • 引用计数法

  • 可达性分析法

引用计数法

为每个对象添加一个引用计数器,用来统计指向该对象的引用个数。当有地方引用它时,计数器加一;引用失效时减一。当某个对象的引用计数为零时,说明该对象已死亡,便可以被回收了。

  • 优点:原理简单,判定效率高。

  • 缺点:无法解决循环依赖的问题(对象之间相互循环引用)。

可达性分析法

通过一系列称为 GC Roots 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为"引用链"(Reference Chain),若某个对象到 GC Roots 间没有任何引用链相连(或者用图论的话来说就是从 GC Roots 到这个对象不可达时)则证明此对象是不可能再被使用的。

GC Roots 可理解为「堆外指向堆内的引用」。在 Java 技术体系中,固定可作为 GC Roots 的对象包括以下几种(看 2 遍就好了):

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

  • 类静态属性引用的对象(比如引用类型的静态变量)

  • 常量引用的对象(比如字符串常量池的引用)

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

  • JVM 内部的引用(例如:基本数据类型的 Class 对象、常驻异常、系统类加载器)

  • 同步锁(synchronized 关键字)持有的对象

  • 反应 JVM 内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等

上面提到了引用,那什么是引用呢,Java 中都有哪些引用呢?

引用的分类

无论是引用计数法还是可达性分析法,二者都离不开「引用」,Java 中定义了 4 种引用类型

  • 强引用

  • 软引用

  • 弱引用

  • 虚引用

简单记一下就好了~

强引用(Strongly Reference)

例如:Object obj = new Object()

特点:无论任何情况,只要强引用存在,垃圾收集器永不回收被引用的对象

软引用(Soft Reference)

场景:用于一些还有用、但非必须的对象

时机:被软引用关联的对象,在系统将发生 OOM 前,回收这些内存

实现:java.lang.ref.SoftReference

弱引用(Weak Reference)

场景:非必须对象,比软引用更弱

时机:被弱引用关联的对象只能生存到下一次垃圾收集发生(无论内存是否充足都会回收)

实现:java.lang.ref.WeakReference

虚引用(Phantom Reference)

又称“幽灵引用”或“幻影引用”

特点:最弱的引用,是否存在完全不会影响其生存时间,无法通过它获取对象实例

唯一目的:该对象被回收时收到一个系统通知

实现:java.lang.ref.PhantomReference

垃圾回收算法

提到算法,是不是很神秘?没事,有些算法还是比较容易的,比如:把大象放进冰箱需要几步?

垃圾回收算法主要有 4 种:标记复制、标记清除、标记整理、分代收集

标记复制

把内存空间划为两个相等的区域,每次只使用其中一个区域。

优点:实现简单,内存效率高,不易产生碎片

缺点:内存压缩了一半,倘若存活对象多,效率会大大降低

一般不需要按照 1:1 的比例划分内存空间,而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。

当回收时,将 Eden 和 Survivor 中还存活的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。

HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1 (即“浪费”了 10% 的新生代空间)。

由于无法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够用时,需要依赖老年代进行分配担保,也就是直接进入老年代(相当于“兜底方案”)。

现在大多数虚拟机对新生代的垃圾回收都采用的这种算法,并且其他算法也是在这个算法的基础上进行改进优化。

标记清除

标记出所有需要回收的对象,在标记完成后统⼀回收所有被标记的对象

优点:实现简单

缺点:效率低,标记清除后会产⽣⼤量不连续的碎⽚

标记整理

复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。

标记-整理(Mark-Compact)算法:标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

标记-清除算法与标记-整理算法区别:前者是一种非移动式的回收算法,后者是移动式的。

首先标记出哪些对象可被回收,在标记完成后,将对象向一端移动,然后直接清理掉边界以外的内存。

优点:解决了产生大量不连续碎片问题

分代收集

根据各个年代的特点选择合适的垃圾收集算法。

新生代中因为对象都是"朝生夕死的",90% 以上的对象存活率很低,适用于复制算法,复制算法比较适合用于存活率低的内存区域。

它效率高且无碎片问题,无非就是占点内存而已,空间换时间。

每次新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。

在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代,该区域中对象存活率高。

老年代的垃圾回收(又称Major GC)通常使用 「标记-清理」或「标记-整理」算法(跟 JDK 版本还有指定配置有关)。

回收算法只是概念,理论层面的,具体实现上就对应着垃圾收集器了,下面介绍一下垃圾收集器。

垃圾收集器

衡量垃圾收集器优劣的指标主要有三个:

  • 内存占用(Footprint)

  • 吞吐量(Throughput)

  • 延迟(Latency)

此三者构成了一个「三元悖论」难以同时满足。

收集算法只是内存回收的方法论,而垃圾收集器才是内存回收的具体实现(可理解为“接口”与“实现类”的关系)。

下面这个图,刚开始可能看不明白,MinorGC 对应年轻代可用的垃圾收集器,MajorGC 对应老年代可用的垃圾收集器,中间的 -XX 参数表示指定使用哪两组收集器来完成 MinorGC, MajorGC, 下图中总共有 5 组可用的组合(当然还有未画的,做个了解即可)。

下面来具体看一下上面提到的垃圾收集器是什么吧!

Serial/Serial Old

单线程收集器,历史最悠久的垃圾收集器,工作时必须暂停所有用户线程(STW, Stop The World)。

Serial收集器采用「标记-复制」主要针对新生代的收集。

Serial Old收集器采用「标记-整理」算法主要针对老年代的收集器。

实现简单高效,但会停顿。

Serial 配合 Serial Old 示意图:

ParNew

ParNew 收集器实质上是 Serial 收集器的多线程并行版本,同样作用于 MinorGC。

ParNew 配合 Serial Old 示意图:

Parallel Scavenge/Parallel Old

Parallel 是新生代收集器,同样采用「标记-复制」算法,并行收集的多线程收集器,也称“吞吐量优先收集器”。

能更高效地利用 CPU 资源,尽快完成计算任务(主要适合在后台运算而不需要太多交互的任务)。

Parallel Old采用「标记-整理」算法主要针对老年代的收集器。

Parallel 配合 Parallel Old 示意图:

在注重吞吐量或者处理器资源较为稀缺的场合,都可以考虑 Parallel Scavenge + Parallel Old 收集器的组合。

CMS

CMS(Concurrent Mark Sweep)收集器是一种以「获取最短回收停顿时间」为目标的收集器。

它基于「标记-清除」算法实现,运作过程分为四步:

  • 初步标记(CMS initial mark):只标记 GC Roots 能直接关联到的对象,速度很快;

  • 并发标记(CMS concurrent mark):从 GC Roots 遍历整个对象图,耗时较长,但无需停顿用户线程(可与用户线程并发执行);

  • 重新标记(CMS remark):修正并发标记期间,因用户线程导致标记产生变动的标记记录;

  • 并发清除(CMS concurrent sweep):清理删除标记阶段判断的已经死亡的对象,可与用户线程并发执行。

目前很大一部分 Java 应用集中在互联网网站或者 B/S 系统的服务上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验,CMS 收集器非常符合这类应用的需求。

CMS 收集器示意图:

Garbage First(G1)

G1 收集器是收集器技术发展最前沿的成果,面向服务端应用的收集器,能充分利用多CPU、多核环境。

相比与 CMS 收集器,G1 收集器两个最突出的改进是:

  • 基于 「标记-整理」 算法,不产生内存碎片。

  • 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域。

G1 收集器的定位是「CMS 收集器的替代者和继承人」,这里只做简单描述:

  • JDK 7 Update 40 时,Oracle 认为它达到了足够成熟的商用程度;

  • JDK 8 Update 40 时,G1 收集器提供了并发的类卸载支持,被 Oracle 称为“全功能的垃圾收集器(Fully-Featured Garbage Collector)”。

此外,还有一些更为先进的低延迟收集器,比如 OracleJDK 11 加入的 ZGC,RedHat 公司的 Shenandoah 收集器,知道一点即可。

总结

本文又是一片硬骨头,放松心态,当个小说读两遍,能记多少记多少吧!

回顾一下本篇内容就是,了解了对象是否存活的 2 种策略:引用计数法和可达性分析法,了解了 Java 的 4 类引用分类:强软弱虚,知道了垃圾回收的常见算法:标记-复制,标记-清除,标记-整理,并根据图示能理解其大概行为,最后介绍了常见的垃圾收集器:Serial, ParNew, Parallel, CMS, G1, ZGC, 知道其大概优缺点即可。


END

好了,今天的分享就到这里,关注公&号:新质程序猿,和我一起死磕 P7, 一起学习成长。

感谢大家的阅读,如果有任何异议的地方,欢迎指正,也欢迎大家公号找到我与我做朋友!

小福利

文末小福利,作为资深囤货达人,购置或转存了上千 T 的各种资源,反正我也学不完,如有需要,可以公号: 新质程序猿 找到我,直接送您,能帮助到大家也算是有所福报吧!

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

神仙级AI产品经理入门手册,从入门到入魂非常详细,收藏这一篇,少走三年弯路!!!

作为一个产品经理,你可能已经熟悉了一些常见的AI技术和应用,比如机器学习、深度学习、自然语言处理、计算机视觉等。 但是,你是否了解什么是大模型?大模型又有什么特点和优势?为什么大模型会成为AI领域的一个重要趋势…

Llama系列迈向多模态新时代:3.2版本开源超越闭源,并携手Arm推出手机优化版

在多模态领域,开源模型也超闭源了! 就在刚刚结束的 Meta 开发者大会上,Llama 3.2 闪亮登场: 这回不仅具备了多模态能力,还和 Arm 等联手,推出了专门为高通和联发科硬件优化的 “移动” 版本。 具体来说&a…

尾矿库安全监测系统:守护矿山安全的关键技术

尾矿库是矿山企业用于存放尾矿的重要设施,其安全状况直接关系到周边环境和人民生命财产安全。近年来,随着技术的不断进步,尾矿库安全监测系统应运而生,为尾矿库的安全管理提供了强有力的技术支持。本文将详细介绍尾矿库安全监测系…

en造数据结构与算法C# 之 二叉排序树的删除

en造数据结构与算法C# 之 二叉排序树的增/查-CSDN博客 删除方法比起添加和查找就稍显复杂了 &#xff0c;所以单独拿出来写一篇 分析 输入 1.根节点&#xff0c;用于从根上查找你要删除的节点 2.需要删除的值 public Node<T> Delete(Node<T> root, T data) {if (…

设计模式、系统设计 record part02

软件设计模式&#xff1a; 1.应对重复发生的问题 2.解决方案 3.可以反复使用 1.本质是面向对象 2.优点很多 1.创建型-创建和使用分离 2.结构型-组合 3.行为型-协作 571123种模式 UML-统一建模语言-Unified Modeling Language 1.可视化&#xff0c;图形化 2.各种图&#xff08;9…

SSH连接提示秘钥无效

说明&#xff1a;本文记录一次使用SSH连接服务器失效的问题。 使用SSH命令连接服务器&#xff0c; ssh -i ssh秘钥路径 user192.xx.xx.xx提示下面的错误&#xff1b; Load key "shuhe.bin": invalid format aochuang192.xx.xx.xx: Permission denied (publickey,g…

Python新手学习过程记录之基础环境:环境变量、版本区分、虚拟环境

https://img-blog.csdnimg.cn/img_convert/0604267530a515112e51dfc80d0b0ee7.png 刚开始接触Python并学习一门开发语言,可能就会遇到一些棘手的问题,比如电脑上不知不觉已经安装了多个python版本,python3.8/3.10/3.11,甚至一些软件中也集成有python解释器&#xff1b;那么我编…

每日一题|2516. 每种字符至少取 K 个|双指针、最长子串、字典

本题需要转化求解目标。 对于一个序列&#xff0c;两头收集的最少数量的时候&#xff0c;剩下的部分&#xff08;我们称之为子串&#xff09;就会对应的越长。也就是说&#xff0c;我们只要求解一个满足要求的最长子串&#xff0c;使得两边剩余的字符数量刚好满足要求。 由于…

实时美颜功能技术揭秘:视频美颜SDK与API的技术剖析

当下&#xff0c;用户希望在视频直播中呈现出最佳状态&#xff0c;这推动了视频美颜SDK和API的迅速发展。本文将深入剖析这项技术的核心原理、应用场景以及未来趋势。 一、实时美颜技术的基本原理 在实现这些效果的过程中&#xff0c;视频美颜SDK通常会使用以下几种技术&…

Geekbench6使用指南:Linux系统性能测试,如此简单!

在当今计算机性能日益重要的时代&#xff0c;Geekbench 成为了测试 CPU 性能的热门工具。本文将带你深入了解如何使用 Geekbench&#xff0c;让你的电脑性能一目了然。 1. 什么是 Geekbench&#xff1f; Geekbench 是一款跨平台的基准测试工具&#xff0c;能够评估单核和多核…

Ubuntu Server 20.04 64bit定时备份MySQL8.0.36数据库数据

一、编写sh脚本 常见备份命令介绍 我选用的是mysqldump命令&#xff0c;命令使用简介 [root]> mysqldump -helpUsage: mysqldump [OPTIONS] database_name [tables] OR mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...] OR mysqldump [OPTIONS] --all…

字节软件测试岗二面:APP测试问题被虐哭了,直到看到这些知识点

APP测试一直是大厂软件测试问到的一个重点细节&#xff0c;下面我来给大家展示一下大厂关于APP测试经常会问到的一些问题&#xff0c;以及解析。想象一下当你遇到面试官问到你这些问题时&#xff0c;你是否也能够对答如流哦&#xff01; 注意&#xff0c;注意啦&#xff01;小编…

对抗攻击方法详解:梯度攻击、转移攻击与模型集成攻击

对抗攻击方法详解&#xff1a;梯度攻击、转移攻击与模型集成攻击 近年来&#xff0c;随着深度学习模型在各个领域取得惊人突破&#xff0c;对抗攻击&#xff08;Adversarial Attack&#xff09; 逐渐成为研究热点。对抗攻击旨在通过在输入数据上施加精心设计的微小扰动&#x…

Meta号称替代程序员的LlamaCoder效果被CodeFlying秒杀,来看实测!

在讲LlamaCoder之前咱先浅聊一下Meta 这两天Meta凭借着Connect 2024大会可谓是风头正盛&#xff0c;刚刚发布的全新开源多模态大模型Llama3.2&#xff0c;热度是一路赶超咱的小草莓啊。 作为一款开源大模型能够和闭源的4o-mini、Claude3 Haiku打的有来有回&#xff0c; 甚至L…

32. Java栈和队列

1. 前言 栈和队列相关的题目是校招中出现频率一般,但是是属于相对基础的题型。我们要关注两类问题,栈和队列的添加和删除操作,以及栈和队列之间的区别和联系。 2. 栈和队列 2.1 数据结构 首先我们给出栈和队列的数据结构定义: (1)栈(Stack):允许在某一端插入元素(…

Ubuntu下Kafka安装及使用

Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;同时也是一个高吞吐量的分布式发布订阅消息系统。它由Scala和Java编写&#xff0c;具有多种特性和广泛的应用场景。 Kafka是一个分布式消息系统&#xff0c;它允许生产者&#xff08;Producer&#xff09;发布消…

CUDA error: no kernel image is available for execution on the device

记录一下出现上述问题的一个原因&#xff1a; pytorch所依赖的cuda版本不满足显卡(GPU)的算力要求&#xff01; 举例来说&#xff0c;显卡是3090&#xff0c;并按照以下命令安装Pytorch: conda install pytorch1.7.0 torchvision0.8.0 torchaudio0.7.0 cudatoolkit10.1 -c p…

另外知识与网络总结

一、重谈NAT&#xff08;工作在网络层&#xff09; 为什么会有NAT 为了解决ipv4地址太少问题&#xff0c;到了公网的末端就会有运营商路由器来构建私网&#xff0c;在不同私网中私有IP可以重复&#xff0c;这就可以缓解IP地址太少问题&#xff0c;但是这就导致私有IP是重复的…

2024最新gewechat开发微信机器人教程说明

简介&#xff1a;本文将指导你如何搭建一个微信机器人&#xff0c;通过接入gewe框架实现智能回复与聊天功能。我们将从基础设置开始&#xff0c;逐步讲解如何配置机器人&#xff0c;并通过实例展示其实际应用。 随着人工智能技术的不断发展&#xff0c;智能机器人已经成为我们…

南沙csp-j/s一对一家教 解一本通题: 1937:【06NOIP普及组】数列

【题目描述】 给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列&#xff0c;例如&#xff0c;当k3时&#xff0c;这个序列是&#xff1a; 1&#xff0c;3&#xff0c;4&#xff0c;9&#xff0c;10&#xff0c;12&#xff0c;13&a…