在这篇文章中《Jvm运行时数据区》介绍了Java内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈,3个区域随着线程的生存而生存的。内存分配和回收都是肯定的。随着线程的结束内存天然就被回收了,所以不须要考虑垃圾回收的问题。而Java堆和方法区则不同,各线程共享,内存的分配和回收都是动态的。所以垃圾收集器所关注的都是这部份内存。html
接下来咱们就讨论Jvm是怎么回收这部份内存的。在进行回收前垃圾收集器第一件事情就是肯定哪些对象还存活,哪些已经死去。下面介绍两种基础的回收算法。java
给对象添加一个引用计数器,每当有一个地方引用它时计数器就+1,当引用失效时计数器就-1,。只要计数器等于0的对象就是不可能再被使用的。算法
此算法在大部分状况下都是一个不错的选择,也有一些著名的应用案例。可是Java虚拟机中是没有使用的。数据结构
优势:实现简单、判断效率高。post
缺点:很难解决对象之间循环引用的问题。例以下面这个例子url
Object a = new Object(); Object b = new Object(); a=b; b=a; a=b=null; //这样就致使gc没法回收他们。
经过一系列的称为“GC Roots”的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有使用任何引用链时,则说明该对象是不可用的。spa
主流的商用程序语言(Java、C#等)在主流的实现中,都是经过可达性分析来断定对象是否存活的。线程
经过下图来清晰的感觉gc root与对象展现的联系。所示灰色区域对象是存活的,Object5/6/7均是可回收的对象htm
在Java语言中,可做为GC Roots 的对象包括下面几种对象
优势:更加精确和严谨,能够分析出循环数据结构相互引用的状况;
缺点:实现比较复杂、须要分析大量数据,消耗大量时间、分析过程须要GC停顿(引用关系不能发生变化),即停顿全部Java执行线程(称为"Stop The World",是垃圾回收重点关注的问题)。
在jdk1.2以后,Java对引用的概念进行了扩充,整体分为4类:强引用、软引用、弱引用、虚引用,这4中引用强度依次逐渐减弱。
宣告一个对象死亡,至少要经历两次标记。
若是对象进行可达性分析算法以后没发现与GC Roots相连的引用链,那它将会第一次标记而且进行一次筛选。
筛选条件:判断此对象是否有必要执行finalize()方法。
筛选结果:当对象没有覆盖finalize()方法、或者finalize()方法已经被JVM执行过,则断定为可回收对象。若是对象有必要执行finalize()方法,则被放入F-Queue队列中。稍后在JVM自动创建、低优先级的Finalizer线程(可能多个线程)中触发这个方法;
GC对F-Queue队列中的对象进行二次标记。
若是对象在finalize()方法中从新与引用链上的任何一个对象创建了关联,那么二次标记时则会将它移出“即将回收”集合。若是此时对象还没成功逃脱,那么只能被回收了。
finalize()是Object类的一个方法、一个对象的finalize()方法只会被系统自动调用一次,通过finalize()方法逃脱死亡的对象,第二次不会再调用;
特别说明:并不提倡在程序中调用finalize()来进行自救。建议忘掉Java程序中该方法的存在。由于它执行的时间不肯定,甚至是否被执行也不肯定(Java程序的不正常退出),并且运行代价高昂,没法保证各个对象的调用顺序(甚至有不一样线程中调用)。
永久代的垃圾收集主要分为两部份内容:废弃常量和无用的类。
回收废弃常量与Java堆的回收相似。下面举个栗子说明
假如一个字符串“abc” 已经进入常量池中,但当前系统没有一个string对象是叫作abc的,也就是说,没有任何string对象的引用指向常量池中的abc常量,也没用其余地方引用这个字面量。若是这是发生内存回收,那么这个常量abc将会被清理出常量池。常量池中的其余类(接口)、方法、字段的符号引用也与此相似。
须要同时知足下面3个条件的才能算是无用的类。
虚拟机能够对同时知足这三个条件的类进行回收,但不是必须进行回收的。是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制。
----对《深刻理解Java虚拟机》第3章垃圾收集器与内存分配策略 3.2小节总结。接下来总结3.3小结垃圾收集算法。