程序的运行必然须要申请内存资源,无效的对象资源若是不及时处理就会一直占有内存资源,最终将致使内存溢出,因此对内存资源的管理是很是重要了。程序员
为了让程序员更专一于代码的实现,而不用过多的考虑内存释放的问题,因此,在Java语言中,有了自动的垃圾回收机制,也就是咱们熟悉的GC。
有了垃圾回收机制后,程序员只须要关心内存的申请便可,内存的释放由系统自动识别完成。
换句话说,自动的垃圾回收的算法就会变得很是重要了,若是由于算法的不合理,致使内存资源一直没有释放,一样也可能会致使内存溢出的。
固然,除了Java语言,C#、Python等语言也都有自动的垃圾回收机制。算法
假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器就-1,若是对象A的计数器的值为0,就说明对象A没有引用了,能够被回收。markdown
优点
实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就能够直接回收。
在垃圾回收过程当中,应用无需挂起。若是申请内存时,内存不足,则马上报
outofmember 错误。
区域性,更新对象的计数器时,只是影响到该对象,不会扫描所有对象。jvm
class TestA { public TestB b ; } class TestB{ public TestA a; } public class Main { public static void main(String[] args) { TestA a = new TestA(); TestB b = new TestB(); a.b = b; b.a = a; a = null; b = null; // 虽然被设置为null,可是a与b之间依旧存在着循环引用的问题 } }
标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除。
标记:从根节点开始标记引用的对象。
清除:未被标记引用的对象就是垃圾对象,能够被清理。ide
初始状态下,全部的目标对象都是为0(未被标记)
待jvm出现有效内存耗尽,就会挂起线程,执行GC线程,进行标记性能
从根节点进行标记到最后,而后回收未被标记的对象。
清理完毕以后挂起gc线程,从新执行原先被挂起的线程。
而被标记的对象会被从新置0;优化
优劣分析线程
优点
很明显的解决了循环应用致使的不能被回收的问题3d
原理code
标记压缩算法是在标记清除算法的基础之上,作了优化改进的算法。和标记清除算法同样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并非简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,而后清理边界之外的垃圾,从而解决了碎片化的问题。
优势
在标记清除算法的基础上解决了产生碎片的问题
复制算法的核心就是:将原有的内存空间一分为二,每次只用其中的一块,在垃圾回收时,将正在使用的对象复制到另外一个内存空间中,而后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。
典型的复制算法的落地实现就是:jvm中堆内存的年轻代的gc策略(具体能够看我jvm系列的博客的内存模型的那一部份内容)
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor 区“To”是空的。
紧接着进行GC,Eden区中全部存活的对象都会被复制到“To”,而在“From”区中仍存活的对象会根据他们的年龄值来决定去向。年龄达到必定值(年龄阈值,能够经过- XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。
通过此次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。无论怎样,都会保证名为To的Survivor区域是空的。
优点
在垃圾对象多的状况下,效率较高
清理后,内存无碎片
前面介绍了多种回收算法,每一种算法都有本身的优势也有缺点,谁都不能替代谁,因此根据垃圾回收对象的特色进行选择,才是明智的选择。分代算法其实就是这样的,根据回收对象的特色进行选择,在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除或标记压缩算法