JVM 中的垃圾回收策略

news2024/11/26 2:41:47

文章目录

  • JVM 中的垃圾回收策略
    • 死亡对象的判断算法
      • 引用计数
      • 可达性分析
    • 垃圾回收算法
      • 标记-清除算法
      • 复制算法
      • 标记-整理算法
      • 分代算法

JVM 中的垃圾回收策略

C 语言中,malloc 的内存必须 手动 free,否则容易出现内存泄漏(光申请内存,不释放,内存用完了,导致程序崩溃)。

JVM 的垃圾回收,GC,可以帮助程序员自动释放内存。GC 能够有效的减少内存泄漏出现的概率!

Java 运行时的各个内存区域,对于程序计数器、虚拟机栈、本地方法栈这三个区域来说,内存的分配和回收具有确定性,都是随着线程的销毁而销毁。元数据区/方法区中存放的类对象,很少会“卸载”。所以堆是 GC 的主要目标,堆中存放着 new 出来的实例对象,GC 就是以对象为单位进行内存释放的。

GC 中主要分成两个阶段:

  1. 寻找死亡对象。
  2. 释放死亡对象的内存。

死亡对象的判断算法

死亡对象的定义

一个对象,后续再也不使用了,就可以认为是死亡对象。

如果一个对象,没有引用指向它,此时这个对象一定无法再被使用,这个对象就被认为是死亡对象了。

但是一个对象,已经不再使用了,但是还有引用指向它,这个对象也不能被认为是死亡对象。所以 Java 对于死亡对象的识别是比较保守的,避免了误判。

那么 Java 如何知道一个对象是否有引用指向呢?

  1. 引用计数
  2. 可达性分析

引用计数

给对象安排一个额外的空间,保存一个整数,表示该对象有几个引用指向。

缺陷:

  1. 浪费内存空间,需要额外的内存空间来计数。

  2. 循环引用的情况下,会导致引用计数的判定逻辑出错。

缺陷举例:

-- 伪代码
class Test{
	public Test n;
}

Test a = new Test();
Test b = new Test();
a.n = b;
b.n = a;

在这里插入图片描述

说明: 可以看到,此时一个 a 引用的 Test 对象,被 a 引用的 Test 对象的成员变量 n 引用。b 引用的 Test 对象,被 a 引用的 Test 对象的成员变量 n 引用。这就构成了循环引用。此时,两个 Test 对象都有两个引用指向。

在这里插入图片描述

说明: 当 a 和 b 这两个局部变量销毁后,两个 Test 对象的引用计数各自减一,此时两个 Test 对象的引用计数都为 1,不能作为死亡对象,但是这两个对象已经无法使用了。


可达性分析

这是 JVM 采用的方案。

把对象之间的引用关系,理解成一个树型结构。从一些称为 GC Roots 的对象作为起点出发,进行遍历。

只要能遍历访问到的对象,就是“可达”。不能遍历到的对象,就是“不可达”,就是死亡对象。

举例:

class Node {
	int val;
	Node left;
	Node right;
    
    public static Node createTree() {
        Node a = new Node();
        Node b = new Node();
        Node c = new Node();
        a.left = b;
        a.right = c;
        // ......
	}
    
    public static void main(String[] args) {
        Node root = createTree();
    }
}

说明: 假设执行了上述代码后,生成了下图那样的一颗二叉树。

root 是一个局部变量,root 引用了 a 对象,a 对象就是一个 GC Roots。

此时,从 a 对象这个起点开始遍历,二叉树上每个节点都能遍历到,所以每个节点都是可达的。

在这里插入图片描述

a.right = null;

说明: 当执行了这个代码后,就不能遍历到 c、e、f这三个节点,这三个节点就变为不可达,这三个节点就是死亡对象了。

在这里插入图片描述

当 root 这个局部变量销毁后,就找不到 a 节点了,那么这整个二叉树上的节点都是死亡对象了。

Java 中,可作为 GC Roots 的对象有以下几种:

  1. 栈中的局部变量引用的对象。
  2. 方法区中的常量引用的对象。
  3. 方法区中的类静态属性引用的对象。

可达性分析,就是从所有的 GC Roots 的起点出发,进行遍历,将遍历到的所有对象标记为 ”可达“,剩下的就是“不可达”,就是死亡对象了。

缺陷:

  1. 消耗更多的时间。遍历需要时间,因此某个对象成为死亡对象,也不一定能及时发现。
  2. STW(stop the world) 问题。在进行可达性分析的过程中,对象中的引用关系发生了变化,就比较麻烦了,所以为了判断的准确性,需要让其他的业务线程暂停工作

垃圾回收算法

标记-清除算法

分为标记和清除两个阶段:

  1. 标记所有需要回收的对象。
  2. 将标记的对象进行统一回收。

缺陷:

内存碎片。这个算法会产生大量不连续的内存碎片,这可能导致后续分配内存时,找不到一块连续的较大的内存空间。

在这里插入图片描述


复制算法

把整个内存空间分成两半,一次只用一半。

垃圾回收时,将存活对象,拷贝到另一半内存中,然后再统一回收。

这个算法解决了内存碎片的问题,但是也有缺点。

缺陷:

  1. 内存利用率低
  2. 如果死亡对象较少,大部分都是存活对象,那么复制的成本就比较高。

在这里插入图片描述


标记-整理算法

将所有存活对象向一端移动,然后再统一回收后面一段内存。

这个算法也能解决内存碎片问题,但是搬运开销比较大。

在这里插入图片描述


分代算法

JVM 采用的算法。

这个算法结合上述三种算法,针对不同的情况,使用不同的回收策略。

根据对象的存活周期的不同将内存分为几块区域。一般将内存划分为新生代区域和老年代区域。

关于对象的存活周期:每经过一次垃圾回收,没有被回收的对象,存活周期都会加一。

在新生代区域,每次垃圾回收都有大量的对象死去,只有少量对象存活,因此使用复制算法。在老年代区域,对象的存活率高,每次垃圾回收只有少量对象死去,因此没有额外的空间进行复制算法,那么就必须采用标记-整理算法。

经验规律:对于存活周期长的对象,这些对象大概率会继续存活。

分代算法:

  1. 新创建的对象,存放在伊甸区

    在伊甸区中,大部分对象在第一轮 GC 中就被回收了。少量经过一轮 GC 没被回收的对象,会被拷贝到生存区。

  2. 经过伊甸区一轮 GC 没被回收的对象,存放在生存区。

    生存区使用复制算法,将整个生存区分为两半。经过多次 GC 后,生存周期到达一定程度的对象,会被拷贝到老年代区域。

  3. 生产周期到达一定程度的对象,存放在老年代区域

    在老年代区域中的对象,生产周期都挺长,消亡的概率较小,因此针对老年代区域的 GC 扫描频率就会降低很多。每次 GC,这个区域的对象大部分存活,少部分消亡,因此可能没有足够的空间使用复制算法,所以采用标记-整理算法

特殊情况: 如果对象非常大,那么直接放在老年代区域,因为大对象进行复制算法,成本比较高,而且大对象也不会很多。


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

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

相关文章

Prometheus监控Tongweb容器

🏅概述 JMX Exporter主要是利用Java的JMX机制来读取JVM运行时的一些数据,然后转化为Prometheus可读取的metrics格式的数据。 JMX Exporter有两种用法: 启动独立进程。通过RMI读取JVM数据,但是单独进程监控也存在问题。JVM进程内启…

WSL 更新NVIDIA 驱动 安装CUDA

WSL 一定要使用WSL2,我选择的linux系统是ubuntu22.04,在微软应用商店安装的。 安装完成之后可以通过 wsl -l -v查看 NVIDIA 驱动 WSL 中不要直接安装linux版的显卡驱动,而是需要在windows中安装驱动。可以参考这篇文章NVIDIA官网文章 得保…

Spring Cache (基础知识+瑞吉外卖项目)

Spring Cache 基本介绍 Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。 Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓…

机器学习实践(2.1)LightGBM分类任务

前言 LightGBM也属于Boosting集成学习模型(还有前面文章的XGBoost),LightGBM和XGBoost同为机器学习的集大成者。相比越来越流行的深度神经网络,LightGBM和XGBoost能更好的处理表格数据,并具有更强的可解释性,还具有易于调参、输入…

Java 多线程编程在 JMeter 中应用

目录 前言: 首先新建一个简单的线程组和一个简单的请求: 添加 JSR223 预处理程序(后置处理程序需要下一次次请求) 可以通过复制私有变量来控制所有线程获取公共变量时的线程安全问题。 日志输出: 前言&#xff1a…

Excel VLOOKUP使用详解

VLOOKUP语法格式: VLOOKUP(lookup_value,table_array,col_index_num,range_lookup) VLOOKUP(要查找的值,查找区域,要返回的结果在查找区域的第几列,精确匹配或近似匹配) 一、精确查找 根据姓名查找对应…

Jmeter之数据参数化方法汇总

一、什么是参数化 Jmeter参数化是指将脚本中的某些需要输入数据使用参数来代替,在脚本运行时指定参数的取值范围和规则。脚本在运行时就可以根据需要选取不同的参数值作为输入。 常用的Jmeter的参数化请求,包括以下几种方式: 用户参数用户…

Seata分布式事务失效场景

场景: Feign熔断降级;被调用服务全局异常捕获了; 解决方案: 将需要分布式事务的调用链异常一直往上抛出去; 参考:https://blog.csdn.net/qq_16468489/article/details/121798016手动开启全局事务并进行回…

观察者(Observer)模式

目录 使用场景参与者协作效果实现类图 观察者(Observer)又被称为 发布-订阅模式,是一种对象行为模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时所有依赖于它的对象都得到通知并自动更新。通知的发布者(目标&a…

校园跑腿小程序都包括哪些业务呢?

校园创业,大家都知道,一直以来是个非常火热的话题,每位步入大学校园的学子们都有过在校创业的想法,包括我们一些有着学校资源的社会创业者们,早已经看好了中国高校这块巨大的市场。 在这里,我要跟大家分享…

面向对象进阶二(包、final、权限修饰符、代码块、abstract、接口、内部类)

面向对象进阶二 一、包、final、权限修饰符、代码块1.1 包1.2 final关键字1.3 权限修饰符1.4 代码块1.4.1 局部代码块(已淘汰,了解)1.4.2 构造代码块(了解内容)1.4.3 静态代码块 二、抽象方法和抽象类三、接口3.1 接口…

解释 void 类型转换: (void)++y

谢谢大师们的指导,保存,分享

【LeetCode】HOT 100(23)

题单介绍: 精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

JS语法学习实战系列

JS学习实战系列 一、数据类型二、运算符三、流程控制四、let 、var、 const 声明变量的区别 JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名,但是它…

电路维修(双端队列bfs)

题意:从左上角走到右下角,且只能走斜线,斜线可以旋转,旋转斜线那么走过这条线,那么距离就加1,求最小的距离。 分析:有一个性质,因为起点是偶数点(0,0),那么它只能走到偶数点&#x…

进程间通信-有名管道

理解&#xff1a;有名管道类似于文件io 相当于新建一个管道文件作为中间介质 进行数据的交换&#xff08;因此可以不同线程间通信&#xff09; 写入管道文件 #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #…

菜鸡shader:L9 屏幕UV及屏幕UV扰动

文章目录 屏幕UV代码最后效果 屏幕UV扰动代码最后效果 屏幕UV 最主要的代码是屏幕UV的获取&#xff1a; 这里具体可以参考这位大佬的博客&#xff1a; 个人学习笔记——庄懂的技术美术入门课&#xff08;美术向&#xff09;17&#xff08;VS空间畸变矫正的原理&#xff09;个…

驱动程序设计 Linux设备驱动中的阻塞与非阻塞IO、信号与函数 7.12

linux设备驱动中的阻塞与非阻塞I/O 阻塞&#xff1a;等待某种资源&#xff0c;如果获取不到&#xff0c;就会把当前任务挂起&#xff0c;等待资源准备好 1. int flag 0; while(flag 0); 2.等待队列 wait_event(q,flag > 0); wakeup(q);非阻塞&#xff1a;等待某种资源非…

nodejs中使用Redis

安装教程&#xff1a;Redis 安装 | 菜鸟教程 redis下载链接&#xff1a;Release Redis for Windows 5.0.14.1 tporadowski/redis GitHub解压双击redis-server.exe启动服务器端双击redis-cil.exe启动客户端连接服务端在客户端看看能不能ping通 完成 遇到的问题1&#x…

前端处理后端返回的文件流,进行文件下载

二进制流格式 Blob格式 前言&#xff1a; 需求&#xff1a;根据后端接口返回的文件流进行数据处理&#xff0c;并实现文件的下载&#xff0c;且下载文件为word文档. 代码实现&#xff1a; //下载文件 async function DownLoadFile(row) {let res await DownLoadFileAPI(row.…