什么需要回收
如何判断对象是否已死?
- 引用计数法
记录被引用的次数,当为0时就可以回收,确定会有循环依赖的问题,但是高效 - 可达性分析法
通过GCRoot为根节点开始查找,能找到的为存活对象,否则为需要清理的对象
GCRoot包括,虚拟机栈(局部变量表中引用的对象),方法区中类静态属性引用对象,方法区中常量引用对象,本地方法栈中引用对象(Native引用对象)
其他说明
- 方法区的回收判断条件比较苛刻
该类所有对象都已经被回收
类的加载器已经被回收
类对应的Class对象没有任何地方被访问,没有被反射访问 - 枚举根节点
可达性分享中寻找GCRoot会特别难,比如只是扫描方法区可能就数百兆。所以需要记录枚举根节点。在HotSpot的实现使用OopMap数据结构来存储,每次类加载完成后放入OopMap中GC的时候就可以直接使用 - 安全区
为了解决每一条指令都放入OopMap中可能导致空间太大,所以引入安全区的概念,当GC发生时所有线程都执行到安全区然后标示自己已经进入safeRegion,当出去时要判断是否已经完成根节点的枚举(或整个GC过程)
垃圾回收算法
标记清除法
- 分为标记和清除两步,先标记需要回收对象,然后进行清除
- 问题:效率慢,会产生大量不连续的内存碎片
复制算法
- 将空间划分使用和未使用的,每次清理的时候将存活对象存入未使用区域,然后直接清除已使用的区域
- 问题:存活率高时,会造成效率变低。会造成内存的浪费。(新生代使用的回收算法)
标记整理法
- 将存活的对象向一端移动,最后清除掉其他区域
- 问题:效率慢
分代收集算法
- 将内存划分代,根据各个代的特点使用不同的回收算法,如:新生代使用复制算法,老年代使用标记清除
垃圾回收器

作用于新生代的垃圾回收器有:Serial,ParNew,Parallel Scavenge
作用于老年代的垃圾回收器有:CMS,Serial Old, Parallel Old
G1收集器即可以作用于新生代又可以作用域老年代
- Serial收集器
所有线程跑到安全区后暂停,开启单线程进行清理。清理完成后线程开始执行,采用复制算法 - ParNew收集器
Serial收集器多线程版本,清理是开启多线程 - Parallel Scavenge收集器
Parallel Scavenge收集器也是新生代收,复制算法、多线程收集器。于ParNew不同的是关注的是一个可控制的吞吐量,可以设置最大垃圾收集停顿时间以及直接设置吞吐量大小 - Serial Old收集器
老年代的收集器,采用标记整理的算法。主要在Client模式下的虚拟机使用 - Parallel Old收集器
是Parallel Scavenge老年代版本,采用标记整理的算法,只能和新生代的Parallel Scavenge收集器共同使用 -
CMS收集器
image.png
CMS是以获取最短停顿时间为目标的收集器,采用标记-清除的算法,分为4步:初始标记-》并发标记-》重新标记-》并发清除
初始标记:标记GCRoot。很快,但需要STW
并发标记:通过初始标记的GCRoot去追踪标记内存中的对象,和用户线程一块跑(耗时较长)
重新标记:修正在在并发标记有变动的部分,停顿时间比初始标记长但远比并发标记短,需要STW
并发清除:和用户线程一块进行清除垃圾数据
问题:会有碎片,对CPU要求高只有一个CPU的话性能反而会低,产生浮动垃圾(就是清理的时候又产生了垃圾)
-
G1收集器
image.png
网友评论