常用命令:
jps 查看线程
jmap -histo {pid}查看堆内对象
jmap -heap {pid}查看堆分代内存(jdk1.8以上报错的话使用 jhsdb jmap --heap --pid {pid})
jstack pid 查看栈线程
jinfo -flags {pid} jvm参数
jinfo -sysprops {pid} 系统参数
jstat -gc pid
----------------------------------------------------------------------------------------------------------------------------------------------------------
内部算法:
复制算法:它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
缺点:浪费一半空间
标记清除算法:标记存活对象,清除其余对象或标记然后清除未存活对象
缺点:产生空间碎片、一个一个标记效率低
标记整理算法:标记与前面一样,只是最后是将存活对象全部移动到一端,使其连续,然后清除其余的空间
缺点:一个一个标记效率低
----------------------------------------------------------------------------------------------------------------------------------------------------------
垃圾收集器
Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC):单线程收集器,收集垃圾的时候STW暂停其他线程
新生代采用复制算法,老年代采用标记-整理算法。
Parallel收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代)):多线程收集器,除了多线程外其余和Serial没区别,目的增大吞吐量高效率利用CPU,在多线程收集垃圾的时候也会STW暂停其他线程,默认的收集线程数跟cpu核数相同,可以用参数(-XX:ParallelGCThreads)指定收集线程数
新生代采用复制算法,老年代采用标记-整理算法。
ParNew收集器(-XX:+UseParNewGC):Parallel的改进版本,在多线程收集垃圾的时候也会STW暂停其他线程,主要不同是可以和CMS整合使用
新生代采用复制算法,老年代采用标记-整理算法。
CMS(Concurrent Mark Sweep)收集器器(-XX:+UseConcMarkSweepGC(old)):实现了用户线程和垃圾回收基本上同时进行,提升了用户体验,采用标记-清除算法
。
CMS大概流程:
1.初始标记: 暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。
2.并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但 是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变,或者新对象产生会被标记为黑色不做任何处理,等待下一次GC回收。。
并发标记阶段->三色标记大概流程:
- 刚开始,所有的对象都是白色,没有被访问。
- 将GC Roots直接关联的对象置为灰色。
- 遍历灰色对象的所有引用,灰色对象本身置为黑色,引用置为灰色。
- 重复步骤3,直到没有灰色对象为止。
3.重新标记: 暂停所有的其他线程(STW),重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法做重新标记。
4.并发清理: 开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑 色不做任何处理,等待下一次GC回收。
5.并发重置:重置本次GC过程中的标记数据。
缺点:
1.对CPU资源敏感(会和服务抢资源)无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了); 2.它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生,当然通过参数XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理 3.执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并 发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent mode failure",此时会进入stop the world,用serial old垃圾收集器来回收
CMS的相关核心参数 1. -XX:+UseConcMarkSweepGC:启用cms 2. -XX:ConcGCThreads:并发的GC线程数 3. -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片) 4. -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,每次FullGC后都会压缩一次 5. -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比) 6. -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设 定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整 7. -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引 用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段 8. -XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW 9. -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW
多标 浮动垃圾:
1.并发标记期间被标记为存活对象,但之后用户线程结束,gc root被摧毁,变成垃圾对象,等待下一次GC。
2.并发标记或并发清除期间用户线程产生的新对象,直接标记为黑色,等待下一次GC。
漏标 读写屏障:
CMS使用增量更新
1.增量更新(IncrementalUpdate):黑色对象一旦有了新的引用(白色对象),则将引用记录,并在并发标记结束后重新扫描黑色对象。
个人理解:当有新增发生时,把黑色对象变回灰色对象,在并发扫描结束后再去扫描一次这个原黑色变为的灰色对象,新白色对象被扫描后第一次成为灰色,第二次扫描灰色时变为黑色(因为会重复扫描直到没有灰色对象。
2.原始快照(Snapshot At The Beginning,SATB):当灰色对象要删除指向白色对象的引用关系时, 就将这个要删除的引用记录下来, 在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根, 重新扫描一次。
个人理解:当有删除发生时,记录下节点的快照,在并发扫描结束后再去扫描一次,因为B被记录到改变了,所以去重新扫一次,是为了保证B标记的正确性,并不是为了要扫到C.目的是得到一条正确的引用链. C通过增量更新已经被扫到了,不会被误回收。
无论是对引用关系记录的插入还是删除, 虚拟机的记录操作都是通过写屏障实现的,在插入删除之前进行操作类似AOP
记忆集卡表:跨代引用
G1收集器(-XX:+UseG1GC):G1将Java堆划分为多个大小相等的独立区域(Region),JVM最多可以有2048个Region,XX:G1HeapRegionSize"手动指定Region大小,Region角色是变化的。
默认年轻代对堆内存的占比是5%,可以通过“-XX:G1NewSizePercent”设置新生代初始占比。jvm在运行中会逐步加大比例但是默认年轻代最大占比60%,可以通过“-XX:G1MaxNewSizePercent”调整,年轻代内部还是8:1:1。
G1垃圾收集器对于对象什么时候会转移到老年代跟之前讲过的原则一样,唯一不同的是对大对象的处理,G1有专门分配大对象的Region叫Humongous区,而不是让大对象直接进入老年代的Region中。在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,就会被放入Humongous中,而且一个大对象如果太大,可能会横跨多个Region来存放。Humongous区专门存放短期巨型对象,不用直接进老年代,可以节约老年代的空间,避免因为老年代空间不够的GC开销。Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收。
G1收集器一次GC的运作过程大致分为以下几个步骤:
1.初始标记(initial mark,STW):暂停所有的其他线程,并记录下gc roots直接能引用的对象,速度很快 ;
2.并发标记(Concurrent Marking):同CMS的并发标记 3.最终标记(Remark,STW):同CMS的重新标记 4.筛选回收(Cleanup,STW):筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期 望的GC停顿时间(可以用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划,比如说老年代此时有1000个 Region都满了,但是因为根据预期停顿时间,本次垃圾回收可能只能停顿200毫秒,那么通过之前回收成本计算得 知,可能回收其中800个Region刚好需要200ms,那么就只会回收800个Region(Collection Set,要回收的集 合),尽量把GC导致的停顿时间控制在我们指定的范围内。这个阶段其实也可以做到与用户程序一起并发执行,但 是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。不管是年轻代或是老 年代,回收算法主要用的是复制算法,将一个region中的存活对象复制到另一个region中,这种不会像CMS那样 回收完因为有很多内存碎片还需要整理一次,G1采用复制算法回收几乎不会有太多内存碎片。(注意:CMS回收阶 段是跟用户线程一起并发执行的,G1因为内部实现太复杂暂时没实现并发回收,不过到了Shenandoah就实现了并 发收集,Shenandoah可以看成是G1的升级版本)
优点:可预测的STW停顿时间XX:MaxGCPauseMillis、并发收集、一个收集器处理整个分代区,没有空间锁片(对比CMS标记清除来说,CMS的目标是一整块内存区域,里面的连续或不连续对象被清除,导致空间碎片(可通过参数最后来整理),但G1的目标是每个Region区域,本身整个堆都已经被分成了很多个Region区域,使用复制算法,把存活的对象直接复制到隔壁的空闲内存区域,所以对Region来说并没有产生空间碎片)
筛选回收个人理解:在开始回收之前,对每个需要回收的Region进行评估,然后在设置或默认的有限时间内进行对收益最大的Region区域进行回收。
G1垃圾收集分类
1.YoungGC:YoungGC并不是说现有的Eden区放满了就会马上触发,G1会计算下现在Eden区回收大概要多久时间,如果回收时间远远小于参数 -XX:MaxGCPauseMills 设定的值,那么增加年轻代的region,继续给新对象存放,不会马上做YoungGC,直到下一次Eden区放满,G1计算回收时间接近参数 -XX:MaxGCPauseMills 设定的值,那么就会触发
2.MixedGC:不是FullGC,老年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区,正常情况G1的垃圾收集是先做MixedGC,主要使用复制算法,需要把各个region中存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC
3.Full GC:停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这 个过程是非常耗时的。(Shenandoah优化成多线程收集了)
G1收集器参数设置 -XX:+UseG1GC:使用G1收集器 -XX:ParallelGCThreads:指定GC工作的线程数量 -XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区 -XX:MaxGCPauseMillis:目标暂停时间(默认200ms) -XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%) -XX:G1MaxNewSizePercent:新生代内存最大空间 -XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个 年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代 -XX:MaxTenuringThreshold:最大年龄阈值(默认15) -XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合 收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能 就要触发MixedGC了 -XX:G1MixedGCLiveThresholdPercent(默认85%) region中的存活对象低于这个值时才会回收该region,如果超过这 个值,存活对象过多,回收的的意义不大。 -XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一 会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。 -XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都 是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清 理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立 即停止混合回收,意味着本次混合回收就结束了。