基础GC 算法
通用垃圾回收算法
算法名 | 优势 | 缺陷 |
---|---|---|
简单 | 效率低下且会产生很多不连续内存,分配大对象时,容易提前引起另一次垃圾回收 | |
效率较高,不用考虑内存碎片化 | 存在空间浪费 | |
避免了内存碎片化 |
Mark-Sweep(标记- 清除算法)

标记
从概念上来讲,标记
Copying(复制算法)
将现有的内存空间分为两快,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大。因此在真正需要垃圾回收的时刻,复制算法的效率是很高的。又由于对象在垃圾回收过程中统一被复制到新的内存空间中,因此,可确保回收后的内存空间是没有碎片的。该算法的缺点是将系统内存折半。

标记
Mark-Compact(标记- 压缩算法)
复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在年轻代经常发生,但是在老年代更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活的对象较多,复制的成本也将很高。
标记

标记
Incremental Collecting(增量回收算法)
在垃圾回收过程中,应用软件将处于一种
增量算法现代垃圾回收的一个前身,其基本思想是,如果一次性将所有的垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程和应用程序线程交替执行。每次,垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。使用这种方式,由于在垃圾回收过程中,间断性地还执行了应用程序代码,所以能减少系统的停顿时间。但是,因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。
Generational Collecting(分代回收算法)
分代回收器是增量收集的另一个化身,根据垃圾回收对象的特性,不同阶段最优的方式是使用合适的算法用于本阶段的垃圾回收,分代算法即是基于这种思想,它将内存区间根据对象的特点分成几块,根据 每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。以
当代主流虚拟机(Hotspot VM)的垃圾回收都采用“分代回收”的算法

-
新生代(Young Generation
) :大多数对象在新生代中被创建,其中很多对象的生命周期很短。每次新生代的垃圾回收(又称Minor GC )后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。新生代内又分三个区:一个Eden 区,两个Survivor 区(一般而言) ,大部分对象在Eden 区中生成。当Eden 区满时,还存活的对象将被复制到两个Survivor 区(中的一个) 。当这个Survivor 区满时,此区的存活且不满足“晋升”条件的对象将被复制到另外一个Survivor 区。对象每经历一次Minor GC ,年龄加1 ,达到“晋升年龄阈值”后,被放到老年代,这个过程也称为“晋升”。显然, “晋升年龄阈值”的大小直接影响着对象在新生代中的停留时间,在Serial 和ParNew GC 两种回收器中, “晋升年龄阈值”通过参数MaxTenuringThreshold 设定,默认值为15 。 -
老年代(Old Generation
) :在新生代中经历了N 次垃圾回收后仍然存活的对象,就会被放到年老代,该区域中对象存活率高。老年代的垃圾回收(又称Major GC )通常使用“标记- 清理”或“标记- 整理”算法。整堆包括新生代和老年代的垃圾回收称为Full GC (HotSpot VM 里,除了CMS 之外,其它能收集老年代的GC 都会同时收集整个GC 堆,包括新生代) 。 -
永久代(Perm Generation
) :主要存放元数据,例如Class 、Method 的元信息,与垃圾回收要回收的Java 对象关系不大。相对于新生代和年老代来说,该区域的划分对垃圾回收影响比较小。在Java 8 之前,PermGen 是存放在堆中,在Java 8 之后,PermGen 被Metaspace 替代,并且Metaspace 直接存放在原生内存,而不再是堆中。
动态年龄计算
-
如果固定按照
MaxTenuringThreshold 设定的阈值作为晋升条件:a)MaxTenuringThreshold 设置的过大,原本应该晋升的对象一直停留在Survivor 区,直到Survivor 区溢出,一旦溢出发生,Eden+Svuvivor 中对象将不再依据年龄全部提升到老年代,这样对象老化的机制就失效了。b)MaxTenuringThreshold 设置的过小, “过早晋升”即对象不能在新生代充分被回收,大量短期对象被晋升到老年代,老年代空间迅速增长,引起频繁的Major GC 。分代回收失去了意义,严重影响GC 性能。 -
相同应用在不同时间的表现不同:特殊任务的执行或者流量成分的变化,都会导致对象的生命周期分布发生波动,那么固定的阈值设定,因为无法动态适应变化,会造成和上面相同的问题。
总结来说,为了更好的适应不同程序的内存情况,虚拟机并不总是要求对象年龄必须达到
Minor GC, Major GC 与Full GC
针对
-
Partial GC:并不收集整个
GC 堆的模式- Young GC:只收集
young gen 的GC - Old GC:只收集
old gen 的GC 。只有CMS 的concurrent collection 是这个模式 - Mixed GC:收集整个
young gen 以及部分old gen 的GC 。只有G1 有这个模式
- Young GC:只收集
-
Full GC:收集整个堆,包括
young gen 、old gen、perm gen(如果存在的话)等所有部分的模式。
最简单的分代式
- Young GC:当
young gen 中的eden 区分配满的时候触发。注意Young GC 中有部分存活对象会晋升到old gen ,所以Young GC 后old gen 的占用量通常会有所升高。 - Full GC:
- 当准备要触发一次
Young GC 时,如果发现统计数据说之前Young GC 的平均晋升大小比目前old gen 剩余的空间大,则不会触发Young GC 而是转为触发Full GC (因为HotSpot VM 的GC 里,除了CMS 的concurrent collection 之外,其它能收集old gen 的GC 都会同时收集整个GC 堆,包括young gen ,所以不需要事先触发一次单独的young GC ) ; - 如果有
perm gen 的话,要在perm gen 分配空间但已经没有足够空间时,也要触发一次Full GC ; - 或者
System.gc() 、heap dump 带GC ,默认也是触发Full GC 。
- 当准备要触发一次
Concurrent Collecting(并发回收算法)
所谓的并发回收算法即是指垃圾回收器与应用程序能够交替工作,并发回收 器其实也会暂停,但是时间非常短,它并不会在从开始回收寻找、标记、清楚、压缩或拷贝等方式过程完全暂停服务,它发现有几个时间比较长,一个就是标记,因 为这个回收一般面对的是老年代,这个区域一般很大,而一般来说绝大部分对象应该是活着的,所以标记时间很长,还有一个时间是压缩,但是压缩并不一定非要每 一次做完
所以他们想出来的办法就是:第一次短暂停机是将所有对象的根指针找到,这个非常容 易找到,而且非常快速,找到后,此时