一,引用计数算法(Reference Counting)
介绍:给对象天极爱一个引用计数器,每当一个地方引用它时,数据器加1;当引用失效时,计数器减1;计数器为0的即可被回收。
优点:实现简单,判断效率高。
缺点:很难解决对象之间的相互循环引用(objA.instance = objB; objB.instance = objA)的问题,所以java语言并没有选用引用计数法管理内存。
二,根搜索算法(GC Root Tracing)
Java和C#都是使用根搜索算法来判断对象是否存活。通过一系列的名为“GC Root”的对象作为起始点,从这些节点开始向下搜索,搜索所有走过的路径称为引用链(Reference Chain),当一个对象到GC Root没有任何引用链相连时(用图论来说就是GC Root到这个对象不可达时),证明该对象是可以被回收的。
在Java中哪些对象可以成为GC Root?
虚拟机栈(栈帧中的本地变量表)中的引用对象
方法区中的类静态属性引用的对象
方法区中的常量引用对象
本地方法栈中JNI(即Native方法)的引用对象
三,标记-清除算法(Mark-Sweep)
这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记那些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:
1.效率不高,标记和清除的效率都很低;
2.会导致大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前出发一次GC动作。
四,复制算法(Copying)
为了解决效率问题,复制算法将可用内存按容量划分相等的量部分,然后每次只使用其中的一块,当第一块内存用完时,就将还存货的对象复制到第二块内存上,然后一次性清除完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的代价太高,每次基本上都要浪费一块内存。
于是将该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大的那份内存叫Eden(伊甸)区,其余两块较小的内存叫Survivor区,每次都会先使用Eden区,若Eden区满,就将对象赋值到第二块内存上,然后清除Eden区,如果此时存货的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制到老年代中(Java堆又分为新生代和老年代)。
五,标记-整理算法(Mark-Compact)
该算法是为了解决标记-清除产生大量内存碎片的问题;当对象存活较高时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候将可回收的对象移动一端,然后清除掉边界以外的对象,这样就不会产生内存碎片。
六,分代收集算法(Generational Collection)
根据对象的存活周期的不同将内存划分为几块,一般就分为新生代和老年代,根据各个年代的特点采用不同的收集算法。新生代(少量存活)用复制算法,老年代(对象存活率高)“标记-整理”算法
补充:分代划分内存介绍
整个JVM内存总共划分为三代:年轻带(Young Generation)、老年代(Old Generation)、持久代(Permanent Generation)
1,年轻代:所有新生成的对象首先都放在年轻代内存中。年轻代的目标就是尽可能快速地收集掉那些生命周期短的对象。年轻代内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另一块Survivor空间上,最后清理Eden和刚才用过的survivor空间。
2,老年代:在年轻代经历了N次GC后,仍然存活的对象,就会被放到老年代中。因此可以认为老年代存放的都是一些生命周期较长的对象。
3,持久代:基本固定不变,用于存放静态文件,例如Java类和方法。持久代对GC没有显著的影响。持久代可以通过-XX:MaxPermSize=<N> 进行设置。