相比C++,java做的一大改进是将复杂的内存管理抽离出来交给jvm去处理,让不再时刻盯着内存泄漏的问题,可以更专注于业务逻辑的开发。这样一来,设计一个合适的垃圾回收算法是很重要的。
JDK1.2以后,Java对引用的概念进行了扩充,将引用分为强引用
、软引用
、弱引用
、虚引用
4种,这4种引用强弱逐渐减弱。
Object object = new Object()
这类的引用,只要强引用还在,垃圾收集器就永远不会回收掉被引用的对象。SoftReference
类来实现软引用。WeakReference
类来实现弱引用。PhantomReference
类来实现虚引用。这种算法的思路在于:将一系列被称为GC Roots的变量作为初始的存活对象合集,然后从该合集出发,所有能够被该集合引用到的对象,并将其加入到该集合中,而不能被该合集所引用到的对象,并可对其宣告死亡。
一般而言,GC Roots 是一些由堆外指向堆内的引用,包括如下几种:
要真正宣告一个对象的死亡,至少需要经历两次标记过程:
finalize()
方法。当对象没有覆盖finalize()
方法,或者finalize()
方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。finalize()
方法,那么这个对象将会放置在一个F-Queue
队列中,稍后会有一个Finalizer
线程去执行它。GC将对F-Queue
队列中的对象进行第二次标记,在这之前,只要重新和任何一个对象建立关联就可以拯救自己,将在第二次标记时被移除“即将回收”的集合。该算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记对象。
为了解决效率问题,复制算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整半块内存进行回收,不必考虑内存碎片问题。
该算法标记过程和“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有对象都向一端移动,然后清理掉端边界以外的内存。
该算法没有什么新的思想,只是根据对象存活周期的不同划分为几块。一般是把Java堆分为新生代(Yong)和老年代(Old),这样就可以根据各个年代的特点采用最适宜的收集算法。