垃圾回收器
管理Jvm内存的工具,可以自动检测和回收不再使用的对象,释放内存,提高程序的性能和稳定性。
常见有以下几种:
串行垃圾回收器 SerialGC 开启命令: -XX:+UseSerialGC=Serial+SerialOld
单线程
堆内存较小,适合个人电脑
垃圾回收线程执行时,其他线程阻塞
吞吐量优先垃圾回收器 ParallelGC
开启命令:(默认开启) -XX:+UseParallelGC或-XX:+UseParallelOldGC
多线程
堆内存较大,多核cpu支持
尽可能让单位时间内,STW时间最短(即单位时间尽可能让暂停时间变短)
会启动多个线程同时进行垃圾回收,线程个数默认和cpu核数相同,会cpu占用100%
线程数目控制: -XX:ParallelGCThreads=n
根据目标设置工作方式
开关: 自适应方式调整(堆/新生代)大小: -XX:+UseAdaptiveSizePolicy 目标: 自适应吞吐量调整目标(百分比): -XX:GCTimeRatio=ratio
(垃圾回收时间不能超过工作时间的1/100)
达不到目标就会自动去调整堆大小来达到目标,一般是增大堆
自适应吞吐量调整目标(值): -XX:MaxGCPauseMills=ms
(每次垃圾回收暂停时间不能超过200ms)
达不到目标就会自动去调整堆大小来达到目标,一般是减小堆
响应时间优先垃圾回收器 CMS
老年代并发收集器,用标记-清除算法,追求最短的回收停顿时间,有高并发、低停顿的特点
开启命令:-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld
特点:
多线程
堆内存较大,多核cpu支持
尽可能让单次的STW的时间最短(即单次暂停时间最短)
并发标记清除,可以在垃圾回收的时候和用户线程一起运行,减少暂停时间
但有时会并发失败,那时会退化成串行回收器
流程: 初步标记(stw)==>并发标记/运行==>重新标记(stw)==>并发清理/运行
UseConcMarkSweepGC是一个老年代垃圾回收器,
垃圾回收时,先暂停用户线程进行初始标记,标记根对象.标记结束(初始标记时间短)
暂停结束,用户线程可以继续运行,垃圾回收线程开始并发标记其他的对象
第二次标记结束,再次暂停,开启多个线程进行重新标记
标记结束,再次开启用户线程,并行清理垃圾
并行时的线程数: -XX:ParallelGCThreads=n (默认是cpu核数)
并发时的线程数: -XX:ConcGCThreads=threads (一般设置为核数的1/4)
因为是并行垃圾清理,可能在清理垃圾的时候产生新垃圾
这些垃圾叫浮动垃圾,只能等待下一次垃圾回收清理
要设置垃圾清理的占比,给浮动垃圾进行一定的内存预留(默认65%)
-XX:CMSInitiatingOccupancyFraction=percent 执行CMS垃圾回收的内存占比
重新标记时,新生代的对象可能引用老年代的对象
要找到这些对象,要重新标记扫描,很耗时间
可以在重新标记之前,可以对新生代进行垃圾回收,这样之后重新标记要扫描的对象就少了
-XX:+CMSScavengeBeforeRemark
CMS是标记清除算法,在内存碎片比较多的情况下,
可能会出现新生代和老年代空间不足的情况,导致并发失败,
这时垃圾回收器会退化为SerialOld,做一次单线程串行的垃圾回收进行标记整理
如果出现退化的情况,垃圾回收耗时会很长
G1(Garbage First):
新型的垃圾回收器,适用于新生代和老年代,采用标记-整理算法,
既能保证高吞吐量,又能实现低停顿时间。
特点:
将堆内存划分为多个大小相等的区域(Region),
每个区域都可作为新生代或老年代使用,且可以动态调整。
分为四个阶段:初始标记、并发标记、最终标记和筛选回收。
初始标记和最终标记需要暂停用户线程(Stop The World),但时间较短;
并发标记则可以与用户线程同时进行;
筛选回收则根据用户设定的停顿时间目标,优先回收垃圾最多区域,
尽可能减少回收次数和范围。
G1适合对吞吐量和响应时间都有要求的应用场景,比如大型电商或金融系统。
缺点: 会占用更多的内存空间作为存储元数据的开销;
且在某些情况下仍可能会发生Full GC,且Full GC的速度较慢。
总结:
SerialGC: 新生代内存不足,minorGC. 老年代内存不足 FullGC
ParallelGC: 新生代内存不足,minorGC. 老年代内存不足 FullGC
CMS: 新生代内存不足,minorGC.
老年代: 并发收集和并行垃圾回收
当并发失败,FullGC
G1: 新生代内存不足minorGC,
老年代: 到达阈值,占堆内存45%,触发并发标记和混合收集阶段
当垃圾回收速度跟不上垃圾生产速度就 FullGC
重新标记remake
在G1和CMS中都有应用,就是在并发标记后都要重新标记
因为如果在标记的同时,用户线程将一个对象的引用取消了,该对象被标记清除,
而此时,该对象又被用户线程引用了,就会出错
当对象引用发生改变时,就会将其加入一个写屏障,(就是将对象加入队列中,并标记为未处理)
并在重写标记阶段将对象从队列中取出,重新检查,如果是被引用就不清理