JVM面试题解,垃圾回收之“分代回收理论”剖析

一、什么是分代回收

我们会把堆内存中的对象间隔一段时间做一次GC(即垃圾回收),但是堆内存很大一块,内存布局分为新生代和老年代、其对象的特点不一样,所以回收的策略也应该各不相同

对于“刚出生”的新对象,很多时候生命周期并不长,有的只是临时用一下,需要短期被GC的概率很高,多次GC周期后都留存下来的对象就是很难被回收的对象。根据这两类对象的特点,我们就分为新生代和老年代,并且采取不同的回收算法。

二、分代回收算法有哪些

复制算法

原始的复制算法(Copying)是这样的:

  • 1、将内存按容量划分为大小相等的两块,每次只使用其中的一块。
  • 2、当其中一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

带来的好处是:

  • 1、实现简单,运行高效,
  • 2、每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要按顺序分配内存即可

存在的弊端是:

  • 1、内存的使用率缩小为原来的一半。
  • 2、内存移动是必须实打实的移动(复制),所以对应的引用(直接指针)需要调整。

适用场景:
复制回收算法适合于新生代,因为大部分对象朝生夕死,那么复制过去的对象比较少,效率自然就高,另外一半的一次性清理是很快的。

但是像 hotspot 这样的虚拟机大都对原生的复制算法进行了改进,因为它对内存空间的利用率不高,而且专门研究表明,新生代中的对象 98% 是“朝生夕死”的,所以并不需要按照 1:1 的比例来划分内存空间,所以改进后的复制回收策略叫做: Appel 式回收。

  • 1、将新生代划分为一块较大的 Eden 区和两块较小的 Survivor 空间(你可以叫做 From 或者 To ) , HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1
  • 2、每次使用 Eden 和其中一块 Survivor ,当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。

在这样的算法下

  • 1、每次新生代中可用内存空间为整个新生代容量的 90%(80%+10%),只有 10%的内存会被 “浪费”
  • 2、当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于 10%的对象存活,当 Survivor 空间不够用时,需要依赖其他内存(老年代)进行分配担保( Handle Promotion )。

 标记-清除算法  

标记-清除(Mark-Sweep)算法分为“标记”和“清除”两个阶段:

  • 1、首先扫描所有对象,标记出需要回收的对象,
  • 2、在标记完成后扫描并回收所有被标记的对象,故需要两次扫描

该算法特点如下:

  • 1、回收效率略低,如果大部分对象是朝生夕死,那么回收效率降低,因为需要大量标记对象和回收对象,对比复制回收效率要低,所以该算法不适合新生代。
  • 2、它的主要问题是在标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。
  • 3、标记清除算法适用于老年代。 

标记-整理算法

算法逻辑如下:

  • 1、首先标记出所有需要回收的对象,
  • 2、在标记完成后,后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,
  • 3、然后直接清理掉端边界以外的内存。

该算法特点:

  • 1、标记整理需要扫描两遍
  • 2、标记整理与标记清除算法的区别主要在于对象的移动。对象移动不单单会加重系统负担,同时需要全程暂停用户线程才能进行,同时所有引用对象的地方都需要更新(直接指针需要调整)。
  • 3、标记整理算法不会产生内存碎片,但是效率偏低。
  • 4、标记整理算法适用于老年代。

因此,老年代采用的标记整理算法与标记清除算法,各有优点,各有缺点。

三、什么是跨代引用,如何解决?

什么是跨代引用

        分代回收也并不是简单划分一下内存区域这么简单,因为对象不是孤立的,对象之间存在跨代引用,譬如:现在要在新生代进行回收,但新生代的对象极有可能被老年代对象所引用,那为了找到这些可能存活的对象,不得不在既定的 GC Roots 之外,再遍历整个老年代对象确保可达性分析结果的正确性。反过来回收老年代也是一样。这样无疑带来了性能负担。

如何解决跨代引用

跨代引用相对于同代引用来说仅仅占少数,正是因为只占少数,所以不应该为了为了这些少量的跨代引用而区扫描整个老年代,也不能浪费空间让每个对象都记录它是否存在跨代引用。

所以为了解决这个问题只需要在新生代建立一个全局的数据结构叫做:记忆集( Remembered Set ),这个结构把老年代划分成若干小块内存,并标识哪块内存存在跨代引用,后续新生代发生 gc 时,只有包含了跨代引用的小内存区域才会被加入到 GC Roots 进行扫描;

当然这种方法需要在对象改变引用关系的时候维护记忆集中数据的正确性。这种做法相比垃圾收集时扫描整个老年代来说仍然是划算的

四、记忆集Remmber Set有了解过吗?卡表是什么

前面讲到,为了解决跨代引用带来的问题,垃圾收集器在新生代建立了一个叫做:记忆集的数据结构存储老年代哪些区域存在跨代引用,以便在根节点扫描时将这些老年代区域加入 GC Roots 的扫描范围,这样避免将整个老年代都加入 GC Roots 的扫描范围。

当然跨代引用的问题并非只在回收新生代才有,回收老年代也是一样的,所以需要更进一步理解记忆集的原理和实现方式。

记忆集定义:是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。

记忆集的实现:最常见的实现方式是通过卡表( Card Table )的方式去实现,卡表最简单的形式是一个字节数组( hotspot ),如下:

CARD_TABLE[this address >> 9 ] = 0
  • 1、字节数组 CARD_TABLE 的每一个元素都对应着其标识的内存区域中一块特定大小的内存块,这个内存块被称作:卡页( Card Page ),卡页大小一般是2的N次幂, hotspot 中是2的9次幂(地址右移9位),即512字节。
  • 2、如果卡表标识的起始地址是:0x0000,那数组的0,1,2号元素,分别对应的地址范围是:0x0000~ox01ff,0x0200~0x03ff,0x0400~0x05ff
  • 3、一个卡页的内存中通常包含不止一个对象,只要卡页内存中有一个或多个对象的字段存在跨代引用指针,那就将卡表对应字节数组元素的值标识位1,称之为 Ditry ,没有则标识位0,垃圾收集器工作时只要筛查 CARD_TABLE 中为1的元素,就能轻易找到哪些卡页内存块中包含跨代引用,就把这些内存块加入到 GC Roots 的扫描范围内。

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

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

相关文章

电脑如何访问手机文件?

手机和电脑已经深深融入了我们的日常生活,无时无刻不在为我们提供服务。除了电脑远程操控电脑外,我们还可以在电脑上轻松地访问Android或iPhone手机上的文件。那么,如何使用电脑远程访问手机上的文件呢? 如何使用电脑访问手机文件…

ThinkPHP 8模型与数据的插入、更新、删除

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《2025新书 ThinkPHP 8高效构建Web应用 编程与应用开发丛书 夏磊 清华大学出版社教材书籍 9787302678236 ThinkPHP 8高效构建Web应用》【摘要 书评 试读】- 京东图书 使用VS Code开发ThinkPHP项目-CSDN博客 编程与应用开…

【MySQL】数据库基础知识

欢迎拜访:雾里看山-CSDN博客 本篇主题:【MySQL】数据库基础知识 发布时间:2025.1.21 隶属专栏:MySQL 目录 什么是数据库为什么要有数据库数据库的概念 主流数据库mysql的安装mysql登录使用一下mysql显示数据库内容创建一个数据库创…

【线性代数】基础版本的高斯消元法

[精确算法] 高斯消元法求线性方程组 线性方程组 考虑线性方程组, 已知 A ∈ R n , n , b ∈ R n A\in \mathbb{R}^{n,n},b\in \mathbb{R}^n A∈Rn,n,b∈Rn, 求未知 x ∈ R n x\in \mathbb{R}^n x∈Rn A 1 , 1 x 1 A 1 , 2 x 2 ⋯ A 1 , n x n b 1…

高等数学学习笔记 ☞ 微分方程

1. 微分方程的基本概念 1. 微分方程的基本概念: (1)微分方程:含有未知函数及其导数或微分的方程。 举例说明微分方程:;。 (2)微分方程的阶:指微分方程中未知函数的导数…

HarmonyOS基于ArkTS卡片服务

卡片服务 前言 Form Kit(卡片开发框架)提供了一种在桌面、锁屏等系统入口嵌入显示应用信息的开发框架和API,可以将应用内用户关注的重要信息或常用操作抽取到服务卡片(以下简称“卡片”)上,通过将卡片添加…

Java复习第四天

一、代码题 1.相同的树 (1)题目 给你两棵二叉树的根节点p和q,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 示例 1: 输入:p[1,2,3],q[1,2,3] 输出:true示例 2: 输…

全面了解 Web3 AIGC 和 AI Agent 的创新先锋 MelodAI

不管是在传统领域还是 Crypto,AI 都是公认的最有前景的赛道。随着数字内容需求的爆炸式增长和技术的快速迭代,Web3 AIGC(AI生成内容)和 AI Agent(人工智能代理)正成为两大关键赛道。 AIGC 通过 AI 技术生成…

新能源汽车充电桩选型以及安装应用

摘要:随着当前经济的不断发展,国家的科技也有了飞速的进步,传统的燃油汽车已经不能适应当前社会的发展,不仅对能源造成巨大的消耗,还对环境造成了污染,当前一种新型的交通运输工具正在占领汽车市场。在环境问题和能源问题愈发严重的当今社会,节能减排已经成为全世界的共同课题,…

一个vue项目npm install失败的问题解决方案

vue的项目一直是史上最难的最烦的问题,今天给别人做毕设单子想在gitee上拉项目二开的时候,由于很久没写过vue项目已经生疏了,在拿到项目之后我还是例行完成最常见的步骤: 1、npm init -y 初始化 2、npm install 用npm把这个项目…

计算机网络 (55)流失存储音频/视频

一、定义与特点 定义:流式存储音频/视频是指经过压缩并存储在服务器上的多媒体文件,客户端可以通过互联网边下载边播放这些文件,也称为音频/视频点播。 特点: 边下载边播放:用户无需等待整个文件下载完成即可开始播放…

UE求职Demo开发日志#6 测试用强化页面UI搭建

1 反向实现思路设计 先看最终效果: 先做了一个大致的分区,右侧的上半部分用来显示数据,下半部分用来强化和显示需要的材料,至于这个背景设定上强化应该叫什么,。。。。,还没定,反正应该不叫强…

python学opencv|读取图像(四十一 )使用cv2.add()函数实现各个像素点BGR叠加

【1】引言 前序已经学习了直接在画布上使用掩模,会获得彩色图像的多种叠加效果,相关文章链接为: python学opencv|读取图像(四十)掩模:三通道图像的局部覆盖-CSDN博客 这时候如果更进一步,直接…

SpringCloudAlibaba 服务保护 Sentinel 项目集成实践

目录 一、简介1.1、服务保护的基本概念1.1.1、服务限流/熔断1.1.2、服务降级1.1.3、服务的雪崩效应1.1.4、服务的隔离的机制 1.2、Sentinel的主要特性1.3、Sentinel整体架构1.4、Sentinel 与 Hystrix 对比 二、Sentinel控制台部署3.1、版本选择和适配3.2、本文使用各组件版本3.…

窥探QCC518x-308x系列与手机之间的蓝牙HCI记录与分析 - 耳机篇

上一篇是介绍如何窥探手机端Bluetooth的HCI log, 本次介绍是如何窥探Bluetooth的HCI log-耳机篇. 这次跟QCC518x/QCC308x测试的手机是Samsung S23 Ultra. QCC518x/QCC308x透过HCI界面取得Log教学. 步骤1: 开启QMDE -> 选择ADK r1102 QCC3083 Headset workspace.步骤2: 点…

C++ list 容器用法

C list 容器用法 C 标准库提供了丰富的功能&#xff0c;其中 <list> 是一个非常重要的容器类&#xff0c;用于存储元素集合&#xff0c;支持双向迭代器。<list> 是 C 标准模板库&#xff08;STL&#xff09;中的一个序列容器&#xff0c;它允许在容器的任意位置快速…

SpringSecurity实现自定义用户认证方案

Spring Security 实现自定义用户认证方案可以根据具体需求和业务场景进行设计和实施&#xff0c;满足不同的安全需求和业务需求。这种灵活性使得认证机制能够更好地适应各种复杂的环境和变化‌。通过自定义认证方案&#xff0c;可以更好地控制和管理用户的访问权限&#xff0c;…

深入内核讲明白Android Binder【三】

深入内核讲明白Android Binder【三】 前言一、服务的获取过程内核源码解析1. 客户端获取服务的用户态源码回顾2. 客户端获取服务的内核源码分析2.1 客户端向service_manager发送数据1. binder_ioctl2. binder_ioctl_write_read3. binder_thread_write4. binder_transaction4.1 …

深入解析:Docker 容器如何实现文件系统与资源的多维隔离?

目录 一、RootFs1. Docker 镜像与文件系统层2. RootFs 与容器隔离的意义 二、Linux Namespace1. 进程命名空间1.1 lsns 命令说明1.2 查看“祖先进程”命名空间1.3 查看当前用户进程命名空间 2. 容器进程命名空间2.1 查看容器进程命名空间列表2.2 容器进程命名空间的具体体现 三…

解锁Java中的国密算法:安全保障的密钥

一、引言 在数字化浪潮席卷全球的当下&#xff0c;信息安全已然成为国家、企业乃至个人无法忽视的重要议题。国密算法&#xff0c;作为我国自主研发的密码算法体系&#xff0c;宛如坚固的盾牌&#xff0c;为国家信息安全筑起了一道坚不可摧的防线。它的诞生&#xff0c;不仅承载…