目录
垃圾回收判定算法
判定算法一:引用计数算法
判定算法二:可达性分析算法
可作为GC Roots的对象
Java中的引用类型
方法区的回收
垃圾回收算法
回收算法一:标记——清除算法
回收算法二:复制算法
传统复制算法
变种复制算法
回收算法三:标记——整理算法
垃圾回收器
回收器一:Serial收集器
回收器二:ParNew收集器
回收器三:Parallel Scavenge收集器
回收器四:Serial Old收集器
回收器五:Parallel Old收集器
回收器六:CMS收集器
CMS分为四个步骤:
CMS的缺点:
回收器七:G1收集器
G1的特点:
G1执行步骤:
在Java堆中存放了几乎所有的对象实例,垃圾回收器在回收前需要检查哪些对象是死的,哪些对象是活的,然后回收那些死的对象。
引用计数算法实现简单,判定效率高,大多数时候都是个不错的算法,他是这样实现的:
给对象添加一个引用计数器,每当有一个地方引用他时,计数器就+1,当引用失效时,计数器-1,那么计数器为0的对象就是没有任何地方引用的对象,可以回收。
但是引用计数算法有个缺点,碰到“相互循环引用”时就不行了。
例如:
class Test{
public Object instance=null;
public static void testGC(){
Test A = new Test();
Test B = new Test();
A.instance = B;
B.instance =A;
}
}
A的成员变量引用B,B的成员变量引用A......
GG
很多主流的语言例如Java、C#等都是使用可达性分析算法来判断对象死活的,这个算法思想是这样的:
以GC Roots作为起点向下搜索,将搜索到的节点连起来,如果一个对象无法连接到GC Roots,则证明此对象不可用。
即使这个对象与其他对象有关联,例如上图object5、6、7之间是联系的,但是他们不能到达GC Roots,不好意思,你们死了。
上面谈了这么多,一直提到引用(reference),我们来了解一下Java中的几种不同的引用类型。
方法区是有垃圾回收的,虽然他收集的远比新生代要少,但是还是存在的。
例如无用的类,废弃的常量,字符串常量等。
而且在大量使用反射、动态代理、CGLib的情况下,方法区的回收是非常有必要的,以防出现内存溢出。
JDK8中方法区(永久代)已被移除,取而代之的是元空间(metaSpace),元空间不再使用JVM虚拟机,而使用本地内存,字符串常量也转移到堆中存放。
根据判定算法判定出需要回收的对象并标记,标记完成后统一回收。
两个缺点:
将内存五五开分成两部分,只使用其中一半,当内存快满时触发垃圾回收,将这一半内存中还存活的对象复制到另一块内存上去并整齐排好~不产生内存碎片,然后再清理掉之前的那一半内存。
现在的大多数虚拟机都使用的是变种的复制算法来回收新生代,因为新生代大部分对象都是“朝生夕死”,回收率很高,所以不用五五开划分,而使用8:1:1的比例来划分。
当垃圾回收时,会将Eden区和From Survivor区还存活的对象复制到To Survivor区中,然后清理到Eden和From Survivor,最后把To Survivor当做新的From Survivor,From Survivor作为新的To Survivor。
由于复制算法需要牺牲一定内存空间,而老年代中的大多数对象回收率不高,甚至出现100%存活的极端情况,所以老年代一般不使用复制算法,而使用标记——整理算法。
标记——整理算法和标记——清除算法差不多,多了一个将其整理好的功能,以免出现内存碎片影响大对象分配。
Serial收集器是最基本、发展时间最长的收集器,这是一个单线程收集器,也就是说在serial执行的时候需要暂停其他所有线程(stop the world)。
图中出现的Safepoint名为安全点,意思是:因为程序不能随时可以停下来进行GC,所以设置一个安全点,等所有的线程都跑到这个安全点的时候,再进行GC。
ParNew其实就是Serial的多线程版本,使用多个线程来完成回收工作,他的其他行为包括控制参数和Serial几乎是一模一样的。
Parallel Scavenge收集器是一个新生代收集器,也是使用复制算法,也是并行多线程收集器。
Parallel Scavenge收集器的特点是:它的关注点和其他收集器不同,其他收集器关注的是更少的STOP THE WORLD,尽量减少停顿时间,而Parallel Scavenge收集器关注的是吞吐量,吞吐量提的是运行真正的用户代码的时间与总时间的比值。可以理解为Parallel Scavenge收集器是为了减少垃圾收集所占运行总时间的比例。
越需要用户交互的程序越需要停顿时间短,来保证良好的反应速度,而高吞吐量则可以高效率的利用CPU,尽快完成程序运算任务,适合后台运算(用户交互少)。
Serial Old收集器是Serial的老年代版本,专门用来回收老年代,同样,它也是单线程收集器,使用的是标记——整理算法。
由于Parallel Scavenge新生代收集器只能和Serial Old收集器配合工作,导致它的吞吐量优势无法发挥出来(Serial Old是单线程收集器,会拖累Parallel Scavenge),所以Parallel Old出现后,Parallel Scavenge+Parallel Old的组合可以完美的发挥高吞吐量的优点。
CMS(concurrent mark sweep)的特点是停顿时间短,追求极致相应速度。
从名字就可以看出来,CMS基于标记——清楚算法,并且是多线程并发收集器。
初始标记:仅仅只标记一下GC Roots能关联到的对象,时间很短;
并发标记:与用户线程同时执行,标记对象,持续时间较长;
重新标记:为了修正并发期间因程序继续运行导致标记产生变动的那些对象的标记记录;
并发清除:与用户线程同时执行,清除对象,持续时间较长;
G1收集器可以说是现在最先进的收集器之一。
个人学习总结,有不正确之处望指正。
参考资料:《深入理解JVM虚拟机》