如果从垃圾收集进行分析,我觉得可以从两个方面进行:
1.如何判断某一个对象可以进行回收
2.在哪些Runtime Data Area进行回收
3.如何进行回收
一.判断某个对象是否可以回收
主流的商用语言,如Java及C#甚至Lisp都是采用GC Roots Tracing(根搜索算法)判断某个对象是否可以进行回收。具体的做法是选定一些可以作为GC Roots的对象,如:
a.JVM Stack的Stack Frame中的局部变量表中引用的对象。
b.Method Area中类的静态属性引用的对象。
c.Method Area中的常量引用的对象。
d.Native Method Area中引用的对象。
如果某一个对象到GC Roots没有Reference Chain,则该对象被判定为可以回收。
大致的回收过程如下:
1.以根搜索算法判定某个对象到GC Roots是否存在Reference Chain,进行第一次标记并判断是否有必要执行finalize()方法。
2.如被判定有必要执行finalize()方法,则进入F-Queue队列中,进行第二次标记,如果在finalize()执行过程中未建立到GC Roots的Reference Chain,则该对象注定要被回收,反之则被移除即将会后的集合。
二.在哪些区域进行回收
分析这个问题需要从分配的角度去理解:
前提,Java Heap 可以分为新生代与老年代
1.大多数情况下,对象在新生代Eden区中分配。
2.大对象直接进入老年代。
3.长期存活的对象进入老年代。
根据以上分配策略,可以知道,回收主要发生在Java Heap中。
是否要对Method Area进行回收?
首先我们知道,HotSpot的Method Area存在垃圾回收。
根据以往的数据分析结果,Method Area发生的垃圾回收效率太低,主要因为以下原因:
Method Area中垃圾回收主要针对废弃的常量和无用的类,回收废弃的常量较为容易,但是回收无用的类却较为麻烦,因为一个类是否需要回收的判定条件太苛刻:
1.该类所有的实例都已经被回收,即Java Heap中不存在该类的任何实例
2.加载该类的ClassLoader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在热河地方通过反射访问该类的方法
当然,可以通过一下几个参数进行控制:
-Xnoclassgc 不回收类
三.如何进行回收
先来看几种垃圾回收算法的原理及优缺点:
1.Mark-Sweep(标记清除)算法
先标记,后清除。
缺点:容易产生大量的不连续内存(内存碎片),这样在分配大对象时较为麻烦。
2.Copying(复制算法)
复制算法的思想:
将内存非为两块,每次只使用一块,当这一块用完了,就将这块内存中存活的对象复制到另外一块内存上,然后将第一块内存中的对象进行一次性回收。
优点:高效
缺点:将内存一份为二,代价较高。
现在的商业虚拟机都使用这种算法对新生代进行回收,如HotSpot对新生代进行回收的方式:
将新生代分为Eden空间、From Survivor空间、To Survivor空间,默认比例为8:1:1
每次回收时将Eden空间与其中一块Survivor空间中存活的对象复制到另外一块Survivor空间上,然后进行清除,这其中还涉及到分配担保的概念
3.Mark-Compact(标记整理)算法
针对老年代的回收算法
每次回收时进行整理,将未被标记的对象(存活对象)移动到一端,然后清理掉边界以外的内存。
4.Generational Collection(分代收集)
分代垃圾回收的思想是针对不同区域的特点,在不同区域应用不同的回收算法。
新生代:Mark-Copying-Sweep
老年代: Mark-Compact-Sweep
Minor GC与Full GC
Minor GC即新生代GC,主要针对Java Heap的新生代,Minor GC较为频繁快速。
Full GC即老年代GC,主要针对Java Heap的老年代,Full GC较Minor GC慢10倍左右。