【JVM】垃圾回收理论

news2025/2/3 13:05:57

一、关于回收目标
在前面我们已经了解到,JVM的内存模型划分为多个区域,由于不同区域的实现机制以及功能不同,那么各自的回收目标也不同。一般来说,内存回收主要涉及以下三个区域:
虚拟机栈/本地方法栈:顾名思义,该部分内存以栈的形式作为实现,那么在进栈、出栈的时候内存会自动释放,类似于C的“自动变量区域内存”;
堆:内存回收主要目标,可以认为类似于C中的“动态内存分配区域”,只不过C通过malloc与free函数手动进行管理,而java通过GC进行自动管理;
方法区:该区域回收效果很弱,虚拟机规范强制要求在这里进行回收。回收目标是常量池的回收和对类型的卸载;

二、方法区回收
方法区的回收目标是回收常量池中的废弃常量与类卸载。
2.1.常量回收
若常量池中的某常量没有任何地方引用或者使用,包括该常量不以字面量的形式被使用或引用,则可以被回收。
2.2.类卸载
满足以下条件的类可以被卸载:
该类所有实例已被回收;
该类的ClassLoader已被回收;
该类的类型信息,即java.lang.Class没有任何地方引用(一般为反射使用);
可以看出,对于类的卸载,要求很苛刻。因此在大量使用反射、动态代理、CGLib等字节码框架、动态生成jsp以及OSGI这类频繁自定义ClassLoader功能的场景中,都要求JVM具备类卸载功能,以保证永久带不溢出。

三、堆回收
3.1.对象存活判定
关于堆中的对象存活判定,以标记为基础,并配合其他步骤完成。
3.1.1.标记算法
(1)引用计数法
即给对象添加一个引用计数器,每有一个地方进行引用,则计数器加1。当计数器为0的时候,表示该对象可回收。
引用计数法未被JVM采用,原因是其无法解决对象间循环引用的问题,如下图所示,当堆内的两个对象循环引用,就算他们已经没用了,也无法进行回收:
在这里插入图片描述

(2)可达性分析算法
该算法的思想是将一系列被称为“GC ROOTS”的对象作为起点(或称根节点),向下搜索,所走过的路径称为“引用链(reference chain)”。若一个对象没有可以到达GC ROOTS的路径,则称“该对象不可达”。对于不可达对象,会被标记为回收状态。
在这里插入图片描述

上图中,顺着GC ROOTS,Obj1、Obj2、Obj3和Obj4都是可以到达的,因此他们为存活对象;而Obj5不可到达,Obj6、Obj7即使存在指向它们的引用,但因无法到达GC ROOTS,因此为需要回收的对象。
在可达性分析算法中,最重要的就是GC ROOTS。其本质是对象,但并非所有对象都有资格作为GC ROOTS,只有以下位置的才可以:
栈上引用:虚拟机栈的栈帧中本地变量表内引用的对象;
栈上引用:本地方法栈中JNI引用的对象;
方法区:类静态属性引用的对象;
方法区:类常量引用的对象;
3.1.2.死亡判定
对象在经过标记之后,并不会马上被回收,还要经过以下一系列阶段才最终确定需要被回收:
在这里插入图片描述

一次标记:即通过标记算法将对象标记为待回收状态,并进入一个待回收对象集合;
筛选:对一次标记之后的待回收对象进行过滤,如果该对象覆盖了finalize方法,并且该方法未执行过,则将该对象放入F-QUEUE;反之,对象没有覆盖finalize方法或者finalize方法已经被执行过了,该对象不会进行任何处理;
F-QUEUE:一个队列,JVM会通过一个Finalizer线程去执行这个队列中对象的finalize方法,并且只保证该方法的执行,不保证该方法成功执行完成。因为若finalize方法有死循环,会造成FQUEUE后续未被执行对象的持续等待,导致整个内存回收系统崩溃。根据这个特点,对象可以在执行finalize方法时进行“自救”,所谓的自救,就是将对象重新与GC ROOTS相关联;
二次标记:GC会对FQUEUE中的对象进行额外的一次标记,若对象“自救”成功,则会从待回收对象集合中移除;若对象“自救”失败,它仍然会处于待回收对象集合中,等待真正被回收;
回收:对象通过垃圾收集进行回收,释放内存空间;
3.2.垃圾收集算法
在上一小节我们讲了对象标记相关的算法,本小节来了解一下垃圾收集算法。
3.2.1.标记-清除算法
标记-清除(mark-sweep)算法,是最基础的垃圾收集算法,它的思想比较简单,就是在“对象存活判定”标记出需要回收的对象后,统一回收(清除)这些对象的内存。
在这里插入图片描述

该算法简单有效,但是存在两个不足:首先是效率问题,标记和清除两个阶段的效率都不高,所谓效率不高,并非指的是自身的执行效率,而是指回收结果与耗时的效益比不高;其次是空间问题,标记-清除算法并未整理内存,会产生大量不连续的内存碎片,要分配较大对象时,可能无法找到足够的连续内存而不得不又触发一次GC。
3.2.2.复制算法
复制算法(copying)是对标记-清除算法的改进,其主要思想是将内存划分为不同的区域,包括“内存使用区”和“结果缓冲区”。每次只使用一部分内存,在该部分内存满了之后,将仍然存活的对象复制到另外一块区域上面,然后将之前使用过的内存区域全部清理掉,现代商业虚拟机都采用其回收新生代。
该算法大大提高了回收效率,也可以避免内存碎片。然而带来了新的问题:由于需要开辟一块内存空间作为每次回收结果的缓冲,因此可用内存无法达到100%,“结果缓冲区”的大小决定了内存有效的比率。
如何设置结果缓冲区的内存大小(比例)?将其设置为50%最能确保每次回收都有足够大小的缓冲区域存放回收结果,毕竟最差的情况就是所有对象都存活,然而内存浪费也太高了。根据IBM的研究,一般情况下,新生代中的对象98%都是“朝生夕死”的,也就是说,每次存活对象的比例并不会太高,我们只需要设置一小块内存作为“回收结果缓冲”即可,他们提出的解决模型如下,将内存划分为eden与2块suvivor:
该算法简单有效,但是存在两个不足:首先是效率问题,标记和清除两个阶段的效率都不高,所谓效率不高,并非指的是自身的执行效率,而是指回收结果与耗时的效益比不高;其次是空间问题,标记-清除算法并未整理内存,会产生大量不连续的内存碎片,要分配较大对象时,可能无法找到足够的连续内存而不得不又触发一次GC。
3.2.2.复制算法
复制算法(copying)是对标记-清除算法的改进,其主要思想是将内存划分为不同的区域,包括“内存使用区”和“结果缓冲区”。每次只使用一部分内存,在该部分内存满了之后,将仍然存活的对象复制到另外一块区域上面,然后将之前使用过的内存区域全部清理掉,现代商业虚拟机都采用其回收新生代。
该算法大大提高了回收效率,也可以避免内存碎片。然而带来了新的问题:由于需要开辟一块内存空间作为每次回收结果的缓冲,因此可用内存无法达到100%,“结果缓冲区”的大小决定了内存有效的比率。
如何设置结果缓冲区的内存大小(比例)?将其设置为50%最能确保每次回收都有足够大小的缓冲区域存放回收结果,毕竟最差的情况就是所有对象都存活,然而内存浪费也太高了。根据IBM的研究,一般情况下,新生代中的对象98%都是“朝生夕死”的,也就是说,每次存活对象的比例并不会太高,我们只需要设置一小块内存作为“回收结果缓冲”即可,他们提出的解决模型如下,将内存划分为eden与2块suvivor:
在这里插入图片描述

eden:主存储区,新对象的创建都在这块区域;
survivor:分为两块,一块作为上次回收结果的“缓存”,一块作为下一次回收的“缓存”区域;
基于这种模型,每次回收时,将eden和上次回收结果的survivor中存活的对象复制进空闲的survivor,然后清理掉被回收的区域即可,简单的示意流程图见下:
在这里插入图片描述

值得注意的是,对于eden-survivor模型,98%的对象可回收只是理想理论,在某些场景下,回收时存活对象的大小有可能大于空闲survivor。对于这种survivor空间大小不够用的情况,需要通过“分配担保”机制来保证对象能正确留存。所谓的分配担保,就是不够空间survivor存放的对象进入老年代。
3.2.3.标记-整理算法
在上一小节我们知道复制算法主要适合于新生代的回收,对于老年代这种对象存活率高的区域,因为每次都会复制大量对象,成本收益比较低,使用复制算法明显不合适;相反,标记-清除算法更适合老年代的特征,为了解决标记-清除算法的内存碎片问题,在此基础上,优化为标记-整理算法(mark-compact)。
标记-整理算法主要思想是在标记对象后,将存活对象向内存的一端移动,然后清理掉端边界以外的内存,所谓的整理也可以理解为压缩。
在这里插入图片描述

3.2.4.总结
没有哪一种垃圾收集算法能够适用于所有情况,对于不同的堆内存区域(新生代、老年代),需要根据实际的对象特征,选择合适的算法。
算法优点缺点适用区域复制效率较高,无内存碎片问题1.内存利用率达不到100%;2.需要分配担保机制确保对象存活率较高时的内存分配;新生代(对象存活率低,复制成本低)标记-清除简单有效1.效率不高;2.有内存碎片问题;老年代(对象存活率高,无额外空间进行分配担保)标记-整理标记-清除的改良,解决了内存碎片问题1.同样存在效率问题;2.整理过程需要额外的时间开销;

在这里插入图片描述

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

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

相关文章

Markdown之高频语法介绍(二十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

Ionic实战二十七:移动端录音方案及Nginx部署配置

文章目录 1.最终效果预览2.实现思路说明3.移动端外壳集成iframe4.视频页nginx配置5.组态页iframe集成6.组态页Nginx配置7.Nginx启动及关闭8.H5页面录音1.最终效果预览 2.实现思路说明 摄像头对接的海康的或者大华,将设备集成到青柿视频平台中,这样视频的播放用video标签即可…

promise的使用和实例方法

前言 异步,是任何编程都无法回避的话题。在promise出现之前,js中也有处理异步的方案,不过还没有专门的api能去处理链式的异步操作。所以,当大量的异步任务逐个执行,就变成了传说中的回调地狱。 function asyncFn(fn1, fn2, fn3) {setTimeout(() > {//处理第一个异步任务fn1…

12.26_黑马数据结构与算法笔记Java

目录 243 图 Floyd Warshall 算法实现2 244 图 Floyd Warshall 算法实现3 245 图 Floyd Warshall 算法实现4 246 图 最小生成树 Prim 247 图 最小生成树 Kruskal 248 图 并查集 1 249 图 并查集 2 250 图 并查集 路径压缩 251 图 并查集 UnionBySize 252 贪心算法 介绍…

vue3开发一个todo List

创建新的 Vue 3 项目: 按装vue3的 工具 npm install -g vue/cli创建一个新的 Vue 3 项目: vue create vue3-todolist进入项目目录: cd vue3-todolist代码: 在项目的 src/components 目录下,创建一个新的文件 Todo…

蓝桥杯c/c++程序设计——冶炼金属

冶炼金属 问题描述 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V,V 是一个正整数,这意味着消耗 V 个普通金属 O 恰好可以冶炼出一个特殊金属 X,当普通金属 O 的数目不足 V 时&#xff0…

基于yolov8,制作停车位计数器(附源码)

大家好,YOLO(You Only Look Once) 是由Joseph Redmon和Ali开发的一种对象检测和图像分割模型。 YOLO的第一个版本于2015年发布,由于其高速度和准确性,瞬间得到了广大AI爱好者的喜爱。 Ultralytics YOLOv8则是一款前沿、最先进(SOTA)的模型&a…

随记-语义分割

Semantic Segmentation 什么是语义分割全卷积网络FCN摘要 什么是语义分割 语义分割 Semantic Segmentation 旨在对图像的每个像素进行分类,将其分配给预定义的语义类别。 (检测图像中的物体并按属性分类) 实例分割 Instance Segmentation 实…

Vue框架引入Element-Ui

首先已经创建好了 Vue 框架,安装好了 node.js。 没有完成的可按照此博客搭建:搭建Vue项目 之后打开终端,使用命令。 1、命令引入 npm i element-ui -S2、package.json 查看版本 在 package.json 文件里可查看下载好的依赖版本。 3、在 ma…

按照不同产品类型,划片机主要可以分为如下几个类别

随着科技的不断发展,划片机在半导体封装行业中的应用越来越广泛。根据不同的产品类型,划片机主要可以分为砂轮划片机和激光划片机两个类别。本文将详细介绍这两类划片机的特点和应用。 一、砂轮划片机 砂轮划片机是综合了水气电、空气静压高速主轴、精密…

【Vulnhub 靶场】【Funbox: Scriptkiddie】【非常简单】【20210720】

1、环境介绍 靶场介绍:https://www.vulnhub.com/entry/funbox-scriptkiddie,725/ 靶场下载:https://download.vulnhub.com/funbox/Funbox11.ova 靶场难度:简单 发布日期:2021年07月20日 文件大小:1.3 GB 靶场作者&…

Windows无法安装edge 无法连接Internet

如果出现以上问题,或者Edge浏览器无法更新,提示防火墙错误之类的都可以解决问题。 下载以下证书文件并导入即可解决问题。 MicrosoftRootCertificateAuthority2011.cer

《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识(4)

接前一篇文章:《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识(3) 1.1 PCI总线的组成 PCI总线作为处理器系统的本地总线,是处理器系统的一个组成部件。因此,讲述PCI总线的组成结构,不…

DataFunSummit:2023年数据湖架构峰会-核心PPT资料下载

一、峰会简介 现今,很多企业每天都有PB级的数据注入到大数据平台,经过离线或实时的ETL建模后,提供给下游的分析、推荐及预测等场景使用。面对如此大规模的数据,无论是分析型场景、流批一体、增量数仓都得益于湖仓一体等数据湖技术…

RocketMQ文件准备

1、RocketMQ下载 下载地址:下载 | RocketMQ Source下载与Binary下载区别: binary是编译好的可以直接使用,source是还没编译过的源代码,需要自行编译。 这里大家自行下载需要的版本 2、RocketMQ管理界面 因为Rocket没有图形化管理…

渗透测试 | 信息收集常用方法总结

目录 一、关于域名 1.子域名收集 a.搜索引擎查找 b.在线查询 c.工具 d.SSL/TLS证书查询 2.端口型站点收集 3.目录文件扫描 a.目录扫描工具 b.github搜索 c.google搜索 d.在线网站 e.文件接口工具 4.旁站和C段 a.旁站查询 b.C段查询 5.网站技术架构信息 a.基础…

重磅!这本SCI期刊已解除「On hold」,另有Top期刊仍被调查中

近期小编在Master Journal List上查询期刊时偶然发现,此前被标记为「On Hold」的SCI期刊Biomass Conversion and Biorefinery,已经被科睿唯安取消了「On Hold」标识! 查询网址:https://mjl.clarivate.com/home 此前期刊处于「On …

【Petalinux】制作SD卡 操作系统 启动

Vivado 添加 SD0 导出hdf 制作SD卡 https://mathd.blog.csdn.net/article/details/135217761 【Petalinux】下为空白SD卡建立BOOT,rootfs分区 Petalinux 生成 Petalinux 框架 petalinux-create --type project --template zynq --name sdtest进入 sdtest 文件…

Python3.12 新版本之f-string的几个新特性

目录 概述 f-string表达式 1. 支持转义字符 2. 支持多行字符串 3. 支持重复使用的引号种类 4. 支持无限嵌套 附录 Python3.12新语法特性 概述 Python 3.12在10月2日发布,新版本包含一系列对语言和标准库的改变。 库的改变主要集中在清理已弃用的 API、可…

cJSON简析

文章目录 json概要cJSON数据结构 递归解析示例references json概要 json是一种文本格式的协议 对于人的可阅读性非常好 其中object和array中的value都可以嵌套 cJSON数据结构 每个节点的数据结构如下 /* cJSON Types: */ #define cJSON_Invalid (0) #define cJSON_False (…