Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 垃圾回收算法
文章目录
- 垃圾回收机制
- 垃圾判断算法
- 引用计数法
- 可达性分析算法
- 虚拟机栈中的引用(方法的参数、局部变量等)
- 本地方法栈中 JNI 的引用
- 类静态变量
- 运行时常量池中的常量
- 垃圾收集算法
- Mark-Sweep(标记-清除)算法
- Copying(标记-复制)算法
- Mark-and-Compact(标记-整理)算法
- Generation Collection (分代收集) 算法
垃圾回收机制
垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
垃圾判断算法
引用计数法
给对象中添加一个引用计数器:
- 每当有一个地方引用它,计数器就加 1;
- 当引用失效,计数器就减 1;
- 任何时候计数器为 0 的对象就是不可能再被使用的。
这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题。
![![[Pasted image 20231011222409.png]]](https://i-blog.csdnimg.cn/direct/2ae2be8cb7c34e08b6c4e1d805135ca3.png)
可达性分析算法
该算法通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
![![[Pasted image 20231011222616.png]]](https://i-blog.csdnimg.cn/direct/044431ac269e4fc9ae7a82b892eadb84.png)
图片来源:JavaGuide
Object 6 ~ Object 10 之间虽有引用关系,但它们到 GC Roots 不可达,因此为需要被回收的对象
所谓对象之间的相互引用问题:除了对象 objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 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]]](https://i-blog.csdnimg.cn/direct/706a94cd14bb4fd28114ed2b4c7d861d.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_STRING 和 CONSTANT_CLASS 作为常量存储在运行时常量池。它们可以用来作为 GC Roots。
这些常量引用的对象(字符串"Hello, World"和 Object.class 类对象)在常量池中,只要包含这些常量的 ConstantPoolReference 类未被卸载,这些对象就不会被垃圾回收。
垃圾收集算法
垃圾收集算法(Garbage Collection Algorithm) 是一种自动内存管理机制,用于在程序运行时自动识别和回收不再使用的对象,以释放内存空间和提升系统性能
Mark-Sweep(标记-清除)算法
![![[Pasted image 20231011230810.png]]](https://i-blog.csdnimg.cn/direct/96b2de7317854a8c83046848290189ec.png)
图片来源:JavaGuide
标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。
它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:
- 效率问题:标记和清除两个过程效率都不高。
- 空间问题:标记清除后会产生大量不连续的内存碎片。
对于标记的对象是可回收还是不可回收对象,两种说法都有支持者,按照前者:
- 当一个对象被创建时,给一个标记位,假设为 0 (false);
- 在标记阶段,从根对象出发,将所有可达对象(或用户可以引用的对象)的标记位设置为 1 (true);
- 扫描阶段 清除标记位为 0 (false)的对象。
Copying(标记-复制)算法
![![[Pasted image 20231011231209.png]]](https://i-blog.csdnimg.cn/direct/118d8a5ee5384e239b25bb2031b56abe.png)
图片来源:JavaGuide
为了解决 标记-清除 算法的效率和内存碎片问题,复制(Copying)收集算法 将内存分为大小相同的两块,每次使用其中的一块。
当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
该算法存在的问题:
- 可用内存变小:可用内存缩小为原来的一半。
- 不适合老年代:如果存活对象数量比较大,复制性能会变得很差。
Mark-and-Compact(标记-整理)算法
![![[Pasted image 20231011231514.png]]](https://i-blog.csdnimg.cn/direct/f8b31b8188c84e50b80bf535e72105ec.png)
图片来源:JavaGuide
标记-整理(Mark-and-Compact)算法是根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
由于多了整理这一步,因此效率不高,适合老年代这种垃圾回收频率不是很高的场景。
Generation Collection (分代收集) 算法
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 Java 堆分为新生代和老年代,根据各个年代的特点选择合适的垃圾收集算法,通过优化垃圾收集的效率,提高了系统的性能。
比如在新生代中,每次 GC 都会有大量对象死去,可以选择 Copying 算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。
而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
Java 虚拟机(JVM)中的垃圾收集器通常会根据堆内存的大小、对象的存活时间等因素动态选择合适的垃圾收集算法和策略,以最大程度地提高内存利用率和应用程序的性能。
相关文章:
Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 垃圾回收算法
文章目录 垃圾回收机制垃圾判断算法引用计数法可达性分析算法虚拟机栈中的引用(方法的参数、局部变量等)本地方法栈中 JNI 的引用类静态变量运行时常量池中的常量 垃圾收集算法Mark-Sweep(标记-清除)算法Copying(标记-…...
苍穹外卖Day01-2
导入接口文档 yApi接口管理平台http://api.doc.jiyou-tech.com/ 创建项目 导入接口文件 导入结果界面 Swagger 介绍 使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面。 官网:ht…...
软考中级软件设计师——数据结构与算法基础学习笔记
软考中级软件设计师——数据结构与算法基本概念 什么是数据数据元素、数据项数据结构逻辑结构物理结构(存储结构) 算法什么是算法五个特性算法效率的度量时间复杂度空间复杂度 什么是数据 数据是信息的载体,是描述客观事物属性的数、字符及所…...
虚幻引擎 | (类恐鬼症)玩家和NPC语音聊天(中)
虚幻引擎 | (类恐鬼症)玩家和NPC语音聊天-CSDN博客 上篇偏重实现步骤,中篇偏重理解校准和降低延迟,下篇加入上下文背景array和设置口音 TTS通用参数 ————————————————————————————————————…...
整流电路的有源逆变工作状态
目录 1. 逆变的概念 2. 有源逆变的条件 3. 电流电路的概念 4. 产生逆变的条件 5. 三相桥式全控整流电路的有源逆变工作状态 6. 逆变角的概念 7. 逆变失败的原因 8. 最小逆变角的限制 整流电路的有源逆变状态是指通过控制整流器,使其将直流电源的能量反向送回…...
Android 签名、空包签名 、jarsigner、apksigner
jarsigner是JDK提供的针对jar包签名的通用工具, 位于JDK/bin/jarsigner.exe apksigner是Google官方提供的针对Android apk签名及验证的专用工具, 位于Android SDK/build-tools/SDK版本/apksigner.bat jarsigner: jarsigner签名空包执行的命令: jar…...
java基础(小技巧)
文章目录 一、日志输出二、字符串拼接三、日期比较四、常用注解五、Lombok的原理 提示:以下是本篇文章正文内容,下面案例可供参考 一、日志输出 之前使用的方式。在要使用的类里面定义日志类: private static Logger logger LoggerFactory…...
Android Studio 安装配置教程(Windows最详细版)
目录 前言 Android Studio 下载 Android Studio 安装 Android Studio 使用 一、创建默认项目(Compose) 二、创建常规项目 三、使用ViewBinding 四、查看Gradle版本、SDK版本、JDK版本 ① Gradle版本 ② SDK版本 ③ JDK版本 前言 Android开发…...
Cesium绘制可编辑线
Cesium 第一章 绘制可编辑线 Screen-2024-09-17-202059的副本 文章目录 Cesium一、绘制线二、编辑线三、使用 一、绘制线 1、方法 //场景相机控制viewer.scene.screenSpaceCameraController.enableRotate false; //cesium相机控制 绘制和编辑时 禁止转动场景// 鼠标样式修改…...
【算法】差分思想:强大的算法技巧
📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…...
微软开源项目 Detours 详细介绍与使用实例分享
目录 1、Detours概述 2、Detours功能特性 3、Detours工作原理 4、Detours应用场景 5、Detours兼容性 6、Detours具体使用方法 7、Detours使用实例 - 使用Detours拦截系统库中的UnhandledExceptionFilter接口,实现对程序异常的拦截 C++软件异常排查从入门到精通系列教程…...
Numba基础
1. Numba 基础 1.1 什么是 Numba? Numba 是一个 JIT 编译器,用于加速数值计算。它通过即时编译技术,将 Python 代码在运行时编译为机器代码,极大地提升执行速度,特别适合循环和矩阵操作等密集型计算。 2. Numba 基本…...
[JAVA]介绍怎样在Java中通过字节字符流实现文件读取与写入
一,初识File类及其常用方法 File类是java.io包下代表与平台无关的文件和目录,程序中操作文件和目录,都可以通过File类来完成。 通过这个File对象,可以进行一系列与文件相关的操作,比如判断文件是否存在,获…...
oracle停止当前运行的JOB或kill会话
在Oracle中,可以使用DBA_SCHEDULER_JOBS视图来查找当前正在运行的作业(job),并使用DBMS_SCHEDULER.STOP_JOB过程来停止它们 SELECT JOB_NAME, STATE FROM DBA_SCHEDULER_JOBS WHERE STATE RUNNING; SELECT * FROM DBA_SCHEDULE…...
SpringBoot 消息队列RabbitMQ 消息可靠性 数据持久化 与 LazyQueue
介绍 在默认情况下,RabbitMQ会将接收到的信息保存在内存中以降低消息收发的延迟 一旦MO宕机,内存中的消息会丢失内存空间有限,当消费者故障或处理过慢时,会导致消息积压,引发MQ阻塞 在消息队列运行的过程中…...
CLIP论文中关键信息记录
由于clip论文过长,一直无法完整的阅读该论文,故而抽取论文中的关键信息进行记录。主要记录clip是如何实现的的(提出背景、训练数据、设计模式、训练超参数、prompt的作用),clip的能力(clip的模型版本、clip…...
sshj使用代理连接服务器
之前我是用jsch连接服务器的,但是没办法使用私钥连接,搜了一下似乎是不支持新版的SSH-rsa,并且jsch很久没更新了,java - "com.jcraft.jsch.JSchException: Auth fail" with working passwords - Stack Overflow 没办法…...
【Leetcode:1184. 公交站间的距离 + 模拟】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...
VRRP 笔记
一、概念: vrrp:Virtual Router Redundancy Protocol 虚拟路由冗余协议,当网关发生故障时,进行主备切换,保证业务连续性 把多台物理机的网关虚拟成一台Virtual Router,称为 VRID VIP:虚拟IP VM…...
【洛谷】P3743 小鸟的设备 的题解
【洛谷】P3743 小鸟的设备 的题解 题目传送门 题解 水一道二分 qaq 刚开始考虑的是动态规划,但是动态规划并不能维护题目所要求的东西。所以我们将思路转向另一种求最值问题的方法:二分答案。 首先,如果一个设备在 t t t 的时间内消耗的…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
