人们在考虑GC需要完成的3件事情
那些内存需要回收
什么时候回收
如何回收
在对堆里面存放着Java世界里面几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象那些还活着,那些已经死去(即不在被任何途径使用的对象)
一、哪些内存需要回收
下面是判断对象是否要被回收的方法(即是否要让其“死去”)
1、引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器就减1;任何时刻计数器为0的对象就是不可能再被使用。
2、可达性分析算法
这种算法的基本思想就是通过一系列的称为“GC Roots”的对象到GC Roots没有任何引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话说,就是GC Rotts到这个对象不可达)时,则证明此对象是不可用的。
在java语言中,可作为GC Roots的对象包括下面几种:
1.虚拟机栈(栈帧的本地变量表)中引用的对象。
2.方法区中类静态属性引用的对象。
3.方法区中常量引用的对象
4.本地方法栈中JNI(即一般说的Native方法)引用的对象。
二、什么时候需要回收
判断对象是否存活与“引用”有直接关系
在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
强引用
强引用是代码中最常见的引用,类似Object obj = new Object();只要有强引用存在,垃圾收集器永远不会收掉被引用的对象。
软引用
如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存
弱引用
弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作的时候,无论当前内存是否足够,都会回收掉只被弱引用关联的对象
虚引用
为一个对象设置虚引用的唯一目的就是能在对象被收集器回收的时候收到一个系统的通知。
三、如何回收
垃圾收集算法
1、标记-清除算法
这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需
要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:

从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的
空间而提前触发新的一次垃圾收集动作。
2、复制算法。所用在新生代
为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对
象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:

这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。
很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。
3、标记-整理算法,多用在老年代
为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将
存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:
4、分代收集算法(复制算法和标记-整理算法的结合)
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。
注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。
回收方法区
很多人认为方法区(或者HotSpot虚拟机中的永生代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求在方法区实现垃圾收集,而且在方法区中进行垃圾收集的“性价比”一般比较低:在堆中,尤其是在新生代中,常规应用进行一次垃圾回收一般可以回收70%~95%的空间,而永久代的垃圾收集效率运低于此。
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类
《深入理解java虚拟机》