Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 垃圾回收算法

news2024/11/10 14:21:17

文章目录

    • 垃圾回收机制
    • 垃圾判断算法
      • 引用计数法
      • 可达性分析算法
        • 虚拟机栈中的引用(方法的参数、局部变量等)
        • 本地方法栈中 JNI 的引用
        • 类静态变量
        • 运行时常量池中的常量
    • 垃圾收集算法
      • Mark-Sweep(标记-清除)算法
      • Copying(标记-复制)算法
      • Mark-and-Compact(标记-整理)算法
      • Generation Collection (分代收集) 算法

垃圾回收机制

垃圾回收Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收

垃圾判断算法

引用计数法

给对象中添加一个引用计数器

  • 每当有一个地方引用它,计数器就加 1;
  • 当引用失效,计数器就减 1;
  • 任何时候计数器为 0 的对象就是不可能再被使用的。

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题。

![[Pasted image 20231011222409.png]]

可达性分析算法

该算法通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。

![[Pasted image 20231011222616.png]]

图片来源:JavaGuide

Object 6 ~ Object 10 之间虽有引用关系,但它们到 GC Roots 不可达,因此为需要被回收的对象

所谓对象之间的相互引用问题:除了对象 objAobjB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。

所谓的 GC Roots,就是一组必须活跃的引用,不是对象,它们是程序运行时的起点,是一切引用链的源头。在 Java 中,GC Roots 包括以下几种:

  • 虚拟机栈中的引用(方法的参数、局部变量等)
  • 本地方法栈中 JNI 的引用
  • 类静态变量
  • 运行时常量池中的常量(String 或 Class 类型)
虚拟机栈中的引用(方法的参数、局部变量等)
public class StackReference {
    public void greet() {
        Object localVar = new Object(); // 这里的 localVar 是一个局部变量,存在于虚拟机栈中
        System.out.println(localVar.toString());
    }

    public static void main(String[] args) {
        new StackReference().greet();
    }
}

greet 方法中,localVar 是一个局部变量,存在于虚拟机栈中,可以被认为是 GC Roots

greet 方法执行期间,localVar 引用的对象是活跃的,因为它是从 GC Roots 可达的。

greet 方法执行完毕后,localVar 的作用域结束,localVar 引用的 Object 对象不再由任何 GC Roots 引用(假设没有其他引用指向这个对象),因此它将有资格作为垃圾被回收掉。

本地方法栈中 JNI 的引用

Java 通过 JNI(Java Native Interface)提供了一种机制,允许 Java 代码调用本地代码(通常是 C 或 C++ 编写的代码)。

当调用 Java 方法时,虚拟机会创建一个栈帧并压入虚拟机栈,而当它调用本地方法时,虚拟机会通过动态链接直接调用指定的本地方法。

![[Pasted image 20240917212049.png]]

JNI 引用是在 Java 本地接口(JNI)代码中创建的引用,这些引用可以指向 Java 堆中的对象。

// 假设的JNI方法
public native void nativeMethod();

// 假设在C/C++中实现的本地方法
/*
 * Class:     NativeExample
 * Method:    nativeMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_NativeExample_nativeMethod(JNIEnv *env, jobject thisObj) {
    jobject localRef = (*env)->NewObject(env, ...); // 在本地方法栈中创建JNI引用
    // localRef 引用的Java对象在本地方法执行期间是活跃的
}

在本地(C/C++)代码中,localRef 是对 Java 对象的一个 JNI 引用,它在本地方法执行期间保持 Java 对象活跃,可以被认为是 GC Roots。

一旦 JNI 方法执行完毕,除非这个引用是全局的(Global Reference),否则它指向的对象将会被作为垃圾回收掉(假设没有其他地方再引用这个对象)。

类静态变量

来看下面这段代码:

public class StaticFieldReference {
    private static Object staticVar = new Object(); // 类静态变量

    public static void main(String[] args) {
        System.out.println(staticVar.toString());
    }
}

StaticFieldReference 类中的 staticVar 引用了一个 Object 对象,这个引用存储在元空间,可以被认为是 GC Roots

只要 StaticFieldReference 类未被卸载,staticVar 引用的对象都不会被垃圾回收。如果 StaticFieldReference 类被卸载(这通常发生在其类加载器被垃圾回收时),那么 staticVar 引用的对象也将有资格被垃圾回收(如果没有其他引用指向这个对象)。

运行时常量池中的常量
public class ConstantPoolReference {
    public static final String CONSTANT_STRING = "Hello, World"; // 常量,存在于运行时常量池中
    public static final Class<?> CONSTANT_CLASS = Object.class; // 类类型常量

    public static void main(String[] args) {
        System.out.println(CONSTANT_STRING);
        System.out.println(CONSTANT_CLASS.getName());
    }
}

ConstantPoolReference 中,CONSTANT_STRINGCONSTANT_CLASS 作为常量存储在运行时常量池。它们可以用来作为 GC Roots

这些常量引用的对象(字符串"Hello, World"和 Object.class 类对象)在常量池中,只要包含这些常量的 ConstantPoolReference 类未被卸载,这些对象就不会被垃圾回收。

垃圾收集算法

垃圾收集算法(Garbage Collection Algorithm) 是一种自动内存管理机制,用于在程序运行时自动识别和回收不再使用的对象,以释放内存空间和提升系统性能

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

![[Pasted image 20231011230810.png]]

图片来源:JavaGuide

标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象

它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:

  1. 效率问题:标记和清除两个过程效率都不高。
  2. 空间问题:标记清除后会产生大量不连续的内存碎片。

对于标记的对象是可回收还是不可回收对象,两种说法都有支持者,按照前者:

  1. 当一个对象被创建时,给一个标记位,假设为 0 (false);
  2. 在标记阶段,从根对象出发,将所有可达对象(或用户可以引用的对象)的标记位设置为 1 (true)
  3. 扫描阶段 清除标记位为 0 (false)的对象

Copying(标记-复制)算法

![[Pasted image 20231011231209.png]]

图片来源:JavaGuide

为了解决 标记-清除 算法的效率和内存碎片问题,复制(Copying)收集算法 将内存分为大小相同的两块,每次使用其中的一块

当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收

该算法存在的问题:

  • 可用内存变小:可用内存缩小为原来的一半。
  • 不适合老年代:如果存活对象数量比较大,复制性能会变得很差。

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

![[Pasted image 20231011231514.png]]

图片来源:JavaGuide

标记-整理(Mark-and-Compact)算法是根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存

由于多了整理这一步,因此效率不高,适合老年代这种垃圾回收频率不是很高的场景

Generation Collection (分代收集) 算法

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 Java 堆分为新生代和老年代,根据各个年代的特点选择合适的垃圾收集算法,通过优化垃圾收集的效率,提高了系统的性能。

比如在新生代中,每次 GC 都会有大量对象死去,可以选择 Copying 算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。

而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

Java 虚拟机(JVM)中的垃圾收集器通常会根据堆内存的大小、对象的存活时间等因素动态选择合适的垃圾收集算法和策略,以最大程度地提高内存利用率和应用程序的性能。

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

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

相关文章

电脑右击没有txt文件

文本文档是一个好工具&#xff0c;小而快&#xff0c;比word快多&#xff0c;一般情况下&#xff0c;记录都会先用txt文本&#xff0c;但是今天发现右击菜单新建里面没有&#xff0c;怎么回事&#xff1f; 这个需要打开注册编辑表修改 一、打开注册编辑表 win R 输入regedi…

JetLinks物联网学习(前后端项目启动)

前后端项目启动 1、后端1.1 pgsql改mysql报错2、elasticSearch7.0版本以上_doc格式取消 2、前端 1、后端 环境准备&#xff1a; 1、window系统7,8&#xff0c;10 。 硬件资源最低要求4c8G&#xff0c;硬盘40G 2、JDK 1.8.0_2xx (需要小版本号大于200) 3、Maven3.6.3 4、Redis …

专题四_位运算( >> , << , , | , ^ )_算法详细总结

目录 位运算 常见位运算总结 1.基础位运算 2.给一个数 n ,确定它的二进制表示中的第 x 位是 0 还是 1 3.运算符的优先级 4.将一个数 n 的二进制表示的第 x 位修改成 1 5.将一个数n的二进制表示的第x位修改成0 6.位图的思想 7.提取一个数&#xff08;n&#xff09;二进…

解决使用阿里云DataV Geo在线地图路径访问403问题

文章目录 1. DataV Geo在线地图路径访问403问题2. 解决方法3. 重启生效 1. DataV Geo在线地图路径访问403问题 最近在写一个省市下钻的demo&#xff0c;用到的是 阿里云DataV Geo在线地图 去动态获取GeoJSON 省市的数据&#xff0c;如下代码 axios.get("https://geo.dat…

Ubuntu24.04 安装opencv4.10

Ubuntu24.04 安装opencv4.10 一、下载OpenCV二、更新系统&#xff0c;安装必要的包1、“E: unable to locate libjasper-dev"的解决方法2、没有公钥&#xff0c;无法验证下列签名 :NO_PUBKEY 的解决方法 三、配置&#xff0c;使用cmake工具1、新建build目录2、在build中&a…

计算机毕业设计 玩具租赁系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

智能 Uber 发票 PDF 合并工具

在现代商务出行中&#xff0c;尤其是在跨国出差中&#xff0c;处理和整合大量 Uber 发票已成为一项不小的挑战。手动整理和合并这些发票不仅耗时&#xff0c;还容易出错。作为开发者&#xff0c;为什么不开发一个自动化工具&#xff0c;将这些任务交给代码来完成呢&#xff1f;…

【AI学习笔记】初学机器学习西瓜书概要记录(一)机器学习基础知识篇

初学机器学习西瓜书的概要记录&#xff08;一&#xff09;机器学习基础知识篇(已完结) 初学机器学习西瓜书的概要记录&#xff08;二&#xff09;常用的机器学习方法篇(待更) 初学机器学习西瓜书的概要记录&#xff08;三&#xff09;进阶知识篇(待更) 文字公式撰写不易&#x…

基于代理的分布式身份管理方案

目的是使用分布式的联合计算分发去替换掉区块链中原有的类第三方可信中心的证书机制&#xff0c;更加去中心化。 GS-TBK Group Signatures with Time-bound Keys. CS-TBK 算法 Complete subtree With Time-bound Keys&#xff0c;该算法是用来辅助检测用户的签名是否有效&…

新手学习Python第十天-新手笔记(速学)

一、特殊方法和特殊属性 1.1 特殊属性 __dict__:获得类对象或实例对象所绑定的所有属性和方法的字典 1.2 特殊方法&#xff1a; 1.2.1 .__len__:通过重写__len__()方法&#xff0c;让内置函数len()的参数可以是自定义类型 长度 1.2.2 __add__():通过重写__add__()方法&…

ICMP

目录 1. 帧格式2. ICMPv4消息类型(Type = 0,Code = 0)回送应答 /(Type = 8,Code = 0)回送请求(Type = 3)目标不可达(Type = 5,Code = 1)重定向(Type = 11)ICMP超时(Type = 12)参数3. ICMPv6消息类型回见TCP/IP 对ICMP协议作介绍 ICMP(Internet Control Messag…

HTTP中的Cookie与Session

一、背景 HTTP协议是无状态无连接的。 无状态&#xff1a;服务器不会保存客户端历史请求记录&#xff0c;每一次请求都是全新的。 无连接&#xff1a;服务器应答后关闭连接&#xff0c;每次请求都是独立的。 无状态就导致服务器不认识每一个请求的客户端是否登陆过。 这时…

TCP并发服务器的实现

一请求一线程 问题 当客户端数量较多时&#xff0c;使用单独线程为每个客户端处理请求可能导致系统资源的消耗过大和性能瓶颈。 资源消耗&#xff1a; 线程创建和管理开销&#xff1a;每个线程都有其创建和销毁的开销&#xff0c;特别是在高并发环境中&#xff0c;这种开销…

计算机三级网络技术总结(一)

RPR环中每一个节点都执行SRP公平算法IEEE 802.11a和g将传输速率提高到54Mbps一个BGP发言人与其他自治系统中的BGP发言人要交换路由信息就要先建立TCP连接在一个区域内的路由器数一般不超过200个进入接口配置模式&#xff1a;Router(config)#interface <接口名> 封装ppp协…

CentOS上使用Mosquitto实现Mqtt主题消息发布和订阅mqtt主题消息连同时间戳记录到文件

场景 CentOS上使用rpm离线安装Mosquitto(Linux上Mqtt协议调试工具)附资源下载&#xff1a; CentOS上使用rpm离线安装Mosquitto(Linux上Mqtt协议调试工具)附资源下载-CSDN博客 上面介绍了mosquitto的离线安装。 如果业务场景中需要订阅某mqtt主题的消息并将收到消息的时间以…

婚礼弹幕上墙阳光正好,爱意正浓,打造一场出圈的唯美婚礼!

原文地址 婚礼现场的弹幕功能可以给整个场景增添温暖和喜庆的氛围。通过手机发送祝福&#xff0c;让亲友可以即时将祝福传达给新人&#xff0c;同时这些祝福以弹幕的形式在大屏幕上滚动展示&#xff0c;增加了现场互动的乐趣。墙上新闻搭配的功能则更加抢眼&#xff0c;不仅可…

idea插件推荐之Cool Request

Cool Request是一款基于IDEA的HTTP调试工具&#xff0c;可以看成是轻量版的postman&#xff0c;它会自动扫描项目代码中所有API路径&#xff0c;按项目分组管理。一个类被定义为Controller且其中的方法被RequestMapping或者XXXMapping注解标注以后就会被扫描到。 对应方法左侧会…

【C++二叉树】102.二叉树的层序遍历

107. 二叉树的层序遍历 II - 力扣&#xff08;LeetCode&#xff09; 思路分析&#xff1a; 层序遍历&#xff0c;但是要注意输出的结果是一个二维数组&#xff0c;不是一层一个值一个值的输出&#xff0c;而是要一层一层的输出。可以通过一个循环控制每一层的数据个数&#xff…

FastAdmin CMS 操作手册

FastAdmin CMS 操作手册 概述&#xff1a; 安装&#xff1a; 配置&#xff1a; 模板&#xff1a; 模板目录&#xff1a; 标签&#xff1a; 全局&#xff1a; 文章&#xff1a; 专题&#xff1a; 栏目&#xff1a; 公共参数&#xff1a; 单页&#xff1a; 特殊标签&#xff1a;…

python正则表达式如何不区分大小写

使用python的re模块做模式匹配时&#xff0c;有时需要忽略大小写&#xff0c;只需要在re.search()函数中添加参数re.IGNORECASE即可。 mystring some string pattern some pattern match re.search(pattern, mystring, re.IGNORECASE)