对象存货判定算法
引用计数法
给每个对象添加一个引用计数器,每当有一个地方引用它时,计数器家1,;当引用失效时,计数器减1,;任何时刻计数器为0的对象,则该对象可被回收。
引用计数会存在一个问题,它无法解决对象循环引用的问题。
可达性分析算法
通过一系列GC ROOT对象作为起点,向下搜索,搜索过的路径称为引用链,当一个对象到GC ROOT没有任何引用链相连时,则该对象可被回收。
只有引用类型的变量才被认为是ROOTs,值类型的变量永远不会被认为是ROOTs。
在java中,可以被认为是GC ROOT 的对象包括:
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 方法区中类静态属性永远的对象
- 方法区中常量引用的对象
- 本地方法栈中Native方法引用的对象
在可达性分析中,对象的引用类型会对对象的生命周期产生影响:
- 强引用:只要该引用有效,GC就不会回收
- 软引用:内存空间不足时不进行回收,在内存溢出发生进行前回收
- 弱引用:弱引用关联的对象只能存活到下一次GC收集
- 虚引用:无法通过虚引用获得对象,也不会对对象的生存时间产生影响,虚引用的唯一目的就是当该对象被GC回收时,收到一个系统通知。
一个对象真正不可用,要经历两次标记过程:
首先进行可达性分析,筛选出与GC Roots没用引用链的对象,进行第一次标记
第一次标记后,再进行一次筛选,筛选条件是是否有必要执行finalize()方法。若对象有没有重写finalize()方法,或者finalize()是否已被jvm调用过,则没必要执行,GC会回收该对象
若有必要执行,则该对象会被放入F-Queue中,由jvm开启一个低优先级的线程去执行它(但不一定等待finalize执行完毕)。
Finalize()是对象最后一次自救的机会,若对象在finalize()中重新加入到引用链中,则它会被移出要回收的对象的集合。其他对象则会被第二次标记,进行回收
JAVA中的垃圾回收算法有:
- 标记-清除(Mark-Sweep)
- 两个阶段:标记, 清除
- 缺点:两个阶段的效率都不高;容易产生大量的内存碎片
- 复制(Copying)
- 把内存分成大小相同的两块,当一块的内存用完了,就把可用对象复制到另一块上,将使用过的一块一次性清理掉
- 缺点:浪费了一半内存
- 标记-整理(Mark-Compact)
- 标记后,让所有存活的对象移到一端,然后直接清理掉端边界以外的内存
- 分代收集
- 把堆分为新生代和老年代
- 新生代使用复制算法
- 将新生代内存分为一块大的Eden区和两块小的Survivor;每次使用Eden和一个Survivor,回收时将Eden和Survivor存活的对象复制到另一个Survivor(HotSpot的比例Eden:Survivor = 8:1)
- 老年代使用标记-清理或者标记-整理
触发GC又涉及到了内存分配规则:
(对象主要分配在Eden,若启动了本地线程分配缓冲,将优先在TLAB上分配)
- 对象优先在Eden分配
- 当Eden区没有足够的空间时就会发起一次Minor GC
- 大对象直接进入老年代
- 典型的大对象是很长的字符串和数组
- 长期存活的对象进入老年代
- 每个对象有年龄计数器,每经过一次GC,计数器值加一,当到达一定程度时(默认15),就会进入老年代
- 年龄的阈值可通过参数 -XX:MaxTenuringThreshold设置
- 对象年龄的判定
- Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象就可直接进入老年代,无须等到MaxTenuringThreshold要求的年龄
- 空间分配担保
- 发生Minor GC前,jvm会检查老年代最大可用的连续空间是否大于新生代所有对象总空间,若大于,则Minor GC是安全的
- 若不大于,jvm会查看HandlePromotionFailure是否允许担保失败,若不允许,则改为一次Full GC
- 若允许担保失败,则检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,若大于,则尝试进行Minor GC;若小于,则要改为Full GC
垃圾收集器:
-
Serial(串行收集器)
- 特性:单线程,采用复制算法,在进行垃圾收集时,会暂停其他所有的工作线程,直至Serial收集器收集结束为止
- 应用场景:jvm在Client模式下默认的新生代收集器
- 优点:在单核cpu环境中简单高效
-
ParNew
- 特点:是Serial的多线程版本,采用复制算法
- 应用场景:在Server模式下常用的新生代收集器,可与CMS配合工作
- 优点:在多核cpu环境下能有效利用cpu资源,默认开启的收集线程数与CPU的数量相同,也可使用-XX:ParallerGCThreads参数设置
-
Parallel Scavenge
- 特点:并行的多线程新生代收集器,采用复制算法,以吞吐量优先为目标、高效率地利用CPU时间,有自适应调节策略,通过打开参数-XX:+UseAdaptiveSizePolicy,可以根据系统运行的实际情况动态调整新生代大小比例、晋升老年代年龄等信息
- 应用场景:需要吞吐量大的时候
-
SerialOld
- 特点:Serial的老年代版本,单线程,使用标记-整理算法
-
Parallel Old
- Parallel Scavenge的老年代版本,多线程,标记-整理算法
-
CMS
- 特点:以最短回收停顿时间为目标,使用标记-清除算法
- 过程:
- 初始标记:stop the world 标记GC Roots能直接关联到的对象
- 并发标记:进行GC Roots Tracing
- 重新标记:stop the world;修正并发标记期间因用户程序继续运作而导致标记产生变动的 那一部分对象的标记记录
- 并发清除:清除对象
- 优点:并发收集,低停顿
- 缺点:
- 对CPU资源敏感
- 无法处理浮动垃圾(并发清除 时,用户线程仍在运行,此时产生的垃圾为浮动垃圾)
- 产生大量的空间碎片
-
G1
- 特点:面向服务端应用,将整个堆划分为大小相同的region。
- 并行与并发
- 分代收集
- 空间整合:从整体看是基于“标记-整理”的,从局部(两个region之间)看是基于“复制”的。
- 可预测的停顿:使用者可明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
- 执行过程:
- 初始标记:stop the world 标记GC Roots能直接关联到的对象
- 并发标记:可达性分析
- 最终标记:修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录
- 筛选回收:筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划
- GC自适应调节策略 Parallel Scavenge收集器有一个参数-XX:+UseAdaptiveSizePolicy。当这个参数打开之后,就不需要手工指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)。
G1收集器详解
在G1中堆被分成棋盘状的一块块大小相等、互相独立的heap region,每块区域既有可能属于O区、也有可能是Y区,且每类区域空间可以是不连续的(对比CMS的O区和Y区都必须是连续的),每块region都会被打上唯一标识,如eden,survivor,old,humongous,humongous region是用来存放大小超过region的1/2的大对象。
G1提供了两种GC模式
Young GC和Mixed GC,两种都是Stop The World(STW)的
G1 Young GC(STW)
G1 YoungGC在Eden充满时触发,在回收之后所有之前属于Eden的区块全部清空。然后至少有一个区块是属于S区的,同时可能有一些数据移到了O区。
G1 Mixed GC
Mixed GC是混合GC,在用户指定的开销目标范围内会同时进行YGC以及尽可能清理掉由“全局并发标记”统计得出的回收收益高的老年代区域
“全局并发标记”的执行过程分为五个步骤:
- 初始标记(initial mark,STW)
- 在此阶段,G1 GC 对根进行标记。这个阶段通常是在YGC中进行的。
- 根区域扫描(root region scan)
- G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,不被YGC打断,并且只有完成该阶段后,才能开始下一次 YGC。
- 并发标记(Concurrent Marking)
- G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 YGC中断
- 最终标记(Remark,STW)
- 修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录
- 清除垃圾(Cleanup,STW)
- 主要做的是发现哪些区域包含可回收的垃圾最多以供混合GC选择回收。
G1相对于CMS的区别在:
- G1在压缩空间方面有优势
- G1通过将内存空间分成区域(Region)的方式避免内存碎片问题
- Eden, Survivor, Old区不再固定、在内存使用效率上来说更灵活
- G1可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间避免应用雪崩现象
- G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做
- G1会在Young GC中使用、而CMS只能在O区使用
(垃圾回收器部分重点讲CMS和G1)
回收方法区:
- 永久代中主要回收两部分内容:废弃常量和无用的类
- 废弃常量回收和对象的回收类似
- 无用的类需满足3个条件:
- 该类的所有实例对象已被回收
- 加载该类的ClassLoader已被回收
- 该类的Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法