首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

大白话理解可达性分析算法

  • 23-11-17 19:42
  • 3389
  • 9028
blog.csdn.net

垃圾收集(Garbage Collection,下文简称GC)是Java有别于其他编程语言的一大特点,GC主要考虑的有三个问题:

  • 哪些内存需要回收?
  • 什么时候回收?
  • 如何回收?

在这里插入图片描述
今天咱们主要聊聊JVM是如何判断对象可以被回收的。

常用的算法有两种:

  • 引用计数算法
  • 可达性分析算法
引用计数算法

在对象中添加一个引用计数器,每当新加一个引用时,计数器就加1,当引用失效时,计数器就减1。任何时刻只要计数器为0就代表对象没有引用可以被回收。

这种算法实现简单,判断高效,但是有一些缺点:
在这里插入图片描述
主流的商用JVM实现都没有采用这种方式。


可达性分析算法

目前主流的商用JVM都是通过可达性分析来判断对象是否可以被回收的。
在这里插入图片描述

这个算法的基本思路是:

通过一系列被称为「GC Roots」的根对象作为起始节点集,从这些节点开始,通过引用关系向下搜寻,搜寻走过的路径称为「引用链」,如果某个对象到GC Roots没有任何引用链相连,就说明该对象不可达,即可以被回收。

初看这段话是不是一脸懵呢?笔者当初也是的,完全不知道什么意思,后面才慢慢理解。

要想理解可达性算法,首先要想明白几个问题:
在这里插入图片描述

1、什么是对象可达?

对象可达指的就是:双方存在直接或间接的引用关系。
根可达或GC Roots可达就是指:对象到GC Roots存在直接或间接的引用关系。
如下代码:

public class MyObject {
	private String objectName;//对象名
	private MyObject refrence;//依赖对象

	public MyObject(String objectName) {
		this.objectName = objectName;
	}

	public MyObject(String objectName, MyObject refrence) {
		this.objectName = objectName;
		this.refrence = refrence;
	}

	public static void main(String[] args) {
		MyObject a = new MyObject("a");
		MyObject b = new MyObject("b");
		MyObject c = new MyObject("c");
		a.refrence = b;
		b.refrence = c;

		new MyObject("d", new MyObject("e"));
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

创建了5个对象,他们之间的引用关系如图:
在这里插入图片描述
假设a是GC Roots的话,那么b、c就是可达的,d、e是不可达的。

2、GC Roots是什么?

垃圾回收时,JVM首先要找到所有的GC Roots,这个过程称作 「枚举根节点」 ,这个过程是需要暂停用户线程的,即触发STW。
然后再从GC Roots这些根节点向下搜寻,可达的对象就保留,不可达的对象就回收。
那么,到底什么是GC Roots呢?

GC Roots就是对象,而且是JVM确定当前绝对不能被回收的对象(如方法区中类静态属性引用的对象 )。
只有找到这种对象,后面的搜寻过程才有意义,不能被回收的对象所依赖的其他对象肯定也不能回收嘛。

当JVM触发GC时,首先会让所有的用户线程到达安全点SafePoint时阻塞,也就是STW,然后枚举根节点,即找到所有的GC Roots,然后就可以从这些GC Roots向下搜寻,可达的对象就保留,不可达的对象就回收。

即使是号称几乎不停顿的CMS、G1等收集器,在枚举根节点时,也是要暂停用户线程的。

GC Roots是一种特殊的对象,是Java程序在运行过程中所必须的对象,而且是根对象。
那么,哪些对象可以成为GC Roots呢?

3、哪些对象可以作为GC Roots?

可以作为GC Roots的对象可以分为两大类:全局对象和执行上下文。
在这里插入图片描述
下面就一起来理解一下为什么这几类对象可以被作为GC Roots。
1、方法区静态属性引用的对象
全局对象的一种,Class对象本身很难被回收,回收的条件非常苛刻,只要Class对象不被回收,静态成员就不能被回收。

2、方法区常量池引用的对象
也属于全局对象,例如字符串常量池,常量本身初始化后不会再改变,因此作为GC Roots也是合理的。

3、方法栈中栈帧本地变量表引用的对象
属于执行上下文中的对象,线程在执行方法时,会将方法打包成一个栈帧入栈执行,方法里用到的局部变量会存放到栈帧的本地变量表中。只要方法还在运行,还没出栈,就意味这本地变量表的对象还会被访问,GC就不应该回收,所以这一类对象也可作为GC Roots。

4、JNI本地方法栈中引用的对象
和上一条本质相同,无非是一个是Java方法栈中的变量引用,一个是native方法(C、C++)方法栈中的变量引用。

5、被同步锁持有的对象
被synchronized锁住的对象也是绝对不能回收的,当前有线程持有对象锁呢,GC如果回收了对象,锁不就失效了嘛。

尾巴

总结,可达性分析就是JVM首先枚举根节点,找到一些为了保证程序能正常运行所必须要存活的对象,然后以这些对象为根,根据引用关系开始向下搜寻,存在直接或间接引用链的对象就存活,不存在引用链的对象就回收。

程序员小潘
微信公众号
专注于Java后端技术分享~
注:本文转载自blog.csdn.net的程序员小潘的文章"https://javap.blog.csdn.net/article/details/109253339"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top