1.引用计数法
①给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一;当引用失效时,计数器减一;任何时刻计数器为0的对象就是不可能再被使用的对象
②引用计数法无法解决相互引用的问题
2.可达性分析算法
①通过一系列称为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明这个对象时不可用的
②Java语言中GC Roots对象包括以下几种
- 虚拟机栈中引用的对象
- 方法区中类静态属性所引用的对象
- 方法区中常量引用的对象
- 本地方法区中JNI引用的对象
3.生存还是死亡
①如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,它将会被第一次标记并且进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法
②当finalize()没有被覆盖或者已经被虚拟机调用过了,则将被视为没有必要执行
4.回收方法区
①永久代的垃圾收集针对废弃常量和无用的类
②无用的类:
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
- 加载该类的ClassLoader已经被回收
- 该类对应的Class对象没有再任何地方被引用,无法在任何地方通过反射访问该类的方法
5.标记清除算法
①首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象
②存在两个问题
- 标记和清除的效率都不高
- 标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不触发另一次垃圾收集动作
6.复制算法
①将可用内存划分为大小相等的两块,每次只使用其中的一块。当一块内存使用完了,就将存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次清理掉
②新生代采用复制算法来进行垃圾回收
③当Survivor空间不够用时,需要依赖老年代进行分配担保
7.标记整理算法
①根据老年代的特点,标记过程与“标记-清除算法”一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一段移动
8.分代收集算法
①根据对象存活周期不同将对内存划分为几块,一般把Java堆划分为新生代和老年代
②新生代,每次垃圾回收时都发现有大批对象死去,只有少量存活,那就选用复制算法
③老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收
9.枚举根节点
①GC Roots节点
- 全局性引用(例如常量或类的静态属性)
- 执行上下文(例如栈中的本地变量表)
10.安全点
①在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots枚举
11.垃圾收集器
12.Serial收集器
①是一个单线程收集器
②垃圾收集时必须暂停其他所有工作线程
③Serial是虚拟机运行在Client模式下的默认新生代垃圾收集器
④单CPU环境下,Serial没有线程交互的开销,单线程收集效率高
13.ParNew收集器
①是Serial收集器的多线程版本
②控制参数与Serial一致
- -XX:SurvivorRatio
- -XX:PretenureSizeThreshold
- -XX:HandlePromotionFailure
③ParNew收集器是运行在Server模式下默认的虚拟机新生代垃圾收集器
④默认开启收集线程数与CPU数量相同,可以使用-XX:ParallelGCThreads参数来限制垃圾收集线程数
14.Parallel Scavenge收集器
①目标是达到一个可以控制的吞吐量
②吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值(吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾收集时间))
③两个参数用于精确控制吞吐量
- 最大垃圾收集停顿时间-XX:MaxGCPausemillis
- 设置吞吐量大小-XX:GCTimeRatio
④GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的
⑤-XX:+UseAdaptiveSizePolicy打开这个参数后,就不需要手工指定新生代的大小、Eden与Survivor比例、晋升老年代对象大小等参数
15.Serial Old收集器
①是一个单线程收集器
②使用标记-整理算法
③用于Client模式下使用
④在server模式下还具备两大用途
- 与新生代Parallel Scavenge收集器搭配使用
- CMS收集器后备预案,Concurrent Mode Failure时使用
16.Parallel Old收集器
①Parallel Old时Parallel Scavenge收集器的老年代版本,使用多线程标记整理算法
②Parallel Scavenge和Parallel Old搭配使用
17.CMS收集器
①获取最短停顿回收时间为目标的垃圾收集器
②基于标记清除算法
③垃圾收集分为四个步骤:
- 初始标记:标记GC Roots能直接关联到的对象,速度很快,需要stop the world
- 并发标记:进行GC Roots Tracing的过程
- 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
- 并发清除
④3个明显的缺点:
- 对CPU资源非常敏感
- 收集器无法处理浮动垃圾,可能出现Concurrent Mode Failure失败而导致另一次Full GC的产生。
- 浮动垃圾:CMS并发清理阶段用户线程还在运行,自然会产生新的垃圾,这部分垃圾出现在标记过程后,CMS无法在当次收集中处理掉他们,只能在下一次GC时再清理
- 在CMS运行期间预留的内存无法满足需求,就会出现Concurrent Mode Failure失败,这时临时启用Serial Old收集器来重新进行老年代的垃圾收集,导致停顿时间过长
- -XX:CMSInitiatingOccupancyFraction设置得太高容易导致大量的Concurrent Mode Failure失败,从而导致性能降低
- CMS是基于标记-清除算法实现的收集器。垃圾收集结束时会产生大量的空间碎片。
- CMS收集器提供-XX:+UseCMSCompactAtFullCollection开关(默认开启),用于CMS收集器顶不住要进行FullGC时开启内存碎片合并整理过程
- 内存整理的过程是无法并发的,空间碎片问题没有了,但是停顿时间不得不变长
- -XX:CMSFullGCsBeforeCompaction用于设置执行多少次不压缩的FullGC后,来一次带压缩的(默认值为0,表示每次进入FullGC时都进行碎片整理)
18.G1收集器
①面向服务端的垃圾收集器
②并行与并发:G1充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop the world的时间
③分代收集:分代概念在G1中依然得以保留
④空间整合:G!从整体上看是基于标记=整理算法实现的收集器。从局部看是基于复制算法实现的
⑤可预测的停顿:能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
⑥G1将整个Java堆划分为多个大小相等的独立区域,虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离了,他们都是一部分Region的集合
⑦G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划的避免在整个Java堆中进行全区域的垃圾收集
⑧G1跟踪各个Region里面垃圾堆的价值大小,在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的Region
⑨G1中每个Region都有一个与之对应的Remember Set,当内存回收时,在GC根节点的枚举范围中加入Remember Set即可保证不对全堆扫描也不会有遗漏
⑩G1垃圾收集可分为以下步骤:
- 初始标记:仅仅只标记以下与GC Roots能直接关联到的对象,需要线程停顿,时间很短
- 并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活对象,这个阶段耗时较长,但是可以与用户线程并发运行
- 最终标记:修正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分标记记录,需要停顿线程,但可并行运行
- 筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来指制定回收计划
19.垃圾收集器参数总结
参数 | 描述 |
---|---|
UseSerialGC | 虚拟机运行在Client模式下的默认值,打开此开关后,使用Serial+Serial Old的收集器组合进行内存回收 |
UseParNewGC | 打开此开关后,使用ParNew+Serial Old的收集器组合进行内存回收 |
UseConcMarkSweepGC | 打开此开关后,使用ParNew+CMS+Serial Old的收集器组合进行垃圾回收。Serial Old收集器将作为CMS收集器出现Concurrent Mode Failure失败后使用 |
UseParallelGC | 虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge+Serial Old进行内存回收 |
UseParallelOldGC | 打开此开关后,使用Parallel Scavenge+Parallel Old的收集器组合进行回收 |
SurvivorRatio | 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Survivor=8:1 |
PretenureSizeThreshold | 直接晋升到老年代的对象年龄,设置这个参数后,大于这个参数的对象将直接在老年代分配 |
MaxTenuringThreshold | 晋升到老年代的对象年龄。每个对象在坚持过一次Minior GC后,年龄就增加1,当超过这个参数时就进入老年代 |
UseAdapterSizePolicy | 动态调整Java堆中各个区域的大小以及进入老年代的年龄 |
HandlePromotionFailure | 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的所有对象都存活的极端情况 |
ParallelGCThreads | 设置并行GC时进行垃圾回收的线程数 |
GCTimeRatio | GC 时间占用总时间的比率,默认值为99,即允许1%的GC时间。仅在使用Parallel Scavenge收集器时生效 |
MaxGCPauseMillis | 设置GC最大停顿时间。仅在使用Parallel Scavenge收集器时生效 |
CMSInitiatingOccupancyFraction | 设置CMS收集器在老年代空间被使用多少后触发垃圾收集。 |
UseCMSCompactAtFullCollection | 设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在使用CMS收集器时生效 |
CMSFullGCsBeforeCompaction | 设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS收集器时生效 |
20.对象优先在Eden分配
①大多数情况下,对象在Eden中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC
②虚拟机提供-XX:+PrintGCDetails这个收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各个区域分配情况
21.大对象直接进入老年代
①大对象指的是需要大量连续内存空间的对象,典型的大对象就是很长的字符串和数组
②虚拟机提供一个-XX:pretenureSizeThreshold参数,令大于这个参数的对象直接在老年代分配
22.长期存活的对象将进入老年代
①新生代对象年龄增加到一定程度(默认是15岁),就会被晋升到老年代中
②-XX:MaxTenuringThreshold设置晋升老年代的年龄阈值
23.空间分配担保
①在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败