概述
- 垃圾回收的方法:复制(年轻代,浪费空间但是快)、标记清除(CMS,同时CMS支持N次FGC后压缩一次,速度快但有内存碎片)、标记压缩(节省空间但延长STW,parallel回收器)、分代回收(G1)
- GC Roots(栈区和方法区):jvm栈、本地方法栈、静态成员变量的引用、常量池对象的引用
- 系统默认JVM 启动内存1/64,最大内存1/4,充分利用系统内存需要自己配置Xms Xmx
- 垃圾回收期的种类,回收器是针对老年代和年轻代的
- Serial单线程模式,Serial + Serial Old,单线程,低CPU配置电脑上性能好,CMS promotion fail会切换S O
- Parallel追求的是吞吐量最大,Parallel Scanvenge + Parallel Old,多线程,自动调节带区大小。适合那种报表型的应用,不用交互
- Cms追求的最小停顿时间,parNew + Cms 多线程,适合交互类的应用,比如网站
- G1 :把堆区分为多个相对独立的Rigion块,来实现更加灵活的垃圾回收,在满足设定的最长STW时间的限制下,实现最大吞吐量。G1各个区的大小是JVM自动调整的。参数MaxGCPauseMillis。适用机器配置很高的环境
G1回收器
吞吐量和最短停顿时间本来就互相矛盾,Parallel Old追求的是吞吐量,CMS追求的是STW的最短,而G1通过把堆分成多个相对独立的Region块,并行的进行选择性的回收,实现一个两者兼顾的回收器。
G1类似于CMS,G1回收器支持用户并发操作,MajorGC在并发标记时允许用户线程同时进行业务操作。
G1相对CMS而言,更加灵活,因为内部是多个Rigion组成,而且每个Rigion是独立的(Rigion之间对象的相互引用记录在Remembered Set里面),所以可以根据用户设定的允许最长停顿时间,在一次MajorGC的过程中只回收部分Rigion(回收空间价值最高而经验推算的时间成本又最低的Rigion块,比如Rigion1对象活跃度10%,Rigion2对象活跃度50%,那么优先回收Rigion1块),这是CMS无法做到的,CMS的老年代是完整的一块内存,要GC的话,就必须整个老年代全部GC,而整个老年代全部GC的话,停顿的时间就会相对更长。年轻代来看,也更加灵活,因为年轻代的Eden区和S区的Rigion块数量是动态调整的。
G1相对CMS而言,因为采用的是复制算法,所以没有内存碎片的产生。避免了堆中大对象因为没有足够的连续的内存空间而触发MajorGC的情况。其实CMS之所以使用标记清除算法,是为了减少压缩垃圾过程时的STW,因为G1可以做到部分而不是所有Rigion块的GC,所以可以在预测的时间内完成复制操作。
G1的适合大内存的JVM环境。因为采用复制的算法(YGC和MajorGC都是复制),所以需要有一定的空闲内存块来存储复制来的对象,空间利用率低。而且每次GC都不是完整的GC,需要该Region块的活跃度低于一定标准,这个Region块才会发起回收,所以一些活跃度高的内存块里面的垃圾对象不会被回收掉,默认堆内存使用率达到45%的时候,就开始FullGC。
G1适合多CPU的JVM环境。因为传统的回收器都是3个内存块,而G1默认是2000多个相对独立的内存块,在GC几乎所有的过程中,Region块都是高度并行化的进行回收,而且还有在并发标记阶段支持用户并发性的业务操作,所以对CPU的要求较高
G1的灵活性还在于能够根据设置的STW时间,动态调整各个分代的内存占比,有点类似于Parallel Old,但是和CMS是不同的。
G1回收器FullGC回收过程
- 初始标记,STW,分为老年代和年轻代两部分。先会触发一次YGC(排除掉年轻代对老年代有引用,但是该年轻代对象已经死亡的情况),然后再对Survivor to区进行遍历(确保所有的年轻代的对象都是活的,因为经历了一次YGC),查找并标记S区对象对老年代的直接引用的所有老年代对象(这里是直接引用,比如Survivor to区A对象,引用了老年代的B对象,就标记B对象,虽然B对象还可能引用了C、D、E对象)。第二部分是针对老年代,标记老年代中所有GC Roots直接引用的对象。
- 并发标记,根据可达性分析,找到所有GCRoots的对象引用,一层一层的标记,这个时候没有STW,用户线程正常执行。
- 最终标记,STW,修正并发标记期间的对象关系变更,根据Remembered Set Logs,来修改Remember Set,该阶段可以多个线程并行
- 筛选回收,STW,会跟踪各个Rigion里面的垃圾堆积的价值大小(比如需要回收的对象的空间大小以及回收所需时间的经验值)排序,根据用户设定的停顿时间,回收特定Region,因为每个Region块都有独立的Remembered Set,所以每个Region可以独立的进行回收,也就是多个Region块并行多线程的垃圾回收。
G1回收的细节
- 当执行垃圾回收时,G1以类似于CMS回收器的方式运行。G1执行一个并发的全局标记阶段,以确定整个堆中对象的活跃度。
- G1使用一个暂停预测模型来满足用户定义的暂停时间目标并基于指定的暂停时间目标选择回收的区域数量
- G1因为经常性的不完整GC,活跃度高的内存块GC的会更晚,而且需要有空闲的Region来实现复制,进行垃圾回收的时机更早,默认在堆内存使用率达到45%时就会开始垃圾回收。这体现了G1(Garbage-First/垃圾回收优先)这一名字的含义。至于活跃度多低才会进行回收,则是由G1决定的,G1会调整自己的回收策略来尽可能满足用户设置的最大STW时长。
- 每个Rigion都有一个Remembered Set,来纪录Region块之间的对象引用,RSet使得区域并行独立的回收成为可能。当程序发生写操作的时候,会判断相关的引用是否涉及到跨Region的引用,比如Rigion0当中,A对象之前是引用Rigion0中的B对象,现在执行程序,A对象被重新赋值,引用了Rigion1中的C对象,那么在Rigion1的Remembered Set当中就要纪录,否则Region1在GC的时候,会把C对象直接回收了,通过读取自己的Remembered Set知道C对象被Region0种的A对象引用,这样既避免了全堆的扫描,又实现了分块管理。
- FullGC触发的时机是内存使用率达到一定的比例,默认45%