垃圾回收与算法
如何确定垃圾
引用计数法
在Java中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,一个简单的办法是通过引用计数来判断一个对象是否可以回收。一个对象如果没有任何与之关联的引用,则说明对象不太可能再被用到,那么这个对象就是可回收对象。
可达性分析
为了解决引用计数法的循环引用问题,Java使用了可达性分析的方法。通过一系列的“GC roots”对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则该对象是不可达的。要注意的是,不可达对象不等价可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后如果还是不可达对象,则变为可回收对象,面临回收。
标记清除算法(Mark-Sweep)
最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。这种算法最大的问题就是内存碎片化严重,后续可能发生大对象找不到可利用空间的问题。
复制算法(Copying)
为了解决Mark-Sweep算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小的两块。每次只使用其中一块,当一块内存满后,把还存活的对象复制到另一块内存上,把已使用的内存清掉。这种算法虽然解决了内存碎片化的问题,但是内存空间也被压缩剩一半,而且当存活对象多了时,这种算法的效率也大打折扣。
标记整理算法(Mark-Compact)
结合上面两个算法,标记整理算法是先标记,再把存活对象挪到内存的一端,清理存活对象以外的内存空间的对象。
分代收集算法
分代收集算法,是目前大部分jvm所采用的方法。它的核心思想是把不同生命周期的对象划分到不同的内存区域。一般GC 堆分为新生代和老年代。老年代的特点是被回收的对象比较少,新生代则反之。所以我们可以用不同算法来处理不同的区域。
新生代-用复制算法
因其需要复制的存活对象较少,把新生代分为一块较大的eden空间和两块较小的survivor(From survivor 和To
survivor ),每次使用eden和其中一块survivor空间,当空间不足时,就会发生一次GC进行回收,把存活对象复制到另一块survivor区域中去,当另一块survivor区域没有足够的空间存储某个对象,就会把这个对象放到老年代。当对象在survivor区域中躲过一次GC后,年龄+1,默认情况下年龄大于15的对象会被移到老年代中。
老年代-标记整理(标记清理)算法
老年代每次回收对象较少,所以采用Mark-Compact 算法,不必进行内存复制,直接腾出空闲内存。
分区收集算法:把整个堆分为连续的不同小区间,每个小区间独立使用,独立回收。可以控制一次回收多少小区间,减少gc所产生的的停顿。
GC垃圾收集器的详细介绍
在分代回收算法中,Java虚拟机针对新生代和老年代分别提供了多种不同的垃圾回收器,JDK 1.6中HotSpot虚拟机的垃圾收集器如下: