java gc回收算法_Java GC回收算法-判定一个对象是否可以回收
开源推荐
推荐一款一站式性能监控工具(开源项目)
Pepper-Metrics是跟一位同事一起开发的开源组件,主要功能是通过比较轻量的方式与常用开源组件(jedis/mybatis/motan/dubbo/servlet)集成,收集并计算metrics,并支持输出到日志及转换成多种时序数据库兼容数据格式,配套的grafana dashboard友好的进行展示。项目当中原理文档齐全,且全部基于SPI设计的可扩展式架构,方便的开发新插件。另有一个基于docker-compose的独立demo项目可以快速启动一套demo示例查看效果https://github.com/zrbcool/pepper-metrics-demo。如果大家觉得有用的话,麻烦给个star,也欢迎大家参与开发,谢谢:)
进入正题...
GC的出现解放了程序员需要手动回收内存的苦恼,但我们也是要了解GC的,知己知彼,百战不殆嘛。
常见的GC回收算法主要包括引用计数算法、可达性分析法、标记清除算法、复制算法、标记压缩算法、分代算法以及分区算法。
其中,引用计数法和可达性分析法用于判定一个对象是否可以回收,其他的算法为具体执行GC时的算法。
今天来聊聊可达性分析法,并说明一下什么样的对象才是真正可以被回收的。
在介绍引用计数法的时候,我们提到了此种算法的循环引用的缺陷,所以Java没有使用此种算法。
那Java使用的是啥算法来标记一个对象是否是垃圾对象呢?
Java是通过判断一个对象是否可触及,以及一个对象的引用类型(强引用、软引用、弱引用、虚引用)来决定是否回收这个对象。
本文将根据上述,分为两部分进行介绍。
最后会简单介绍一下GC回收过程中保证数据一致性的方法:Stop the World
1 如何判断一个对象是否可触及?
判断是否可触及包含两个要素: 通过可达性分析该对象到GC Root不可达,如果不可达会进行第一次标记。 已经丧失"自救"机会,如果没有自救机会,会进行第二次标记,此时该对象可回收。
1.1 可达性分析
可达性分析法定义了一系列称为"GC Roots"的对象作为起始点,从这个起点开始向下搜索,每一条可达路径称为引用链,当一个对象没有任意一条引用链可以到达"GC Roots"时,那么就对这个对象进行第一次"可回收"标记。
那么什么是GC Root呢?
可以理解为由堆外指向堆内的引用。
那么都有哪些对象可以作为GC Roots呢? 包括如下几种 代码中某一方法中的局部变量 类变量(静态变量) 常量 本地方法栈中引用的对象 * 已启动且未停止的线程
下面以一段代码来简单说明一下前三类
class Test {
private static A a = new A(); // 静态变量 public static final String CONTANT = "I am a string"; // 常量
public static void main(String[] args) {
A innerA = new A(); // 局部变量 }
}
class A {
...
}
这段代码的运行时内存图示如下:
首先,类加载器加载Test类,会初始化静态变量a,将常量引用指向常量池中的字符串,完成Test类的加载;
然后,main方法执行,main方法会入虚拟机方法栈,执行main方法会在堆中创建A的对象,并赋值给局部变量innerA。
此时GC Roots状态如下:
当main方法执行完出栈后,变为:
第三个对象已经没有引用链可达GC Root,此时,第三个对象被第一次标记。
1.2 对象的"自救"
一个被可达性分析标记为可回收的对象,是有机会进行自救的。前提是:覆写了Object的finalize()方法,且GC还没有执行该对象的finalize()方法。
先来看一下finalize方法的定义
/*** Called by the garbage collector on an object when garbage collection* determines that there are no more references to the object.* A subclass overrides the {@code finalize} method to dispose of* system resources or to perform other cleanup.*
* The general contract of {@code finalize} is that it is invoked* if and when the Java™ virtual* machine has determined that there is no longer any* means by which this object can be accessed by any thread that has* not yet died, except as a result of an action taken by the* finalization of some other object or class which is ready to be* finalized. The {@code finalize} method may take any action, including* making this object available again to other threads; the usual purpose* of {@code finalize}, however, is to perform cleanup actions before* the object is irrevocably discarded. For example, the finalize method* for an object that represents an input/output connection might perform* explicit I/O transactions to break the connection before the object is* permanently discarded.*
* The {@code finalize} method of class {@code Object} performs no* special action; it simply returns normally. Subclasses of* {@code Object} may override this definition.*
* The Java programming language does not guarantee which thread will* invoke the {@code finalize} method for any given object. It is* guaranteed, however, that the thread that invokes finalize will not* be holding any user-visible synchronization locks when finalize is* invoked. If an uncaught exception is thrown by the finalize method,* the exception is ignored and finalization of that object terminates.*
* After the {@code finalize} method has been invoked for an object, no* further action is taken until the Java virtual machine has again* determined that there is no longer any means by which this object can* be accessed by any thread that has not yet died, including possible* actions by other objects or classes which are ready to be finalized,* at which point the object may be discarded.*
* The {@code finalize} method is never invoked more than once by a Java* virtual machine for any given object.*
* Any exception thrown by the {@code finalize} method causes* the finalization of this object to be halted, but is otherwise* ignored.** @throws Throwable the {@code Exception} raised by this method* @see java.lang.ref.WeakReference* @see java.lang.ref.PhantomReference* @jls 12.6 Finalization of Class Instances*/
protected void finalize() throws Throwable { }
大致翻译一下前两段:当GC判定某一对象不再通过任一形式被引用时,GC会调用该对象的finalize方法。方法执行时,可以进行任何操作,包括将这个对象再次赋值给某一变量引用,但其主要目的还是做一些对象的清除操作。
其实在finalize方法中,只要将这个对象的引用(this)再次赋值给某一变量,这个对象就可以"自救"。
如果一个对象在finalize阶段也没有完成自救,那么就真的要被回收了。
下面演示一个"自救"的例子:
public class SaveMe {
public static SaveMe saveMe;
public static void main(String[] args) throws InterruptedException {
saveMe = new SaveMe();
saveMe = null; // 取消引用,经过可达性分析,上面new出来的对象不再可达GC Root System.gc(); // 第一次GC,会执行finalize方法 Thread.sleep(1000);
if (saveMe == null) {
System.out.println("对象为null");
} else {
System.out.println("对象不为null");
}
// 经过上面的过程,对象已经自救了,这里再次将其引用置空 saveMe = null;
System.gc(); // 不会再执行finalize方法,没有机会自救了 Thread.sleep(1000);
if (saveMe == null) {
System.out.println("对象为null");
} else {
System.out.println("对象不为null");
}
}
// finalize方法全局只会执行一次 @Override
protected void finalize() throws Throwable {
super.finalize();
saveMe = this; // 进行自救 }
}
上述代码很简明,可根据注释理解。代码执行结果如下:
2 不同引用类型的回收
Java中有四种引用类型,引用强度由强到弱:强引用、软引用、弱引用、虚引用。针对不同的引用类型,GC的回收策略不同。
2.1 强引用
通过关键字new的对象就是强引用对象,强引用指向的对象任何时候都不会被回收,宁愿OOM也不会回收。
2.2 软引用
如果一个对象持有软引用,那么当JVM堆空间不足时,会被回收。
一个类的软引用可以通过java.lang.ref.SoftReference持有。
2.3 弱引用
如果一个对象持有弱引用,那么在GC时,只要发现弱引用对象,就会被回收。
一个类的弱引用可以通过java.lang.ref.WeakReference持有。
2.4 虚引用
几乎和没有一样,随时可以被回收。
通过PhantomReference持有。
3 Stop the World
问题的出现:如果程序一边执行,一边进行可达性分析的标记操作,那么有可能刚标记完一个对象,这个对象又再次被赋值给其他的引用。这样就有可能回收掉正在使用的对象。
解决这个问题的方式就是Stop the World(STW),STW会在所有线程到达一个安全点时,暂停掉所有应用线程的执行,然后开始专心的标记垃圾对象。这样就保证了数据的一致性,不会导致误回收。
java gc回收算法_Java GC回收算法-判定一个对象是否可以回收相关推荐
- .net 把一个对象赋值给一个参数_Java GC回收算法-判定一个对象是否可以回收
开源推荐 推荐一款一站式性能监控工具(开源项目) Pepper-Metrics是跟一位同事一起开发的开源组件,主要功能是通过比较轻量的方式与常用开源组件(jedis/mybatis/motan/dub ...
- java 二维链表_Java数据结构与算法----数组与链表
数据类型 1 数据类型介绍 数据类型的分类(按照结构划分):线性结构和非线性结构 线性结构:线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系 线性结构有两种不同的存储结构,即顺序 ...
- java 大整数编程_Java编程--RSA算法中的大整数运算
Java编程–RSA算法中的大整数运算 RSA原理浅析 RSA是利用陷门单向函数实现的,其安全基础依赖于大整数的分解问题的难解性 算法过程 为了加深对RSA算法的了解,接下来通过简单的一个例子来分析一 ...
- java常用算法_JAVA编程常用算法——冒泡排序
一.冒泡排序算法运作的过程 (1)比较相邻的元素.如果第一个比第二个大,就交换他们两个. (2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. (3 ...
- java密码学原型算法_java密码学原型算法实现——双线性对.pdf
java密码学原型算法实现--双线性对 Java 密码学原型算法实现--双线性对 1.背景介绍 如何使用jPBC 库进行双线性群初始化,包括: (1)质数阶双线性群(Prime-Order Bilin ...
- java快排原理_Java数据结构与算法——快速排序
声明:码字不易,转载请注明出处,欢迎文章下方讨论交流. 前言:Java数据结构与算法专题会不定时更新,欢迎各位读者监督.本篇文章介绍排序算法中最常用也是面试中最容易考到的排序算法--快排,包括快排的思 ...
- java冒泡遍历对象_Java经典排序算法(冒泡、选择、插入)
排序算法说明 排序说明 对一序列对象根据某个关键字进行排序. 术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面: 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b ...
- java string逆序_java经典入门算法题,java初学者必备
java经典入门算法题 开头求关注警告 喜欢这样文章的可以关注我,我会持续更新,你们的关注是我更新的动力!需要更多java学习资 料的也可以私信我! 祝关注我的人都:身体健康,财源广进,福如东海,寿比 ...
- java 计算矩阵行列式_Java 矩阵行列式算法(非高斯消元)
最近由于项目任务较少,手上有不少空闲的时间,所以抽空研究了一下矩阵行列式的算法. 先来说说行列式,以下摘自百度百科: 行列式在数学中,是由解线性方程组产生的一种算式.行列式的特性可以被概括为一个多次交 ...
最新文章
- oracle 生成 json文件,oracle - PLSQL导入JSON并导出为JSON文件 - 堆栈内存溢出
- python for循环九九乘法表_python—用for循环、while循环和一句话打印九九乘法表
- 为PHP5.4开启Zend OPCode缓存
- Swift中switch比较元组类型
- Java中可怕的双重检查锁定成语
- C#使用TCP/IP与ModBus进行通讯
- redis专题:redis缓存穿透、缓存击穿、缓存雪崩等问题如何解决?
- 2021-10-25 Vue异步操作
- Pointer Networks简介及其应用(格式化)
- (转载)AS3中的mouseEnabled与mouseChildren
- 实战揭秘地方性社区门户站运营大法
- CSS font-family 各字体一览表
- 知了课堂学习笔记一-Django预热-虚拟环境
- STM32F103ZET6驱动TM7705(AD7705)代码加心得
- EIGamal encryption VS Pairing encryption
- 趣味计算机冷知识,那些不为人知的计算机冷知识,还不码起来!
- Franka Emika Panda 机械臂环境配置
- 【附源码】计算机毕业设计SSM汽车维修服务系统
- 在每天一个故事中......
- 在前端页面中调用本机的摄像头
热门文章
- IO流的体系及FileReader、FileWriter
- linux指令查看tomcat日志
- android edittext禁止输入特殊字符,Android EditText禁止输入空格和特殊字符
- php strpos与strrpos,PHP开发之 strpos stripos strrpos strripos的区别
- implicit request ?
- 《App后台开发运维与架构实践》第2章 App后台基础技术
- Ipython\Jupyter数据分析工具
- Intellij IDEA创建的Web项目配置Tomcat并启动Maven项目
- C#生成新浪微博短网址 示例源码
- Spring的常见问题及答案