开源推荐

推荐一款一站式性能监控工具(开源项目)

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回收算法-判定一个对象是否可以回收相关推荐

  1. .net 把一个对象赋值给一个参数_Java GC回收算法-判定一个对象是否可以回收

    开源推荐 推荐一款一站式性能监控工具(开源项目) Pepper-Metrics是跟一位同事一起开发的开源组件,主要功能是通过比较轻量的方式与常用开源组件(jedis/mybatis/motan/dub ...

  2. java 二维链表_Java数据结构与算法----数组与链表

    数据类型 1 数据类型介绍 数据类型的分类(按照结构划分):线性结构和非线性结构 线性结构:线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系 线性结构有两种不同的存储结构,即顺序 ...

  3. java 大整数编程_Java编程--RSA算法中的大整数运算

    Java编程–RSA算法中的大整数运算 RSA原理浅析 RSA是利用陷门单向函数实现的,其安全基础依赖于大整数的分解问题的难解性 算法过程 为了加深对RSA算法的了解,接下来通过简单的一个例子来分析一 ...

  4. java常用算法_JAVA编程常用算法——冒泡排序

    一.冒泡排序算法运作的过程 (1)比较相邻的元素.如果第一个比第二个大,就交换他们两个. (2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. (3 ...

  5. java密码学原型算法_java密码学原型算法实现——双线性对.pdf

    java密码学原型算法实现--双线性对 Java 密码学原型算法实现--双线性对 1.背景介绍 如何使用jPBC 库进行双线性群初始化,包括: (1)质数阶双线性群(Prime-Order Bilin ...

  6. java快排原理_Java数据结构与算法——快速排序

    声明:码字不易,转载请注明出处,欢迎文章下方讨论交流. 前言:Java数据结构与算法专题会不定时更新,欢迎各位读者监督.本篇文章介绍排序算法中最常用也是面试中最容易考到的排序算法--快排,包括快排的思 ...

  7. java冒泡遍历对象_Java经典排序算法(冒泡、选择、插入)

    排序算法说明 排序说明 对一序列对象根据某个关键字进行排序. 术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面: 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b ...

  8. java string逆序_java经典入门算法题,java初学者必备

    java经典入门算法题 开头求关注警告 喜欢这样文章的可以关注我,我会持续更新,你们的关注是我更新的动力!需要更多java学习资 料的也可以私信我! 祝关注我的人都:身体健康,财源广进,福如东海,寿比 ...

  9. java 计算矩阵行列式_Java 矩阵行列式算法(非高斯消元)

    最近由于项目任务较少,手上有不少空闲的时间,所以抽空研究了一下矩阵行列式的算法. 先来说说行列式,以下摘自百度百科: 行列式在数学中,是由解线性方程组产生的一种算式.行列式的特性可以被概括为一个多次交 ...

最新文章

  1. oracle 生成 json文件,oracle - PLSQL导入JSON并导出为JSON文件 - 堆栈内存溢出
  2. python for循环九九乘法表_python—用for循环、while循环和一句话打印九九乘法表
  3. 为PHP5.4开启Zend OPCode缓存
  4. Swift中switch比较元组类型
  5. Java中可怕的双重检查锁定成语
  6. C#使用TCP/IP与ModBus进行通讯
  7. redis专题:redis缓存穿透、缓存击穿、缓存雪崩等问题如何解决?
  8. 2021-10-25 Vue异步操作
  9. Pointer Networks简介及其应用(格式化)
  10. (转载)AS3中的mouseEnabled与mouseChildren
  11. 实战揭秘地方性社区门户站运营大法
  12. CSS font-family 各字体一览表
  13. 知了课堂学习笔记一-Django预热-虚拟环境
  14. STM32F103ZET6驱动TM7705(AD7705)代码加心得
  15. EIGamal encryption VS Pairing encryption
  16. 趣味计算机冷知识,那些不为人知的计算机冷知识,还不码起来!
  17. Franka Emika Panda 机械臂环境配置
  18. 【附源码】计算机毕业设计SSM汽车维修服务系统
  19. 在每天一个故事中......
  20. 在前端页面中调用本机的摄像头

热门文章

  1. IO流的体系及FileReader、FileWriter
  2. linux指令查看tomcat日志
  3. android edittext禁止输入特殊字符,Android EditText禁止输入空格和特殊字符
  4. php strpos与strrpos,PHP开发之 strpos stripos strrpos strripos的区别
  5. implicit request ?
  6. 《App后台开发运维与架构实践》第2章 App后台基础技术
  7. Ipython\Jupyter数据分析工具
  8. Intellij IDEA创建的Web项目配置Tomcat并启动Maven项目
  9. C#生成新浪微博短网址 示例源码
  10. Spring的常见问题及答案