如何判断对象是否死亡/如何判断对象可以被回收
- 引用计数法
- 引用计数法就是当一个对象被引用一次, 计数就+1, 再被其他对象引用一次, 计数就为2. 如果一个对象计数为1, 那么它就可以被回收了.
- 引用计数法存在的问题是, 当出现循环引用, a引用b, b引用a. 那么他们的引用计数都等于1, 则永远无法被回收. 但其实这时他们已经不被需要应该被回收.
- 可达性分析
- 从GC Roots开始向下搜索,搜索所走过的路径称为引用链. GC Root直接或者间接引用的对象就是不可被回收的. 而根对象没有直接或间接引用的对象就是要被回收的对象.
常用的垃圾收集算法,以及特点
GC最基础的算法有三种: 标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。
-
标记 -清除算法
- 算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
- 缺点是会产生内存碎片, 需要维护一个空闲列表来进行分配.
-
标记整理算法/标记压缩算法
- 算法分为“标记”和“整理”两个阶段:首先标记出所有需要回收的对象,在标记完成后让所有存活的对象都向一端移动,然后直接清理掉另一端的内存. 标记整理算法适用于存活对象多, 垃圾较少的情况.
- 优点是不会产生内存碎片, 消除了复制算法中内存减半的缺点
- 缺点是效率不如复制算法, 需要STW
-
复制算法
- 复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在新生代经常发生,但是在老年代,更常见的情况是大部分对象都是存活对象。如果依然使用复制算法由于存活对象较多,复制的成本也将很高。因此,基于老年代垃圾回收的特性,更适合标记整理算法.
- 执行过程
- 将原有的内存空间一分为二,每次只用其中的一块
- 在垃圾回收时,将正在使用的对象复制到另外一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾回收。
- 优点
- 没有标记和清除过程,实现简单,运行高效。
- 复制过去以后保证空间的连续性,不会出现“碎片” 问题。
- 缺点
- 需要两倍的内存空间, 或者需要将原本的内存一分为二。
- 复制而不是移动,意味着 GC 需要维护对象引用关系,不管是内存占用或者时间开销也不小。
-
分代收集算法
- 将堆内存分为新生代和老年代.
- 所有新生成的对象首先都是放在新生代的, 新生代的目标就是尽可能快速的收集掉那些生命周期短的对象。新生代代分三个区。一个Eden区(伊甸园),两个Survivor区(from和to)
- 首先, 大部分对象都会在Eden区生成
- 当Eden区空间不足时, 就会触发一次minorGC, 此时会采用复制算法, 将存活的对象从Eden区复制到to区, 存活对象的寿命+1, 清除Eden区的垃圾. 然后将from区和to区交换, from区就保留了存活的对象.
- 此时, 新的对象又在Eden区生成. 当Eden区空间不足时, 又会触发MinorGC, 此时会采用复制算法, 将Eden区和from区的存活对象复制到to区, 且寿命+1. 清除Eden区的垃圾, 然后将from和to区交换, from区保留了存活的对象.
- 以此类推, 每当Eden区空间不足时, 就触发MinorGC, 采用复制算法, 将Eden和from区的存活对象复制到to区, 且寿命+1, 清除Eden区的垃圾, 然后将from和to区交换, from区保留了存活的对象.
- Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
- 当存活对象的寿命超过阈值时, 就将该对象从新生代转移到老年代. 因为该对象一直存活说明十分重要, 应该放到gc不频繁的老年代.
- 老年代中gc不频繁, 一般放着那些存活时间久的对象
- 大对象会直接进入老年代, 且不会触发gc.
- 当老年代空间不足, 会先尝试触发minor gc清理新生代, 如果空间依旧不足, 那么就会触发full gc, 清理老年代.
- full gc也会触发SWT(stop the world), 但是相比于minor gc, full gc的暂停时间更长.
- 如果full gc后空间足够, 那就继续运行. 如果full gc后空间不足, 那么抛出out of memory error
常见的垃圾回收器有哪些?
串行垃圾收集器
- Serial收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效; 全程STW
- Serial Old收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;全程STW
并行垃圾收集器
- ParNew收集器 (复制算法): 新生代收并行集器,实际上是Serial收集器的多线程版本,在多核 CPU环境下有着比Serial更好的表现; 全程STW
- Parallel Scavenge收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,尽 快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景; 全程STW
- Parallel Old收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本; 全程STW
并发垃圾收集器
- CMS(Concurrent Mark Sweep)收集器(标记-清除算法): 老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有并发收集、低停顿的特点,追求最短GC回收停顿时间。
- G1(Garbage First)收集器 (标记-整理算法): Java堆并行收集器,G1收集器是JDK1.7提供的一 个新收集器,G1收集器基于标记-整理算法实现,也就是说不会产生内存碎片。此外,G1收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。
CMS垃圾回收器详细介绍
CMS是老年代并行垃圾收集器, 采取标记清除算法, 追求最短GC回收停顿时间, 优点是并发收集, 低停顿. CMS 处理过程有四个步骤:
- 初始标记,指的是寻找所有被 GCRoots 引用的对象,该阶段需要STW。 这个步骤仅仅只是标记一下 GC Roots 能直接关联到的对象,并不需要做整个引用的扫描,因此速度很快。
- 并发标记,指的是对「初始标记阶段」标记的对象进行整个引用链的扫描,该阶段与用户线程同时运行, 不需要STW
- 重新标记,指的是对「并发标记」阶段出现的问题进行校正,该阶段需要STW. 由于垃圾回收算法和用户线程并发执行,虽然能降低响应时间,但是会发生漏标和多标的问题。
- 并发清除,指的是将标记为垃圾的对象进行清除,该阶段与用户线程同时运行, 不需要STW.
CMS 之所以能极大地降低 GC 停顿时间,本质上是将原本冗长的引用链扫描进行切分。通过 GC 线程与用户线程并发执行,加上重新标记校正的方式,减少了垃圾回收的时间。
CMS缺点是:
- 多线程并发处理, 吃CPU资源
- 无法处理浮动垃圾, 在 CMS 进行并发清理的时候,用户线程同时在运行,也会产生一些浮动垃圾。
- 采用标记清除算法会产生空间碎片
G1垃圾回收器详细介绍
特点
- 同时注重吞吐量(Throughput)和低延迟(Low latency), 默认的暂停目标是200ms
- 将堆内存化整为零,会将堆划分为多个大小相等的Region, 内存的回收是以region作为基本单位的
- 整体上是标记整理算法,两个区域之间是复制算法
G1 垃圾收集过程主要分为4个阶段: 以深入理解java虚拟机为标准
- 初始标记
- 标记了从GC Root开始直接关联可达的对象
- 触发STW
- 并发标记
- 在老年代占用堆空间比例到达阈值时, 从GC Root开始对堆中对象进行可达性分析, 递归扫描整个堆里的对象图, 标记出所有回收对象
- 和用户程序并发执行, 不会STW
- 最终标记
- 并发标记时, 其他 用户线程也会工作, 会产生新的对象, 遗弃旧对象 (处理漏标, 错标)
- 触发STW
- 筛选回收
- 先对Region的回收价值进行排序,然后根据期望暂停时间,选择性回收Region
- 不追求一次全部清理完, 优先回收垃圾最多的区域
- 回收时采用复制算法,多条收集器线程并发执行
- 触发STW
如何选择垃圾收集器
- 在堆大小不是很大(比如 100MB ),选择串行收集器一般是效率最高的。 参数: -XX:+UseSerialGC
- 当项目运行在单核的机器上,或者你的虚拟机核数只有单核,选择串行收集器依然是合适的,这时候启用一些并行收集器没有任何收益。 参数: -XX:+UseSerialGC
- 当项目是“吞吐量”优先的,并且对较长时间的停顿没有什么特别的要求。选择并行收集器是比较好的。 参数: -XX:+UseParallelGC
- 如果项目对响应时间要求较高,想要较少的停顿。甚至 1 秒的停顿都会引起大量的请求失败,那么选择 G1 、 ZGC 、 CMS 都是合理的。虽然这些收集器的 GC 停顿通常都比较短,但它 需要一些额外的资源去处理这些工作,通常吞吐量会低一些。 参数: -XX:+UseConcMarkSweepGC 、 -XX:+UseG1GC 、 -XX:+UseZGC 等。
从上面这些出发点来看,我们平常的 Web 服务器,都是对响应性要求非常高的。选择性其实就集 中在 CMS 、 G1 、 ZGC 上。而对于某些定时任务,使用并行收集器,是一个比较好的选择。