绝大部分内存回收时发生在JVM的堆(用来存储对象实例)上的
1.GC回收判断对象已死的算法
1)引用计数算法:
给对象中添加个引用计数器,每当有地方引用它时,计数器+1;失去引用时,计数器-1;
优点:判定效率高,在大部分情况下都是一个不错的算法。
缺点:很难解决对象之间的相互引用的问题。
ps:JVM中自带的HotSpot并没有使用应勇计数算法,而是使用了可达性分析法算法
public class ReferenceCountingGC { public Object instance = null; private static int _1MB = 1024*1024; private byte[] bigSize = new byte[2*_1MB]; public static void main(String[] args) { ReferenceCountingGC o1 = new ReferenceCountingGC(); ReferenceCountingGC o2 = new ReferenceCountingGC(); o1.instance = o2; o2.instance = o1; o1=null; o2=null; System.gc(); } }
2)可达性分析算法
通过一系列的称为“GC Roots”的对象作为起点,从这些节点向下搜索,搜索走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明这个对象是不可用的,可以被回收的。
在Java语言中,可作为GC Roots的对象包括下面几种:
——虚拟机栈中引用的对象
——方法区中静态属性、常量引用的对象
——本地方法中JNI(Native方法)引用的对象
2.引用(reference)
无论是引用计数法还是可达性算法,判定对象的存活都与引用有关。在JDK 1.2之后,java对引用的概念进行了扩充,分为4个引用类型:
——强引用:类似“Object obj = new Object()”的引用
——软引用:在系统要发生溢出异常之前,会把软引用对象列进回收范围进行二次回收。SoftReference来实现软应用
——弱引用:弱引用关联的对象只能生存到下一次垃圾收集发生之前。WeakReference类来实现弱引用
——虚引用:它是最弱的一种引用关系,完全不会对对象的生命周期造成影响,也无法通过需引用来取得一个对象的实例,唯一的用处就是能在这个对象被垃圾收集器回收时收到一个系统通知。。PhantomReference来实现虚引用。
3.回收方法区(永久代)
永久代的垃圾收集主要回收两部分内容:废弃常量与无用的类
——废弃常量:只要当前系统无任何一个对象引用常量,也没有其他地方引用了,如果这时候发生内存回收,且有必要的话,这个常量就会被清理出常量池。
——无用的类:(1)该类所有实例化的类已经被回收。(2)加载该类的classLoader已经被回收。(3)该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问到该类的方法。
是否对永久代进行回收,HotSpot虚拟机提供了以下参数进行控制:
-Xnoclassgc参数:是否对类进行回收
-verbose[:class|gc|jni] 在输出设备上显示虚拟机运行信息。:
-XX:+TraceClassLoading:查看类加载信息(可在Product版的虚拟机上进行使用)
-XX:+TraceClassUnLoading:查看类卸载信息(需要FastDebug版虚拟机的支持)
4.垃圾收集算法
1)标记——清除算法
首先标记出所有需要回收的对象,在标记完成后统一收回所有被标记的对象。
缺点:(1)效率不高(2)会产生大量的不连续空间碎片。
2)复制算法
将内存分为大小相同的两块,每次只使用其中的一块。当一块内存使用完了,就将还存在着的对象复制到另一块上面,然后把已使用的内存一次性清理掉
优点:每次只对半个内存区域进行回收,内存分配时也不用考虑内存碎片等情况,只要移动堆顶指针,按顺序分配内存就行了,实现简单,运行高效。
缺点:会浪费一部分的内存
现在商业虚拟机都是采用这种算法来回收新生代。将内存分为一块较大的Eden空间和两块较小的Survivor空间。每次使用Eden和其中一块Survivor空间。
当回收时,将Eden与一块Survivior中存活的对象复制到另一块Survivor上,最后清理掉Eden与Survivor的空间。
Eden与Survivor大小比例时8:1。当Survivor空间不够时,需要依赖老年代进行“分配担保”,将存活对象通过分配担保机制进入老年代。
3)标记——整理算法
由于老年代的对象存活率较高,如果采用复制算法的话,效率将会变低。所以老年代采用标记——整理算法。
标记——整理算法也就是标记——整理——清除算法,先是将所有存活对象向一段进行移动,然后直接清理掉边界以外的内存。
4)分代收集算法
将内存分为新生代与老年代,根据不同的年代采取不同的回收算法。新生代采用复制算法,而老年代采用标记——清理或标记——整理算法。