
算法分为标记和清除两个阶段。标记出所有需要回收的对象,在标记完成后,统一回收。
缺点:
多数对象是可回收的情况下,算法需要复制占少数的存活对象;每次针对半个区域进行内存回收,分配内存时不用考虑内存碎片情况,只要移动堆顶指针,按顺序分配即可。实现简单、运行高效。
缺点:
标记过程与标记清除算法一样,后续步骤不一样。后续步骤是将存货对象向一端移动,然后直接清除掉端边界以外的内存。
优点:
缺点:
最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。
当一个对象被强引用变量引用时,它处于“活着”状态,它不可能被回收的。
SoftReference,对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。
- /**
- * java -Xms10m -Xmx10m SoftReferenceTest
- */
- public class SoftReferenceTest {
-
- static class HeapObject {
- byte[] bs = new byte[1024 * 1024];
- }
-
- public static void main(String[] args) {
- SoftReference
softReference = new SoftReference<>(new HeapObject()); -
- List
list = new ArrayList<>(); -
- while (true) {
- if (softReference.get() != null) {
- list.add(new HeapObject());
- System.out.println("list.add");
- } else {
- System.out.println("---------软引用已被回收---------");
- break;
- }
- System.gc();
- }
- }
- }
WeakReference,弱引用也是用来描述非必需对象的,它的强度比软引用更弱一些。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
- /**
- * jdk 1.8
- */
- public class WeakReferenceTest {
- static class TestObject{
-
- }
-
- public static void main(String[] args) throws InterruptedException {
- WeakReference
weakReference=new WeakReference<>(new TestObject()); -
- System.out.println(weakReference.get() == null);//false
-
- System.gc();
- TimeUnit.SECONDS.sleep(1);//暂停一秒钟
-
- System.out.println(weakReference.get() == null);//true
- }
- }
Tips:软引用、弱引用都非常适合来保存那些可有可无的缓存数据。如果这么做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用。
PhantomReference,最弱的一种引用关系,随时都有可能被垃圾回收器回收。
虚引用必须和引用队列(ReferenceQueue)联合使用,主要作用是跟踪对象被垃圾回收的状态。

新生代收集器
串行
单线程收集器,只用一个线程完成垃圾收集,且收集时必须暂停工作线程。
使用标记-复制算法。
新生代收集器
串行
多线程收集,Serial收集器的多线程版本,GC时需要暂停工作线程
使用标记-复制算法
新生代收集器
并行
多线程收集,GC时需要暂停工作线程
高吞吐量为目标
标记-复制算法
虚拟机运行在Server模式下的默认垃圾收集器,适合那种交互少、运算多的场景.例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序.
Parallel Scavenge收集器无法与CMS收集器配合使用,所以在JDK 1.6推出Parallel Old之前,如果新生代选择Parallel Scavenge收集器,老年代只有Serial Old收集器能与之配合使用。
CMS等收集器的关注点是尽可能地缩短STW停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。
Serial 收集器在新生代和老年代都有对应的版本,除了算法不同,两个版本没有其他的差异
老年代收集器
串行
单线程收集器,垃圾收集时必须暂停工作线程
标记-整理算法
Client模式:默认使用Serial Old 收集器。
Server模式:在JDK1.5以及之前的版本中,与Parallel Scavenge收集器配合使用;最为CMS收集器的后备预案,在并发收集发生“Concurrent Mode Failure”时使用。
Parallel Old是Parallel Scavenge 的老年版本,除了算法不同,两个版本没有其他差异
老年代收集器
并行
多线程收集器,GC时需要暂停工作线程
高吞吐量为目标
标记-整理算法
在注重吞吐量及CPU资源敏感的场合,可以优先考虑 Parallel Scavenge + Parallel Old收集器组合。
JDK1.6及之后来代替Serial Old收集器,特别是在 Server模式和多CPU情况下。
CMS是HotSpot在JDK5推出的第一款真正意义上的并发(Concurrent)收集器,让垃圾收集线程与用户线程(基本上)同时工作。
老年代收集器
并发
多线程,收集过程基本不需要暂停用户线程
标记-清除算法
以最短停顿时间为目标
适用于用户较多的场景,应用集中在互联网或B/S系统的服务器上。这类服务注重响应速度,希望停顿时间最短,以给用户带来极好的体验。

初始标记
并发标记
重新标记
并发清除
1.对CPU资源敏感
虽然不会暂停用户线程,但因占用一部分CPU资源,会导致应用程序变慢、总吞吐量降低。
CMS的默认收集线程数量 = (CPU+3) / 4;
当CPU多于4个,收集线程占用的CPU资源多于25%,对用户程序影响可能较大;不足4个时,影响更大。
2.CMS所需要的空间比其他垃圾收集器大
由于在垃圾收集阶段用户线程还需要运行,那就需要预留足够的内存给用户线程使用,CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集。可以理解为CMS所需要的空间比其他垃圾收集器大;
-XX:CMSInitiatingOccupancyFraction 指定当老年代空间使用的阈值达到多少才进行一次CMS垃圾回收。(JDK1.5默认值为68%;JDK1.6变为大约92%)
CMS并发清理阶段用户线程还在运行着,会产生新的垃圾(留待下一次清理),这一部分垃圾称为“浮动垃圾”。
3.Concurrent Mode Failure 失败
CMS的垃圾清理和应用线程是并行进行的,如果在并行清理的过程中老年代的空间不足以容纳应用产生的垃圾,则会抛出“concurrent mode failure”。
会临时启用Serial Old收集器来进行垃圾收集,此时应用线程暂停,停顿时间更长。
所以 "-XX:CMSInitiatingOccupancyFraction"不能设置得太大。
4.产生大量碎片
由于CMS基于"标记-清除"算法,所以会产生碎片。产生大量不连续的内存碎片会导致分配大内存对象时,无法找到足够的连续内存,从而需要提前触发另一次Full GC动作。
G1 的全称是 Garbage-First,意为垃圾优先,哪一块的垃圾最多就优先清理它。
G1 最主要的设计目标是:降低STW的停顿时间,将停顿的时间和分布变的可预期、可配置。
G1收集器之所以能建立可预测的停顿时间模型,是因为:
- 它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。
- G1跟踪各个Region获得其收集价值大小,后台维护一个优先列表
- 每次根据允许的收集时间,优先回收价值最大的Region,这样就保证了效率。
1.面向服务端应用
针对具有大内存、多处理器的机器;为需要低GC延迟,并具有大堆的程序提供解决方案。如:在堆大小约6GB或更大时,可预测的暂停时间可以低于0.5秒;
2.用来替换掉JDK1.5的CMS收集器
回收价值:回收所获得的空间大小以及回收所需时间
注意:G1与前面的垃圾收集器有很大不同,它把新生代、老年代的划分取消了!
这意味着,在收集过程中完成了堆的压缩(至少是部分堆的压缩),这样也就不会有CMS内存碎片问题的存在了。
Humongous区域
- 如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象,默认分配在 年老代,但如果它存活期短,就会对垃圾收集器造成负面影响。
- 为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。
- 如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有 时候不得不启动Full GC。
