Open xzhuz opened 4 years ago
https://hshanx.github.io/gKqr1JUi4/
困而学,学而知 好记性不如烂笔头
困而学,学而知
好记性不如烂笔头
紧接着上一篇JVM老生常谈之运行时数据区,我们已经连接了Java虚拟机几个运行时数据区,今天我们接着来讲讲Java虚拟机的几个重要的内存回收算法。本文所涉及的知识基本都基于HotSpot虚拟机。
首先,我们先来认识两个普遍用于判断对象是否被引用的算法:引用计数法和可达性算法。
引用计数法其实很简单,如果对象的计数器为0,就说明对象不再被引用,否则就是再被引用。
可达性算法则是通过判断对象是否能够被GC ROOT访问到来判断对象是否还在被引用。
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器就减1。任何时刻计数器为0的对象就是不再被使用的。
但是引用计数法其实是很难解决对象之间相互循环引用的问题,所以,Java虚拟机里面没有选用引用计数算法来管理内存。
我们用下面的例子来验证一下上面说的是否正确。
public class ReferenceCountingGC { public Object instance = null; private static final int _1MB = 1024 * 1024; /** * 这个成员属性的唯一意思就是占点内存,以便能在GC日志中看清楚是否被回收过 */ private byte[] bigSize = new byte[2* _1MB]; public static void testGC(){ ReferenceCountingGC objA = new ReferenceCountingGC(); ReferenceCountingGC objB = new ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; //假设在这行发生gc,objA和ObjB是否能被回收 System.gc(); } }
可以看到,其实也是有被回收了,也就是意味着虚拟机并没有因为两个对象相互引用就不回收他们。侧面说明虚拟机并不是通过引用计数法来判断对象是否存活。
虽然可引用计数法很简单,也经常被提及,但是HotSpot虚拟机却不是用这个算法来判断对象是否继续被引用,而是使用下面要介绍的算法:可达性分析算法。
在主流商用程序语言的主流视线中,都是称通过可达性分析来判定对象是否存活的。
算法的基本思路就是通过一系列称为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径被称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象不可用的。
GC Roots
上图中,Object1~Object4都可以被GC Root访问到,而Object5~Object7都不可以被访问到,这也就是说。也就是说,Object5、6、7这三个对象就是不可达的,下次垃圾回收的时候,可能就会被回收掉。
其实并不是所有的对象都可以作为GC Roots的对象,只有下列的对象可以作为GC Roots的对象。
既然是引用计数法,那肯定就有各种引用,下面来说说一些引用。
JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种。
JDK1.2之前,只有被引用和没有被引用两种状态
Object obj = new Object()
要宣告一个对象死亡,至少要经历两次标记过程:
第一次标记: 如果想在进行可达性分析发生没有 GCRoots 相连接的引用连,那么它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为没有必要执行。否者就是有必要执行,则会被放到一个F-Queue队列。
finalize()
第二次标记:finalize()方法是对象跳脱死亡命运的最后一次机会,稍后GC将对F-Queue中对象进行第二次小规模标记,如果对象要在finalize()中称重拯救自己-只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时她将被一簇“即将回收”的集合。
简单的对上面做一个总结,在JVM中判断一个对象是都需要回收有两种算法:引用计数法和可达性算法。引用计数法是通过判断引用的计数器的值是否为0来确认回收与否。这种算法听起来很简单,但是存在一个缺陷,就可以可能存在循环引用的情况。
还有一种就是可达性算法,可达性算法是通过判断引用能够被 GC Roots 访问到来确认回收与否。能被称为GC Roots对象也是有条件的主要有四种:虚拟机栈中引用的对象、方法中类静态属性引用的对象、方法中常量引用的对象和本地方法栈(native方法)中JNI引用的对象。
引用分为四种类型:强引用、软引用、弱引用和虚引用。
https://hshanx.github.io/gKqr1JUi4/
紧接着上一篇JVM老生常谈之运行时数据区,我们已经连接了Java虚拟机几个运行时数据区,今天我们接着来讲讲Java虚拟机的几个重要的内存回收算法。本文所涉及的知识基本都基于HotSpot虚拟机。
首先,我们先来认识两个普遍用于判断对象是否被引用的算法:引用计数法和可达性算法。
引用计数法其实很简单,如果对象的计数器为0,就说明对象不再被引用,否则就是再被引用。
可达性算法则是通过判断对象是否能够被GC ROOT访问到来判断对象是否还在被引用。
引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器就减1。任何时刻计数器为0的对象就是不再被使用的。
但是引用计数法其实是很难解决对象之间相互循环引用的问题,所以,Java虚拟机里面没有选用引用计数算法来管理内存。
我们用下面的例子来验证一下上面说的是否正确。
可以看到,其实也是有被回收了,也就是意味着虚拟机并没有因为两个对象相互引用就不回收他们。侧面说明虚拟机并不是通过引用计数法来判断对象是否存活。
虽然可引用计数法很简单,也经常被提及,但是HotSpot虚拟机却不是用这个算法来判断对象是否继续被引用,而是使用下面要介绍的算法:可达性分析算法。
可达性分析算法
在主流商用程序语言的主流视线中,都是称通过可达性分析来判定对象是否存活的。
算法的基本思路就是通过一系列称为
GC Roots
的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径被称为引用链,当一个对象到GC Roots
没有任何引用链相连时,则证明此对象不可用的。上图中,Object1~Object4都可以被GC Root访问到,而Object5~Object7都不可以被访问到,这也就是说。也就是说,Object5、6、7这三个对象就是不可达的,下次垃圾回收的时候,可能就会被回收掉。
其实并不是所有的对象都可以作为GC Roots的对象,只有下列的对象可以作为GC Roots的对象。
可以作为GC Roots的对象
既然是引用计数法,那肯定就有各种引用,下面来说说一些引用。
强引用、软引用、弱引用和虚引用
JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种。
Object obj = new Object()
这类的引用,只要强引用还存在,垃圾收集器永远不会回收被引用的对象要宣告一个对象死亡,至少要经历两次标记过程:
第一次标记: 如果想在进行可达性分析发生没有 GCRoots 相连接的引用连,那么它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行
finalize()
方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为没有必要执行。否者就是有必要执行,则会被放到一个F-Queue队列。第二次标记:
finalize()
方法是对象跳脱死亡命运的最后一次机会,稍后GC将对F-Queue中对象进行第二次小规模标记,如果对象要在finalize()中称重拯救自己-只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时她将被一簇“即将回收”的集合。简单总结
简单的对上面做一个总结,在JVM中判断一个对象是都需要回收有两种算法:引用计数法和可达性算法。引用计数法是通过判断引用的计数器的值是否为0来确认回收与否。这种算法听起来很简单,但是存在一个缺陷,就可以可能存在循环引用的情况。
还有一种就是可达性算法,可达性算法是通过判断引用能够被 GC Roots 访问到来确认回收与否。能被称为GC Roots对象也是有条件的主要有四种:虚拟机栈中引用的对象、方法中类静态属性引用的对象、方法中常量引用的对象和本地方法栈(native方法)中JNI引用的对象。
引用分为四种类型:强引用、软引用、弱引用和虚引用。