-
2014-08-04 22:59:33
1.根搜索算法
Java使用根搜索算法回收垃圾,该算法的基本原理:定义一系列名为GC Roots的对象作为起点,从起点向下搜索,搜索所走过的路径称为引用链。
当一个对象到GC Roots没有任何引用链相连,则说明该对象不可用,这时Java虚拟机可以对这些对象进行回收。
Java虚拟机将以下对象定义为 GC Roots :
- Java虚拟机栈中引用的对象:比如方法里面定义这种局部变量 User user= new User();
- 静态属性引用的对象:比如 private static User user = new User();
- 常量引用的对象:比如 private static final User user = new User();
- 本地方法栈中引用的对象
2.引用的定义
2.1 强引用Strong Reference
Object obj = new Object();
这里obj 就是引用,new Object() 对象实例存储在堆内存,obj引用的是对象实例的内存地址。如果执行 obj = null, 那么看起来 new Object() 这个对象实例没有被任何引用持有。
由此看来:引用要么存在,要么不存在。如果没有任何引用持有对象,那么对象可以被JVM回收了。
但有一些缓存系统,希望这些对象不要那么快被回收,因此在JDK1.2之后又定义了另外3种引用类型。
2.2 软引用 Soft Reference
用来描述可用,但不是必须存活的对象。
在内存溢出之前,虚拟机尝试对这些对象进行回收。如果回收之后内存还是不够才会出现内存溢出情况。
2.3 弱引用 Weak Reference
也是描述非必须的对象,但它的引用比软引用还要弱一些。
被弱引用关联的对象只能生存到下一次垃圾回收发生之前。
只要垃圾回收机制在工作,无论内存是否足够,都会回收掉这部分对象。
2.4 虚引用 Phantom Reference
最弱的引用关系,一个对象是否被虚引用关联,完全不对其生存周期构成影响。
也不能通过一个虚引用获取到一个对象实例!
3. 根搜索算法之圾回收执行机制
前面介绍了根搜索算法和引用的定义,我们知道【不可达的对象】,就是JVM回收的重点对象。
但即便引用链不可达,也并不意味着该对象一定会被回收,因为回收要经历两次标记过程!
第一次标记:对象进行根搜索之后,如果发现没有与GC Roots 相连接的引用链,就会被第一次标记并进行筛选
所谓筛选,就是检查此对象是否有必要执行finalize方法,如果对象定义了该方法并且没有执行过。
那么该对象就会被放入到一个队列F-Queue,随后会有一个低优先级的线程去执行这个队列里面对象的finalize方法
第二次标记:JVM 将对F-Queue队列里面的对象进行第二次标记。
如果对象不想被回收,那么就得在finalize方法里面拯救自己,否则,这些对象就真的会被回收
看以下代码示例就明白垃圾回收的基本过程
package reference.test; /** * * @author yli * */ public class GCRootsTest { private static GCRootsTest obj; protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize方法被执行!"); obj = this; } public static void main(String[] args) throws InterruptedException { obj = new GCRootsTest(); obj = null; System.gc(); Thread.sleep(500); if (null != obj) { System.out.println("1-obj还存活着!"); } else { System.out.println("1-obj已经死了"); } obj = null; System.gc(); Thread.sleep(500); if (null != obj) { System.out.println("2-obj还存活着!"); } else { System.out.println("2-obj已经死了"); } } }
更多相关内容 -
根搜索算法
2013-06-05 22:51:393.2.2 根搜索算法 在主流的商用程序语言中(Java和C#,甚至包括前面提到的古老的Lisp),都是使用根搜索算法(GC Roots Tracing)判定对象是否存活的。这个算法的基本思路就是通过一系列的名为“GC Roots”的...3.2.2 根搜索算法
在主流的商用程序语言中(Java和C#,甚至包括前面提到的古老的Lisp),都是使用根搜索算法(GC Roots Tracing)判定对象是否存活的。这个算法的基本思路就是通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如图3-1所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。
在Java语言里,可作为GC Roots的对象包括下面几种:
虚拟机栈(栈帧中的本地变量表)中的引用的对象。
方法区中的类静态属性引用的对象。
方法区中的常量引用的对象。
本地方法栈中JNI(即一般说的Native方法)的引用的对象。
图3-1 根搜索算法判定对象是否可回收
3.2.3 再谈引用
无论是通过引用计数算法判断对象的引用数量,还是通过根搜索算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。在JDK 1.2之前,Java中的引用的定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这种定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。
在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。
强引用就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
软引用用来描述一些还有用,但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。
弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。
虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。
3.2.4 生存还是死亡?
在根搜索算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为F-Queue的队列之中,并在稍后由一条由虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己—只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那它就真的离死不远了。从代码清单3-2中我们可以看到一个对象的finalize()被执行,但是它仍然可以存活。
代码清单3-2 一次对象自我拯救的演示
- /**
- * 此代码演示了两点:
- * 1.对象可以在被GC时自我拯救。
- * 2.这种自救的机会只有一次,因为一个对象的finalize
()方法最多只会被系统自动调用一次 - * @author zzm
- */
- public class FinalizeEscapeGC {
- public static FinalizeEscapeGC SAVE_HOOK = null;
- public void isAlive() {
- System.out.println("yes, i am still alive :)");
- }
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- System.out.println("finalize mehtod executed!");
- FinalizeEscapeGC.SAVE_HOOK = this;
- }
- public static void main(String[] args) throws Throwable {
- SAVE_HOOK = new FinalizeEscapeGC();
- //对象第一次成功拯救自己
- SAVE_HOOK = null;
- System.gc();
- // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它
- Thread.sleep(500);
- if (SAVE_HOOK != null) {
- SAVE_HOOK.isAlive();
- } else {
- System.out.println("no, i am dead :(");
- }
- // 下面这段代码与上面的完全相同,但是这次自救却失败了
- SAVE_HOOK = null;
- System.gc();
- // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它
- Thread.sleep(500);
- if (SAVE_HOOK != null) {
- SAVE_HOOK.isAlive();
- } else {
- System.out.println("no, i am dead :(");
- }
- }
- }
运行结果:
- finalize mehtod executed!
- yes, i am still alive :)
- no, i am dead :(
从代码清单3-2的运行结果可以看到,SAVE_HOOK对象的finalize()方法确实被GC收集器触发过,并且在被收集前成功逃脱了。
另外一个值得注意的地方就是,代码中有两段完全一样的代码片段,执行结果却是一次逃脱成功,一次失败,这是因为任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,因此第二段代码的自救行动失败了。
需要特别说明的是,上面关于对象死亡时finalize()方法的描述可能带有悲情的艺术色彩,笔者并不鼓励大家使用这种方法来拯救对象。相反,笔者建议大家尽量避免使用它,因为它不是C/C++中的析构函数,而是Java刚诞生时为了使C/C++程序员更容易接受它所做出的一个妥协。它的运行代价高昂,不确定性大,无法保证各个对象的调用顺序。有些教材中提到它适合做“关闭外部资源”之类的工作,这完全是对这种方法的用途的一种自我安慰。finalize()能做的所有工作,使用try-finally或其他方式都可以做得更好、更及时,大家完全可以忘掉Java语言中还有这个方法的存在。
3.2.5 回收方法区
很多人认为方法区(或者HotSpot虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区进行垃圾收集的“性价比”一般比较低:在堆中,尤其是在新生代中,常规应用进行一次垃圾收集一般可以回收70%~95%的空间,而永久代的垃圾收集效率远低于此。
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量与回收Java堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果在这时候发生内存回收,而且必要的话,这个“abc”常量就会被系统“请”出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是“无用的类”:
该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
加载该类的ClassLoader已经被回收。
该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而不是和对象一样,不使用了就必然会回收。是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class及-XX:+TraceClassLoading、 -XX:+TraceClassUnLoading查看类的加载和卸载信息。
在大量使用反射、动态代理、CGLib等bytecode框架的场景,以及动态生成JSP和OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。
-
GC根搜索算法
2018-07-24 20:45:51JVM会采用一种算法来进行GC回收,即根搜索算法。它的处理方式就是,设立若干种根对象,当任何一个根对象到某一个对象均不可达时,则认为这个对象是可以被回收的。 就拿上图来说,ObjectD和ObjectE是互相关联的...JVM会采用一种算法来进行GC回收,即根搜索算法。它的处理方式就是,设立若干种根对象,当任何一个根对象到某一个对象均不可达时,则认为这个对象是可以被回收的。
就拿上图来说,ObjectD和ObjectE是互相关联的,但是由于GC roots到这两个对象不可达,所以最终D和E还是会被当做GC的对象,上图若是采用引用计数法,则A-E五个对象都不会被回收。
说到GC roots(GC根),在JAVA语言中,可以当做GC roots的对象有以下几种:
1、虚拟机栈中的引用的对象。
2、方法区中的类静态属性引用的对象。
3、方法区中的常量引用的对象。
4、本地方法栈中JNI的引用的对象。
第一和第四种都是指的方法的本地变量表,第二种表达的意思比较清晰,第三种主要指的是声明为final的常量值。
-
可达性分析算法(根搜索算法GCRoots)
2019-07-30 13:15:07根搜索算法 概念 根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则...转自:https://www.jianshu.com/p/b79fe594eed3
根搜索算法
概念
根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
那么问题又来了,如何选取GCRoots对象呢?在Java语言中,可以作为GCRoots的对象包括下面几种:
(1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
(2). 方法区中的类静态属性引用的对象。
(3). 方法区中常量引用的对象。
(4). 本地方法栈中JNI(Native方法)引用的对象。
下面给出一个GCRoots的例子,如下图,为GCRoots的引用链。
image.png
image.png
根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
从上图,reference1、reference2、reference3都是GC Roots,可以看出: reference1-> 对象实例1; reference2-> 对象实例2; reference3-> 对象实例4; reference3-> 对象实例4 -> 对象实例6; 可以得出对象实例1、2、4、6都具有GC Roots可达性,也就是存活对象,不能被GC回收的对象。 而对于对象实例3、5直接虽然连通,但并没有任何一个GC Roots与之相连,这便是GC Roots不可达的对象,这就是GC需要回收的垃圾对象。
-
JVM垃圾回收根搜索的三种算法
2017-06-30 19:38:01JDK1.2之后,垃圾回收采用跟搜索算法,有三种算法:1、标记清除算法 2、复制算法 3、标记整理算法 1、标记清除算法 从根集合进行扫描,对存活的对象进行标记,标记完后,再重新扫描整个空间,对未标记的对象进行... -
java的回收机制----根搜索算法
2016-03-27 11:13:42根搜索算法的基本思路是通过一系列的“GC Roots”的对象作为起始点,从这些节点开始往下搜索,搜索的走过的路径称为引用链,当一个对象到“GC Roots”没有引用链可达时(也就是用图论的话说就是从GC Roots到这个对象... -
离散控制Matlab代码-GRPF:全局复杂根和极点查找算法
2021-05-23 07:50:26离散控制Matlab代码GRPF:全局复杂根和极点查找算法 编程语言:MATLAB 计划目标 GRPF算法的目的是寻找固定区域中函数的所有零点和极点。 可以分析各种各样的解析函数,并且可以考虑任何形状的搜索区域。 所附示例... -
经典搜索算法总结
2021-05-30 21:09:51前言0x01 搜索问题的形式化0x02 树搜索和图搜索0x03 搜索算法的评估0x04 盲目搜索算法0x04.01 宽度优先搜索算法BFS0x04.02 一致代价搜索算法UCS0x04.03 深度优先搜索算法DFS 前言 搜索问题是在解决各类问题时不可... -
求根问题的量子计算算法
2021-03-28 12:45:07求根问题是计算数论中的一个困难性问题,为了提高求根问题的求解效率和扩大量子计算的应用...在没有使用任何可提高搜索效率的经典策略的情况下,RF-Grover算法能在O(M/k)步内以至少1/2的概率求出求根问题k个解中的一个解. -
分布式P2P网络中基于方向搜索算法研究
2021-01-31 12:53:02针对Flooding算法及其改进算法的理念提出了P2P 网络中基于方向的搜索算法,该算法动态生成一棵以搜索源点为根的搜索树,在每一次的搜索过程中,每个节点都能沿着搜索方向进行,这样可以避免节点被重复地搜索。... -
酉变换步进搜索求根MUSIC算法及其FPGA实现
2018-01-03 11:56:12论文,酉变换步进搜索求根MUSIC算法及其FPGA实现,对初学者有用 -
智能优化算法:天牛须搜索算法
2020-11-23 16:51:25智能优化算法:天牛须搜索算法一、前言二、算法简介三、算法原理3.1 算法模型3.2 算法流程总结 一、前言 天牛须算法 (Beetle Antennae search algorithm, BAS) 是由Jiang... -
JVM垃圾回收对象判定标准之可达性分析算法(根搜索法)
2020-03-10 17:58:19可达性分析算法也称之为根搜索法,其原理是: 从GC Roots节点(起始点)出发向下搜索,如果没有任何引用链相连(即GC Roots到对象不可达),则证明此对象不可用。 什么是引用链? 从起始点向下搜索所走过的路径被称为... -
几种常见的搜索算法
2019-06-05 22:22:03广度优先搜索(BFS) 深度优先搜索(DFS) 爬山法(Hill Climbing) 最佳优先算法(Best-first search strategy) 回溯法(Backtracking) 分支限界算法(Branch-and-bound Search Algorithm) A*算法 广度... -
GC(2.基本算法)
2016-10-03 15:27:282> 根搜索算法(GC Root)根搜索算法在主流的商用程序语言中(Java和C#,甚至包括前面提到的古老的Lisp),都是使用根搜索算法(GC Roots Tracing)判定对象是否存活的。这个算法的基本思路就是通过一系列的名为... -
求根MUSIC算法
2014-01-16 04:03:26求根MUSIC算法,不同于谱搜索MUSIC,可以给出角度 -
常见的几种搜索算法
2020-06-10 17:54:13广度优先搜索算法(Breadth-First Search,BFS)是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。 -
人工智能之搜索算法
2020-03-21 21:41:32通过搜索来解决问题 文章目录通过搜索来解决问题1. 什么是算法?2. 什么是搜索?3. 搜索算法3.1 如何做路径规划?3.2 搜索过... -
c++实现二分搜索算法分析与设计分治算法
2010-04-18 21:03:19用C++实现的二分搜索,改写了算法设计与分析课后的题目。按照《算法分析与设计》书上的例题的算法实现的。采用了分治法的思想。 -
广度优先搜索算法(java实现)
2021-01-25 17:58:57广度优先搜索算法使用了一个队列来保存所有已经被标记过但其邻接表还未被检查过的顶点。先将起点加入队列,然后重复以下步骤直到队列为空: 取队列中的下一个顶点 v 并标记它; 将与 v 相邻的所有未被标记过的顶点... -
图解二叉树搜索算法
2016-09-09 16:30:05二叉查找树(Binary Search Tree),也称二叉搜索树,是指一棵空树或者具有下列性质的二叉树: 任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 任意节点的右子树不空,则右子树上所有... -
深度优先搜索(DFS)与广度优先搜索(BFS)算法详解
2021-10-02 23:16:421.广度优先搜索算法 1.1.前言 和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图中所有顶点进行访问,且仅访问一次。 但是图的遍历相对树而言要更为复杂。因为图中的任意顶点都可能与其他顶点相邻... -
树搜索算法 及 最优解
2020-03-24 16:03:34一、深度优先搜索与广度优先搜索算法有何区别 深度优先搜索算法不全部保留结点,扩展完的结点从数据存储结构栈中弹出删去,在栈中存储的结点数就是解空间树的深度,因此它占用空间较少。所以,当搜索树的结点较多,... -
【算法概论】搜索算法
2019-06-07 21:25:25搜索算法 搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。(百度百科) 搜索算法需要在指数量级的繁杂的可选对象中搜索某个具有特定性质的解,... -
深度优先搜索算法(DFS)原理及示例详解
2020-08-31 08:20:14事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次. 举例说明之:下图是一个无向图,如果我们从A... -
求根问题的量子计算算法 (2015年)
2021-05-14 11:44:52求根问题是计算数论中的一个困难性问题,为了提高求根问题的求解效率和扩大量子计算的... 在没有使用任何可提高搜索效率的经典策略的情况下,RF-Grover算法能在O( M/k)步内以至少1/2的概率求出求根问题k个解中的一个解. -
【算法】极小极大值搜索算法MinimaxSearch————井字棋的应用
2020-11-26 11:36:53极小极大值搜索算法MinimaxSearch 算法描述 极小极大值搜索算法,MinimaxSearch算法,是一种零和算法,是用来最小化对手的利益,最大化自己的利益的算法。 极小极大之搜索算法常用于棋类游戏等双方较量的游戏和程序... -
C++算法之深度优先搜索算法详解
2018-09-28 20:23:411.深度优先搜索算法 深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行... -
人工智能实验一:搜索算法问题求解
2019-04-13 12:15:39能够运用计算机语言实现搜索算法; 应用搜索算法解决实际问题(如罗马尼亚问题); 学会对算法性能的分析和比较 二、实验硬件软件平台 硬件:计算机 软件:操作系统:WINDOWS 应用软件:C,Java或者MATLAB ... -
动态二进制树搜索算法_二进制搜索树
2020-07-27 13:18:50动态二进制树搜索算法 二进制搜索树 (Binary Search Tree) A binary search tree is a useful data structure for fast addition and removal of data. 二进制搜索树是用于快速添加和删除数据的有用数据结构。 It...