JVM垃圾回收机制及JVM调优

垃圾回收算法

  • 标记复制算法:将内存分为两块,将存活的对象标记,按顺序复制到另一块空的内存然后清除没被标记的内存。空间利用率小,内存空间连续
  • 标记清除算法:将存活的对象标记,删除未被标记的对象(也可以反过来)。对象多的话,效率低,内存空间不连续
  • 标记整理算法:将存活的对象标记,将标记的对象向一端移动,然后将端的末端位置之后的对象全部删除。

垃圾收集器

JVM垃圾回收机制及JVM调优_第1张图片

1、serial收集器:单线程收集器,收集过程中会STW(stop the world,暂停其他所有工作线程)。新生代为复制算法、老年代为标记整理算法。

2、parallel收集器:多线程收集器,收集过程中也会STW。新生代为复制算法、老年代为标记整理算法。

3、parnew收集器:和parallel收集器类似,区别主要是可以和CMS收集器配合使用。新生代为复制算法、老年代为标记整理算法。

4、CMS收集器:

JVM垃圾回收机制及JVM调优_第2张图片

 初始标记:暂停其他线程,标记GC ROOTs直接引用的对象,速度非常快。

并发标记:可以和其他线程同时运行,标记GC ROOTs关联的所有对象。耗时比较久。

重新标记:暂停其他线程,标记在并发标记过程中,进入老年代的新对象,主要采用三色算法+增量更新算法来实现。

并发清理:可以和用户线程同时运行,GC线程清理未被标记的对象。同时新进来的对象会标记为黑色(根据三色算法的规则),不会清理。

并发重置:会将GC过程中所有标记的对象重置。

注意:

  • CMS会存在大量的碎片空间,这个可以通过配置XX:+UseCMSCompactAtFullCollection来实现在并发重置之后整理碎片空间。
  • CMS会存在上一次还没回收完,又触发的情况,一边回收一边清理,也许回收的过程还会触发full GC。这时会进入STW,触发serial收集器,单线程回收所有垃圾。

CMS的相关核心参数

  •  -XX:+UseConcMarkSweepGC:启用cms
  •  -XX:ConcGCThreads:并发的GC线程数
  •  -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)
  •  -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一 次
  •  -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
  •  -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设 定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
  •  -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引 用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段
  •  -XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW 9. -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;

三色标记:对象有三种状态:黑色、灰色、白色。黑色为已经扫描完成的对象,灰色为扫描了一部分引用的对象,白色为未被扫描的对象(当扫描开始时都是白色,扫描完成后还是白色则对象会被清除)。

        

JVM垃圾回收机制及JVM调优_第3张图片

 读写屏障:

  • 增量更新:当并发标记和并发清理的过程中,有新增的对象进入老年代,会被放入队列中,重新标记和并发清理时会拿到这个队列中的对象,重新找到对象的ROOT节点重新扫描所有关联的对象,对象还是白色标记则被删除。CMS使用的是增量更新
  • 原始快照:同样在并发标记和并发清理,不管是对象的删除还是新增,都会放入队列中,之后这些对象会标记为黑色,等到下次GC时再重新被扫描。G1使用的是原始快照(SATB)。

记忆集与卡表:

        将夸代引用的对象存放在MAP结构的内存中,只会存内存地址和1bit,避免做新生代扫描时同时也需要对老年代作全盘扫描。

JVM垃圾回收机制及JVM调优_第4张图片

 5、G1收集器

JVM垃圾回收机制及JVM调优_第5张图片

 G1将堆分为多(默认为2048)个区域(Region),通过设置XX:G1HeapRegionSize可以手动指定Region大小。G1中新生代和老年代不再是连续的一大块区域,不是物理隔离,任何一个Region有可能在一次GC之后由新生代变为老年代。默认新生代占物理空间的5%,但是会随着对象的增多而变多,最多不会超过60%,可以通过参数来设置。eden和survivor区默认为8:1:1。还额外增加了一个大对象区域H区。

JVM垃圾回收机制及JVM调优_第6张图片

G1在JDK1.9对算法做了优化。和CMS最大区别就是加了一个 -XX:MaxGCPauseMills用户期望停顿时间,收集器会根据这个参数和复杂的算法来计算回收一部分还是全量回收,因此导致G1的算法非常复杂,一般适用于大内存。

经验建议:4G以下可以用parallel,4-8G可以用ParNew+CMS,8G以上可以用G1,几百G以上用ZGC

JVM调优

JDK原生命令

  • jps:查看机器中运行的进程
  • jmap:查看JVM堆内存中各对象的占用的内存空间
  • jstack:查看当前线程栈的使用情况(查看死锁、占用cpu率)
  • jinfo:查看JVM的启动参数
  • jstat:查看JVM的内存使用情况以及GC的次数(定时执行查看GC的增长次数)

可以通过这些原生命令,推算出Full GC的平均执行频率以及执行时长、Young GC平均执行频率以及执行时长、每次Young GC之后会有多大内存的对象进入老年代,可以根据推算出的这些信息来进行JVM内存分配调整。也可以通过开启GC日志,对GC日志进行分析调整。

可以使用dump命令将内存分配情况,栈情况以文件的形式导出,然后放到可视化工具jvisualvm、jprofile里面进行分析,它也可以帮我们分析一部分。

阿里巴巴arthas jvm分析工具:通过下载jar包运行的形式监控整个机器的运行状态,可以很方便的查看各线程以及JVM内存使用情况,以及具体哪个线程出问题。

arthas还是很强大的,它可以通过ognl命令查看线上内存的具体值甚至更改值。

而GC日志分析工具推荐使用gceasy(一款很强大的GC日志分析工具,现在还有部分免费)

你可能感兴趣的:(JVM调优,垃圾回收,jvm,java,算法)