- 引用计数、引用跟踪、内存泄漏、内存溢出
- GC Roots
- GC 的一般原理
- 标记清除算法(Mark and Sweep)与Full GC
- 分代假设和堆内存池的划分
- 标记-复制算法(Mark-Copy)与 Minor GC
- 标记-清除-整理算法(Mark-Sweep-Compact)与Major GC
- 关于触发Full GC
- GC类型
- 串行 GC(Serial GC)/ParNewGC
- 并行 GC(Parallel GC)
- CMS GC(Mostly Concurrent Mark and Sweep Garbage Collector)
引用计数:是指将对象被引用的次数相加,当对该对象的引用减少则将该对象的引用次数随之减少,当被引用次数变为零时就将其释放的过程
引用计数容易因为对象之间的循环依赖导致所涉及的内存无法被使用,而导致内存泄漏,当这样的对象增多,内存泄漏最终会导致内存溢出,即内存空间不足
引用跟踪:从根(GC Roots)出发,将所有的正在使用的对象串联起来,直至完毕,不被串联的对象即为“不可达”
GC Roots
可以作为 GC Roots 的对象
- 当前正在执行的方法里的局部变量和输入参数
- 活动线程(Active threads)
- 所有类的静态字段(static field)
- JNI 引用
标记清除算法(Mark and Sweep)简称清除算法
- Marking(标记): 遍历所有的可达对象,并在本地内存(native)中分门别类记下
- Sweeping(清除): 这一步保证了,不可达对象所占用的内存,在之后进行内存分配时可以重用
优势:可以处理循环依赖,只扫描部分对象
缺点:标记和清除对象需要 STW(Stop The World),即让全世界停止下来,此时只有GC线程在运行,而应用的其它业务线程都会停止
如果不对JVM堆内存做划分,那么每一次GC都是对整个堆内存的GC(Full GC),此时STW将会对应用的使用体验造成不好的影响,随着堆内存越大,STW的时间就越长,此时客户端等待应用的响应时间就越长
分代假设和堆内存池的划分需要注意的是:标记阶段,暂停的时间,与堆内存大小,对象的总数没有直接关系,而是由存活对象(alive objects)的数量来决定,所以堆内存的大小并不会直接影响标记阶段占用的时间
分代假设:大部分新生对象很快无用,存活较长时间的对象,可能存活更长时间
标记-复制算法(Mark-Copy)与 Minor GC对于存活较长并且可能更长的对象可以继续保留,对于很快无用或存活时间有限长的对象可以被清理,做了这种区分,无疑将缩短更多的STW时间,由此产生了不同类型对象在不同区域,有不同策略处理
标记-复制算法(Mark-Copy)简称复制算法,主要用于对堆内存中的young区的对象GC处理
对象分配在新生代的 Eden 区,标记阶段 Eden 区以及非空存活区中存活的对象就会复制到空的存活区,两个存活区互换 from 和 to 的角色,对象存活到经过了一定数量的来回周期会提升到老年代
标记-清除-整理算法(Mark-Sweep-Compact)简称整理算法,主要用在老年代堆区的GC处理
老年代默认都是存活对象,采用移动方式:
- 标记所有通过 GC roots 可达的对象;
- 删除所有不可达对象;
- 整理老年代空间中的内容,方法是将所有的存活对象复制,从老年代空间开始的地方依次存
放,相当于做了一次移动
为什么老年代不用复制算法,因为老年代存活的对象多,空间相对不足够
关于触发Full GC为什么要做最后一步的整理,因为堆内存空间总是一片连续的内存空间,如果不做整理导致内存碎片化,则当存储大对象时就会容易触发GC,进而产生STW时间
参考什么时候会发生FullGC
GC类型 串行 GC(Serial GC)/ParNewGC启动Java进程时通过 -XX:+UseSerialGC 配置串行 GC
串行 GC 对年轻代使用 mark-copy(标记-复制)算法,对老年代使用 mark-sweep-compact(标记-清除-整理)算法
- 两者都是单线程的垃圾收集器,不能进行并行处理,所以都会触发全线暂停(STW),停止所有的应用线程
- 这种 GC 算法不能充分利用多核 CPU,不管有多少 CPU 内核,JVM 在垃圾收集时都只能使用单个核心
- CPU 利用率高,暂停时间长。简单粗暴,就像老式的电脑,动不动就卡死。该选项只适合几百 MB 堆内存的 JVM,而且是单核 CPU 时比较有用
后来JDK官方对年轻代的GC回收算法进行了更改,改为使用并行GC(算法不变),对老年代的回收GC不变,即ParNewGC
并行 GC(Parallel GC)-XX:+USeParNewGC 为打开改进版本的 Serial GC,可以配合 CMS 使用
并行GC即为多个GC线程一起进行垃圾回收,对年轻代使用并行 mark-copy(标记-复制)算法、对老年代使用 mark-sweep-compact(标记-清除-整理)算法,都会有STW时间
JDK6、7、8都是以并行GC为默认GC,JDK9及之后的默认GC都是G1 GC
-XX:+UseParallelGC 、-XX:+UseParallelOldGC 、-XX:+UseParallelGC -XX:+UseParallelOldGC 均为指定启动Java进程时,该Java使用并行GC
-XX:ParallelGCThreads=N 通过该参数来指定 GC 线程数, 其默认值为 CPU 核心数
并行垃圾收集器适用于多核服务器,主要目标是增加吞吐量。因为对系统资源的更有效使用,所以能达到更高的吞吐量:
- 在 GC 期间,所有 CPU 内核都在并行清理垃圾,所以相同背景和条件下总暂停时间更短
- 在两次 GC 周期的间隔期,没有 GC 线程在运行,不会消耗任何系统资源
通过启动参数 -XX:+UseConcMarkSweepGC 可以指定当前Java进程使用CMS GC
CMS GC 对年轻代采用并行 mark-copy (标记-复制)算法,对老年代主要使用并发 mark-sweep (标记-清除)算法
CMS GC 的设计目标是避免在老年代垃圾收集时出现长时间的卡顿,主要通过两种手段来达成此目标:
- 不对老年代进行整理,而是使用空闲列表(free-lists)来管理内存空间的回收
- 在 mark-and-sweep (标记-清除) 阶段的大部分工作和应用线程一起并发执行
- 如果服务器是多核 CPU,并且主要调优目标是降低 GC 停顿导致的系统延迟,那么使用 CMS 是个很明智的选择
- 进行老年代的并发回收时,可能会伴随着多次年轻代的 minor GC



