Java的JVM垃圾回收机制GC概述

news2024/10/5 7:16:49

JVM——GC机制

  • 1、什么是GC?
  • 2、GC算法的总体概述
  • 3、JVM所处的位置
  • 4、JVM整体结构
  • 5、JVM架构模型
  • 6、Java垃圾回收机制优缺点
  • 7、GC主要关注的区域
    • 垃圾回收算法:标记阶段,引用计数
    • 循环引用
    • 标记阶段:可达性分析算法
    • GC root可以是哪些?
  • 总结①
  • 8、对象的finalization机制
    • 注意
    • 生存还是死亡?
    • 具体的过程如何?


1、什么是GC?

JVM垃圾收集(Java garbage collection)。

GC采用的分带收集算法:

  • 次数上频繁收集Young区。
  • 次数上较少收集Old区。
  • 基本不会动Perm区。

2、GC算法的总体概述

在这里插入图片描述
JVM在进行gc时,并非每次都会对上面的三个内存区域一起回收,大部分时候回收的都只是新生代

GC按照回收的区域分了两种类型:

  • 普通GC(又称之为minor gc):只针对新生代区域的gc。
  • 全局GC(major gc or full gc):针对老年代的gc,偶尔伴随着新生代的gc以及对永久代的gc。

3、JVM所处的位置

JVM运行在操作系统OS之上,与硬件并没有直接的交互。
在这里插入图片描述

4、JVM整体结构

  • HotSpot VM是当前市面上高性能Java虚拟机的代表作之一。
  • 采用解释器 即时编译并存的架构,解释器也就是虚拟机处理字段码的CPU;
  • JVM运行整体逻辑框图如下:
    在这里插入图片描述
    其中,执行引擎包含三部分:解释器、即时编译器器、垃圾回收GC

5、JVM架构模型

Java编译器输入的指令流基本上都是按照基于栈的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构,具体差异如下:

基于栈指令集架构:

  • 设计实现简单,适用于资源受限的系统;
  • 避开了寄存器的分配难题,使用零地址指令方式分配;
  • 指令流中的指令大部分都是零地址指令,执行过程依赖于操作栈。指令集更小,编译器更容易实现;
  • 不需要硬件级支持,可移植性好,更好实现跨平台

基于寄存器指令集架构

  • 典型的应用是X86的二进制指令集,例如传统PC以及Android的Davlik 虚拟机
  • 指令集架构则完全依赖于硬件,可移植性差。
  • 性能优秀和执行高效。
  • 花费了更少的指令去完成 一项操作 。
  • 大部分情况下,基于寄存器架构的指令集往往都是以一地址指令、二地址指令三地址指令为主,而基于栈结构的指令集却往往以零地址指令为主要。

6、Java垃圾回收机制优缺点

优点

  • 自动内存管理,无序开发人员手动参与内存的分配和回收,降低内存泄露或者溢出的风险。
  • 自动内存管理机制,让开发人员着重业务逻辑的实现。
  • oracle官网关于GC机制的介绍:GC机制概述。

7、GC主要关注的区域

GC主要关注方法区的垃圾收集。
在这里插入图片描述
垃圾收集可以对年轻代回收,也可以对老年代回收,甚至是全栈和方法区 的回收。

垃圾回收算法:标记阶段,引用计数

堆里存放的几乎所有Java对象,在GC执行垃圾回收之前,首先休要区分内存哪些是存活的对象,哪些是已经死亡的对象。

一般判定对象存活的方式有两种:引用计数可达性分析算法

引用计数算法(reference counting)对每一个对象保存一个整型的引用计数器属性,记录对象被引用的情况。

  • 对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器+1;当引用失效时,引用计数器-1;
  • 若对象A的引用计数器数值0,则表示对象A不可能被使用,可以被GC。
  • 优点:实现简单,垃圾对象便于辨识,判定效率高,回收没有延迟。
  • 缺点:需要单独的字段存储计数器,增加了存储空间的开销。

每次赋值都需要更新计数器,伴随着加法和减法操作,增加了时间开销。
引用计数器有一个严重的问题,无法处理循环引用的情况,这是致命缺陷。

循环引用

若p的指针断开的时候,内部的引用形成了一个循环,这就是循环引用。从而造成了内存的泄露。

在这里插入图片描述
给出测试代码:

public class RefCountGC {
	// 这个成员属性的唯一作用就是占用内存空间
	private byte[] bigSize = new byte[5 * 1025 * 1024];
	// 引用
	Object reference = null;
	
	public static void main(String[] args) {
		RefCountGC obj1 = new RefCountGC();
		RefCountGC obj2 = new RefCountGC();
		obj1.reference = obj2;
		obj2.reference = obj1;
		obj1 = null;
		obj2 = null;
		// 显式的执行GC行为,判定obj1和obj2是否被gc了
		System.gc();
	}
}

若使用了引用计数算法,则这两个对象都无法被GC,但是测试会发现均被回收了,则说明Java使用的并不是上述提及的算法。


标记阶段:可达性分析算法

可达性分析算法:可以称之为根搜索算法、追踪性能垃圾收集

相比较于引用计数算法而言,可达性分析算法不仅同样具备实现简单和执行高效等特点,更重要的是可以有效解决循环引用的问题,防止内存泄露的发生。

实现基本思路:

  • 可达性分析算法是以根对象集合(GCroot)为起始点,按照从上至下方式搜索被根对象集合所连接的目标对象是否可达。
  • 使用了可达性分析算法后,内存存活对象都会被根对象直接或者间接连接着,搜索所走过的路径称之为引用链(Reference Chain)
  • 若目标对象没有任何引用链相连,则不可达,意味着该对象已经死亡,可以被GC。
  • 在可达性分析算法中,只有能够被根对象集合直接或者简介链接的对象才是存活对象
    在这里插入图片描述

GC root可以是哪些?

  • 虚拟机栈中引用的对象,例如各个线程调用的方法中使用到的参数、局部变量等等。
  • 本地方法的栈内JNI(也就是通常说的本地方法 )引用的对象方法区中类静态属性引用的对象,例如Java类的引用类型的静态变量。
  • 方法区中的常量引用的对象,例如字符串常量池String Table存在的引用。
  • 所有被同步锁synchronized 所持有的对象。
  • Java虚拟机内部的引用,例如:基本数据类型对应的Class对象一些常驻的异常对象(NullPointerException、outofMemoryError)、==类加载器 ==。
  • 反映Java虚拟机内部情况的JMXBeanJVMTI中 注册的回调、本地代码缓存等等。

总结①

除了堆空间 的一些结构,例如:虚拟机栈本地方法栈方法区字符串常量池等地方对堆空间进行引用的,都可以拿来作为GC root进行可达性分析。

除了这些固定的GC Root集合之外,根据用户所选择的GC收集器以及当前回收的内存区域不同,还可以有其他对象临时性 的加入,共同构成完整的GC Root集合,例如:分代收集局部回收(PartialGC)

由于root采用了栈方法存放变量和指针,若是一个指针,则保存了堆内存的对象,但是自己又不存放在堆内存里面,那么其就是一个Root
注意:若要使用可达性分析来判定内存是否可以被回收,那么分析工作必须要在一个能保证一致性的快照中进行。若无法满足这点则分析结果的准确度存疑。
以上这点也是导致GC进行时必须stop the world 的一个重要原因,即使是在号称(几乎)不会发生停顿的CMS收集器中,枚举根节点也是必须要停顿的。

8、对象的finalization机制

Java语言提供了对象终止(finalization)机制来允许开发人员 提供对象被销毁之前的自定义处理逻辑 。

若GC发现没有引用指向一个对象,也就是:GC该对象之前,总是会调用该对象的finalize方法。

finalize方法允许在子类中被重写,用于在对象被回收之前进行资源的释放。通常会在这个方法中进行一些资源释放和清理的工作,例如:关闭文件IO流、套接字或者是数据库连接等等。

注意

永远不要 主动调用某一个对象的finalize方法,应该交给GC使用,原因如下:

  • finalize方法时可能会导致该对象复活。
  • finalize方法执行的时候时间是没有保障的,完全由GC线程来决定,在某些极端情况下,若不发生GC,则finalize完全没有执行的机会;由于优先级比较低,即使主动调用了这个方法,也不会因此直接进行回收。
  • 一个糟糕的finalize会严重的影响GC性能。

从功能角度来说,finalize类似C++的析构函数,但是Java采用的是基于GC的自动内存管理机制,所以finalize方法在本质上不同于C++中的析构函数。

由于finalize方法的存在,虚拟机中的对象一般处于三种可能的状态。

生存还是死亡?

若从所有的根节点都无法访问到某一个对象的时候,说明对象已经无法使用了,则一般来说该被GC回收。

但是实际上 ,一个无法触及的对象可能在某一个条件下复活自己,若是这样,GC回收就是不合理的,因此,定义虚拟机中的对象可能的三种状态如下:

  • 可触及的:从根节点开始,可以到达的对象。
  • 可复活的:对象的所有引用都被释放了,但是对象可能在 finalize中复活。
  • 不可触及的:对象的finalize方法被调用了,并且没有复活,那么进入不可触及状态。不可触及对象不可能被复活,因此finalize只会被调用了一次

以上的三种状态中,由于finalize方法的存在,只有在对象不可触及的时候才能被GC。

具体的过程如何?

判定一个对象objA是否能被GC,至少需要经历两次标记过程

  • 若对象objA到达GC Root没有引用链,则进行了第一次的标记过程。
  • 开始进行筛选,判定该对象是否有必要执行finalize方法。
    • 若对象objA没有重写finalize方法,或者finalize方法已经被虚拟机调用过了,则虚拟机将会视之为没有必要执行,objA判定不可触及
    • 若对象objA重写了 finalize方法,并且还没有执行过,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize方法执行。
    • finalize方法是对象逃脱或者 死亡的最后机会,稍后GC会对F-Queue队列中的对象进行了第二次标记;若objA在finalize方法中引用链上的任何一个对象建立了联系,那么在第二次标记时,objA会被移除即将GC的集合。在这之后,对象再次出现没有引用的情况,这种情况下,finalize方法不会被再次调用,对象直接变成不可触及 的状态,也就是说,一个对象的finalize方法只会被调用一次

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

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

相关文章

JavaScript代码题--以及一些奇奇怪怪的发现

解析 let a{b:10,c:{d:[11,12],e:13}},实现 10111213 效果 解 const a{b:10,c:{d:[11,12],e:13}}function sum(obj) {let total 0;const value Object.values(obj)value.forEach(item>{total typeof item number ? item : sum(item)})return total }const …

Java家教系统家教网站家教兼职系统

简介: 用户可以注册成为学员也可以是教员。教员发布家教信息,学员根据自己的要求查找符合自己的教员。学员预约教员的某一天去家教,教员可以在个人中心里查看,是否接受该预约。在教员接受或拒绝之前,学员随时可以取消…

数据库,计算机网络、操作系统刷题笔记23

数据库,计算机网络、操作系统刷题笔记23 2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,可能很多算法学生都得去找开发,测开 测开的话,你就得学数据库,sql,oracle…

基于 Vue 制作一个猜拳小游戏

目录前言:项目效果展示:对应素材:代码实现思路:实现代码:总结:前言: 在工作学习之余玩一会游戏既能带来快乐,还能缓解生活压力,跟随此文一起制作一个小游戏吧。 描述&…

【2042. 检查句子中的数字是否递增】

来源:力扣(LeetCode) 描述: 句子是由若干 token 组成的一个列表,token 间用 单个 空格分隔,句子没有前导或尾随空格。每个 token 要么是一个由数字 0-9 组成的不含前导零的 正整数 ,要么是一个…

ORA-00600 kcratr_nab_less_than_odr 问题处理

问题:ORA-00600: 内部错误代码, 参数: [kcratr_nab_less_than_odr], [1], [196495], [39399], [39460], [], [], [], [], [], [], []导致原因:可能是由于服务器宕机,控制文件的缺失,或者在线日志文件在实例恢复时不完整1、数据库未…

5G边缘计算网关助力5G工业物联网智能化建设

5G边缘计算,凭借高带宽、高可靠、低时延、移动性等特性,推动工业生产物联网发展趋势,实现工业更快、更精准通信及数据共享。边缘计算网关下5G工业物联网远程感知生产一线,工控数字化、自动化、智能化,降低人物力资源成…

LeetCode 2042. 检查句子中的数字是否递增

【LetMeFly】2042.检查句子中的数字是否递增 力扣题目链接:https://leetcode.cn/problems/check-if-numbers-are-ascending-in-a-sentence/ 句子是由若干 token 组成的一个列表,token 间用 单个 空格分隔,句子没有前导或尾随空格。每个 tok…

【计算机体系结构】指令集并行(ILP)动态调度算法:Tomasulo实现代码(Tomasulo Algorithm Implementation)

Tomasulo Algorithm Implementation (本文章仅提供算法实现过程,具体算法思想请查阅教科书) 如果觉得这篇文章有用,请记得点个赞并收藏哦! 1.Introduction Tomasulo算法用于指令的动态调度,允许乱序执行…

C C++内存对齐以及特殊类的大小

目录C语言内存对齐现象内存对齐规则为什么存在内存对齐如果struct or class中存在成员函数时的大小空类大小为1Cclass存在虚函数时的大小C语言 内存对齐现象 C语言中结构体的大小往往不是结构体中各种数据类型的加和,因为存在内存对齐; struct S {double d;//8字…

Linux常用系统日志

文章目录一 常用系统日志二 系统日志优先级三 其他日志文件一 常用系统日志 日志文件用途/var/log/messages记录大多数系统日志信息,包括启动、IO错误、网络和程序等问题/var/log/secure记录安全和身份验证等相关消息和错误/var/logrsyslog将所有日志文件写入到该目…

Nacos 漏洞利用

Nacos 漏洞利用 1.漏洞描述: Alibaba Nacos是阿里巴巴推出来的一个新开源项目,是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。致力于帮助发现、配置和管理微服务。Nacos提供了一组简单易用的特性集,可以快速实现动态…

ASP.NET企业智能办公OA系统源码带文档【源码免费分享】

ASP.NET企业智能办公OA系统源码带文档 需要源码学习可私信我获取! 本系统特色功能: 1:自定义工作流程,系统所有参数可自定义配置,支持多分公司、多部门架构 2:采用三层结构设计软件,系统扩容性…

vivo 实时计算平台建设实践

作者:vivo 互联网实时计算团队- Chen Tao 本文根据“2022 vivo开发者大会"现场演讲内容整理而成。 vivo 实时计算平台是 vivo 实时团队基于 Apache Flink 计算引擎自研的覆盖实时流数据接入、开发、部署、运维和运营全流程的一站式数据建设与治理平台。 一、v…

【云边有个小卖部】阅读笔记

童年就像童话,这是他们在童话里第一次相遇。 那么热的夏天,少年的后背被女孩的悲伤烫出一个洞,一直贯穿到心脏。 刘十三被欺负得最惨,却想保护凶巴巴的程霜。 每当她笑的时候,就让他想起夏天灌木丛里的萤火虫&#xff…

2022年专业连锁行业研究报告

第一章 行业概况 专业连锁经营是一种商业组织形式和经营制度,是指经营同类商品或服务的若干个企业,以一定的形式组成一个联合体,在整体规划下进行专业化分工,并在分工基础上实施集中化管理,把独立的经营活动组合成整体…

力扣sql入门篇(七)

力扣sql入门篇(七) 1 查询近30天用户活跃数 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 #注意因为between是两端都包含,仅统计30天,因而间隔天数写成29天 SELECT activity_date day,count(distinct user_id) active_users FROM Activity WHERE a…

使用Windbg分析多线程临界区死锁问题分享

目录 1、多线程死锁场景及多线程锁的类型 1.1、发生死锁的场景说明 1.2、锁的类型 2、问题实例说明 3、使用Windbg初步分析 4、进一步分析死锁 4.1、使用!locks命令查看临界区对象信息 4.2、通过占用临界区锁的线程id找到目标线程 4.3、如何将!locks命令打印出来的临界…

# 分布式理论协议与算法 第二弹 ACID原则

ACID 原则是在 1970年 被 Jim Gray 定义,用以表示事务操作:一个事务是指对数据库状态进行改变的一系列操作变成一个单个序列逻辑元操作,数据库一般在启动时会提供事务机制,包括事务启动 停止 取消或回滚。 但是上述事务机制并不真…

(1分钟)速通ikdtree

Ikdtree算法来自fast-lio2 其中i是incremental的缩写,表示增量的kdtree。 ​ Ikdtree算法来自fast-lio2 其中i是incremental的缩写,表示增量的kdtree。 ​ Ikdtree算法来自fast-lio2 其中i是incremental的缩写,表示增量的kdtree。 ​ 编辑…