垃圾收集器
“Stop The World”这项工作实际上是由虚拟机在后台自动发起和自动完成的,在用户不可见的情况下把用户正常工作的线程全部停掉,这对很多应用来说都是难以接受的。假如你的计算机每运行一个小时就会暂停响应5分钟,你会有什么样的心情?
从JDK 1.3开始,HotSpot虚拟机开发团队为消除或者减少工作线程因内存回收而导致停顿的努力一直在进行着,从Serial收集器到Parallel收集器,再到Concurrent Mark Sweep(CMS)乃至GC收集器的最前沿成果Garbage First(G1)收集器,用户线程的停顿时间在不断缩短,但是仍然没有办法完全消除,寻找更优秀的垃圾收集器的工作仍在继续!
可以看做垃圾收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现。
上展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明它们可以搭配使用。
1、Serial收集器 [ˈsɪriəl]
Serial收集器是最基本、发展历史最悠久的收集器,曾经(在JDK 1.3.1之前)是虚拟机新生代收集的唯一选择。
Serial是单线程收集器,它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
现在为止,它依然是虚拟机运行在Client模式下的默认新生代收集器。它也有着优于其他收集器的地方:简单而高效(与其他收集器的单线程比)
2、ParNew收集器
其实是 Serial 收集器的多线程版本。使用多条线程进行垃圾收集。
ParNew收集器除了多线程收集之外,其他与Serial收集器相比并没有太多创新之处,但它却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。
3、Parallel Scavenge收集器 [ˈpærəlel] [ˈskævɪndʒ]
是一个使用复制算法的新生代收集器,又是并行的多线程收集器。但是它的关注点和其它收集器不同,CMS 等收集器的关注点是尽可能的缩短垃圾收集时用户线程的停顿时间,而 Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量,所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值。例如虚拟机总运行了 100 分钟,其中垃圾回收花费了一分钟,那么吞吐量就是 99%。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率的利用 CPU 时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
4、Serial Old 收集器
是 Serial 收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。这个收集器的主要意义也是在于给Client模式下的虚拟机使用。
5、Parallel Old 收集器
是 Parallel Scavenge 收集器的老年代版本,使用多线程和标记-整理算法。
6、CMS 收集器
CMS 收集器是一种以获取最短回收停顿时间为目标的收集器。基于标记清除算法实现。
优点:
- 并发收集
- 低停顿
缺点:
- CMS收集器对CPU资源非常敏感
- CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生
- CMS是一款基于“标记—清除”算法实现的收集器,收集结束时会有大量空间碎片产生。
7、G1 收集器
G1 是一款面向服务端应用的垃圾收集器,有以下特点:
- 并行和并发
- 分代收集
- 空间整合
- 可预测的停顿
内存分配和回收策略
对于内存分配,往大方向讲,就是在堆上分配,对象主要分配在新声代的 Eden 区上,如果启动了本地线程分配缓冲区,将按线程优先在 TLAB 上分配。少数情况下也可以直接分配在老年代。
1、对象优先在 Eden 区分配
大多数情况下,对象在新生代 Eden 区分配,当 Eden 区没有足够的空间进行分配时,虚拟机将发起一次 Minor GC。
-
新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕死的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。
-
老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现 Major GC,经常会伴随至少一次的 Minor GC,Major GC 的速度一般会比 Minor GC 慢10倍以上。
2、大对象直接进入老年代
所谓的大对象是指,需要大量连续内存空间的 Java 对象,最典型的大对象就是那种很长的字符串以及数组。一般来说,超过 3M 的对象会直接在老年代进行分配。
4、长期存活的对象将进入老年代
既然虚拟机采用了分代收集的思想来管理内存,那么内存回收就必须能识别哪些对象应该放在新生代还是老年代。为了做到这一点,虚拟机给每个对象定义了一个对象年龄计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并且对象年龄设为 1。对象在 Survivor 区每熬过一次 Minor GC,年龄就会增加一岁。当它的年龄增加到一定程度,默认是 15,就将会被晋升到老年代中。