一、标记清除法(Mark And Sweep)java
第一步:从根部出发,遍历全局,而后对全部可达的对象进行标记python
第二步:对全部未标记的对象进行清除c++
优势:方法简单,速度较快。缺点:容易产生较多的内存碎片。算法
采用这种方式的语言:lua等c#
二、标记整理回收(mark-compact)缓存
第一步和标记清除法同样,标记全部可达对象安全
第二步将未标记的对象清除,同时将现有对象的空间合并多线程
优势:没有内存碎片。缺点:合并空间的时候,引用该对象的全部线程都会被挂起,合并完成后才会从新执行。并发
采用这种方式的语言:c#等性能
三、标记复制回收(mark-copy)
复制算法开辟了两个相等的空间,每次只使用其中的一块空间
第一步标记
第二步将全部标记过的对象,复制到另外一块空间,当复制完成后,指向原有对象的指针指向新的对象。所有复制完成后,释放原有的空间。
优势:没有内存碎片,不会gc ,效率高 缺点:须要额外的内存空间
采用这种方式的语言:java的新生代
四、引用计数算法(reference counting)
对象每次被引用的时候对引用次数加1,每次被引用对象被删除时,则对引用次数减1,当引用计数为0时,则删除对象。
优势:迅速,每次当对象引用次数为0时,则立刻就会被清除。无需系统支持,去肯定程序的根。
缺点:
一、计数赋值器带来额外的开销。因此不适合通用的大容量的内存管理器。
二、多线程的程序中,可能释放过早。引用计数的存储指针操做是原子化的,并发线程却同时进行读取和修改,开发者要避免更新指针槽过程当中出现的竞争问题。
三、对单个对象的简单操做也会引起内存请求(更新引用次数),会“污染”高速缓存
四、没法解决循环引用问题
五、有可能卡顿,当删除一个大的根节点的时候,须要去递归删除每个子孙节点。
循环引用的解决方法:
一、按期用标记算法做为补充处理
二、设为强引用和弱引用,把可能产生环的引用设为弱引用,全部强引用可达且不成环,当强引用次数为0时,删除对象(这种方法为了一些安全性缘由,性能开销大,只有少数语言使用)
c++的智能指针的弱引用和这种算法的弱引用不同,c++的弱引用只能肯定是否可达,主要是为了不非法访问。
三、部分跟踪算法,循环引用指针出现有2个条件:
(1)环状指针内部,全部引用对象都有内部对象指针产生
(2)若是删除某一对象后,引用计数仍然为0,则说明产生了环状
扫描对象,若是一个对象的全部引用都是循环引用,则进行处理。临时移除对目标对象的引用次数,从而移除内部指针的引用次数,若是目标对象引用计数仍然大于0,则说明存在外部引用,不然一块儿处理掉。
计数回收的语言有:python等
五、分代算法
将内存分为几个区域,不一样状态的对象放进不一样的区域里,对每一个区域采起不一样的垃圾回收策略,能够兼顾优势,可是比较复杂。
采用分代回收的语言:java等
java将内存分红了新生代、年老代和永久代
新生代:新生代用标记复制回收,由于绝大部分建立的对象都是临时用的,很快会被回收掉,同时为了提升性能,和适合用复制回收,复制回收的两块区域大小是9:1。
年老代:当在新生代里复制必定次数尚未被回收之后,则放到年老代里,年老代采用复制标记回收。
永生代:当在年老代理必定时间没有被回收,则放入永生代,永生代采用复制整理回收。
分红的好处针对不一样性质的对象,采用不一样的处理方式。复制整理回收的回收效果好,可是整理过程当中会形成gc,因此用了两层过渡,减小复制整理的发生。