JVM垃圾收集器
一、对象内存回收判断
1.引用计数算法
给对象一个引用计数器,每当有个地方引用就加一;当引用失效则减一;没有任何引用时计数器为0
该方法实现简单,高效;但是目前没有主流GC选择该算法,主要是该方式存在循环引用问题,会导致内存泄漏;
例如:
objA =newObj();
objB =newObj();
objA.instance=objB;
objB.instance=objA;
2.可达性分析算法
从“GC roots”(即线程栈本地变量,静态变量,本地方法栈变量)对象作为起点,从该节点向下查找,找到的都标记为非垃圾对象
二、垃圾收集算法
1.标记-清除算法(Mark-Sweep)
最基本的垃圾收集算法,分为两步,标记和清除;
优点:算法简单,不需要对象移动
缺点:一是效率问题,标记对象太多,效率不高;二空间问题,会产生很多不连续的空间碎片
标记-清除算法2.标记-整理算法 (Mark-Compact)
根据老年代特点,推出的一中算法,标记和标记-清除差不多,但是会把存活对象向一端移动
优点:解决了标记-清除空间问题
缺点:对象移动,一定程度上降低了效率
标记-整理算法3.标记-复制算法
为解决效率问题,产生的算法,一般用于年轻代,但是也有GC老年代使用该算法;
优点:高效、算法简单,不用考虑空间碎片问题
缺点:可用空间会缩小到原来的一半
标记-复制算法三、垃圾收集器
1.Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
串行收集器,单线程,最古老的垃圾收集器,当服务器是单核时,该收集器效率最高;
新生代使用标记-复制算法,老年代使用标记-整理算法;
另外一种是作为CMS垃圾收集器的备用方案
Serial收集器2.Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
Serial收集器的多线程版本,但单CPU情况下,Serial收集器效率高于该垃圾收集器
新生代使用标记-复制算法,老年代使用标记-整理算法;
Parallel Scavenge收集器3.ParNew收集器(-XX:+UseParNewGC)
该算法基本与Parallel Scavenge收集器相似,主要区别是Parallel Scavenge收集器不能与CMS垃圾收集器配合使用,该算法作用于新生代,采用标记-复制算法;图和上面一样
4.CMS收集器(-XX:+UseConcMarkSweepGC(old))
CMS(Concurrent Mark Sweep)收集算法是一种以获取最短停顿时间(STW)为目标的收集器;主要是注重用户体验,该垃圾收集器是第一款实现与用户线程同时工作的并发收集器;
CMS收集器初始标记:该步骤会产生STW,但时间比较短,该次标记只标记GC ROOTS
并发标记:该步骤是和用户线程同时进行的,该步骤持续时间比较长(一般达到该次GC所占80%的时间),会去扫描整个堆,从GC ROOTS开始,标记所有对象;
重新标记:该步骤也会产生STW,主要是对并发标记时,用户线程继续运行导致标记产生改变的对象,进行重新标记;这个时间一般比初始标记要长,远远小于并发标记;主要用到三色标记 增量更新
并发清理:开启用户线程,GC线程对未标记的对象做清理,当该步骤有新增对象,则被标记为黑色(三色标记)
并发重置:重置本次GC对象的标记
缺点:
1.对CPU资源敏感,会和用户线程抢占资源
2.该过程中会有被重复标记的垃圾(浮动垃圾,即当并发标记时,已经变成垃圾的对象,只能等到下次GC时进行回收)
3.采用标记清除算法,会产生空间碎片;使用 UseCMSCompactAtFullCollection(FullGC后做压缩整理)和XX:CMSFullGCsBeforeCompaction(多少次fullGC后整理一次,默认是3次)
4.执行的不确定性,在并发标记时,用户线程一直运行,如果该过程空间不够,会导致再次触发FullGC;也就是"concurrent mode failure";此时会进入STW,会使用 SerialOld 垃圾收集器进行回收;
4.1 三色标记
三色标记从GC Roots(黑色)开始扫描,当扫描到某个对象时,会把该对象标记为灰色,当该对象所有引用对象全部被扫描,则该对象会被标记为黑色,被引用的对象会被标记为灰色;直到扫描完整个堆;
4.2写屏障
当出现上图4和5的情况,则需要特殊处理,CMS采用的是增量更新(Increamental Update),G1采用的是原始快照(Snap shot At The Begining, SATB);
增量更新:当黑色对象新增引用时,将这个新插入的引用记录下来,等并发标记过程之后,重新标记时,再去深度扫描记录下的对象;可以简单理解为,当白色对象被黑色对象引用时,黑色对象会被标记为灰色对象;
原始快照:当灰色对象删除对白色对象的引用时,记录下来这个引用关系,当并发标记结束之后,再把灰色对象当做根,做深度扫描;目的就是让这个对象在这次GC中能存活下来,但是会多出一些浮动垃圾,会在下次GC时被清理掉;
4.3这两次STW的理解
个人理解:第一次STW标记GC Roots,这个是最容易理解的,肯定是需要STW的;并发标记时,就从第一步标记的GC Roots开始,向下扫描整个堆,所以并发标记时间最长;重新标记,这步需要再次扫描新被引用的对象,所以需要STW,不然会不断有新对象产生,这一步就会执行不完;再就是并发清理是不需要STW的,如果需要整理的话,是需要STW的,并发重置也不需要STW。
4.4记忆集与卡表
在新生代做GC ROOTS扫描时,遇到跨代引用,如果去扫描整个老年代,则效率会很低;
为此,在新生代可以引入记录集(Remember Set)数据结构;避免整个老年代做扫描;hotspot使用一种叫做“卡表”(Cardtable)的方式实现记忆集,也是目前最常用的一种方式。类似于java中的HashMap;在每块新生代中维护一个卡表,老年代会分为很多个卡页,每个卡页512个字节;卡表记录着引用关系,指到老年代的卡页;
一个卡页中可包含多个对象,只要有一个对象的字段存在跨代指针,其对应的卡表的元素标识就变成1,表示该元素变脏,否则为0.GC时,只要筛选本收集区的卡表中变脏的元素加入GCRoots里。
使用写屏障维护卡表;
5.G1收集器(-XX:+UseG1GC)
G1垃圾收集器,主要是针对多CPU大内存机器;JDK9开始默认使用,既能满足停顿时间要求的同时,也满足高吞吐量的特征;
G1收集器G1将堆划分为多个Region,默认2048个(-XX:G1HeapRegionSize 可以设置每个region的大小),最多2048个,其实也可以设置最大个数;该垃圾收集器,也保留了分代的概念,对象年龄判断也和之前的垃圾收集器相同;只是现在的分代不是物理划分,是逻辑划分;并且每个Region在GC后都有可能变更类型;-XX:G1NewSizePercent 参数可以设置新生代的初始占比,默认5%,最大不超过60%;
与之前不同的是,该垃圾收集器,多了Humongous区域,专门储存大对象;当对象大小超过Region大小的50%算作为大对象,如果对象太大会横跨多个Humongous区域,不会放入老年代;Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收。
G1 运行过程初始标记:和CMS一样,暂停所有其他线程,并记录GC ROOTS直接引用对象,速度很快;
并发标记:和CMS一样,扫描整个堆
最终标记:和CMS重新标记一样,不同的是,并发标记使用的是颜色指针,最终标记也是
筛选回收:该过程会STW,会根据用户设置的停顿时间,来判断回收Region的优先级,指定回收计划;优先回收价值最大的,回收算法主要是复制算法,所以不会有空间碎片;不能并发回收,到了ZGC可以并发回收;-XX:G1MixedGCCountTarget 参数可以设置回收多少次,为了用户体验,回收是可以中断的,执行一会筛选回收,再执行用户线程;该参数默认是8;
G1收集器,会维护一个优先级列表,优先选择回收价值大的Region,这也是名字G1(Garbage-First)的由来,比如,Region a 花100ms能回收20M,Region b 花50 ms能回收 40M,会优先回收Region b;
被视为JDK1.7以上版本Java虚拟机的一个重要进化特征。它具备以下特点:
并行与并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。
空间整合:与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。
可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1 和 CMS 共同的关注点,但G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段(通过参数"-XX:MaxGCPauseMillis"指定,默认200ms)内完成垃圾收集。
G1 GC分类:
YoungGC:当年轻代Region被放满时,会优先判断与设置的"-XX:MaxGCPauseMillis"是否接近,如果远远小于,则先增加年轻代Region来存放对象;如果时间接近,则触发YoungGc;
MixedGC:当老年代占有率达到(-XX:InitiatingHeapOccupancyPercent)参数设置值时,回收所有的新生代和部分老年代以及大对象区域;主要使用复制算法;如果没有足够的Region来提供MixedGC的拷贝,则触发FullGC;
FullGC:停止系统程序,使用单线程GC来做标记,清除,整理;非常耗时。