接上次JVM虚拟机堆内存模型来继续说,上次咱们主要说了何时可能把对象直接放在老年代,还有咱们的可能性分析,提出GCroot根的概念。此次咱们主要来讲说垃圾回收所使用的的算法和咱们的垃圾回收器,须要了解咱们的可达性分析GCroot根是什么,还有咱们的动态年龄判断和老年代分配担保机制,还不清楚咋回事的小伙伴能够去我上几篇JVM的博客去看一下,JVM内存模型的几篇博客 http://www.javashuo.com/article/p-pvpovcun-eh.html html
垃圾回收算法,主要就三种,标记清除,复制,标记整理。算法
标记清除:服务器
上图说话。多线程
解释一下,标记清理算法,咱们能够视为每个位置去观察,可以清理就标记,不能清理就放那不动。也能够想象为咱们打扫屋子,咱们只扔掉咱们不想要的物品,其他想留下的东西仍是放在原处不动。并发
这种算法清理缺点是效率并不高,并且会带来大量的空间碎片,咱们能够由下面的小方格看到,红色的格子不是连续的,而是分布在不一样位置的。这时进来一个大对象,须要多个连续的格子,就可能放不下了。优化
复制:spa
很少了直接上图。线程
和上面相比,咱们至少能够看到连续的红格子了,复制算法就是查看每个小格子,能够回收就回收掉,不行就挪到保留的另外一半内存中去,每次只标记总体区域的一半,咱们仍是拿收拾屋子举例,复制算法就像是,咱们把卧室收拾一遍,不要的扔掉,想留下的整齐的放在客厅,下次咱们收拾客厅也是如此,每次收拾一半。缺点就是内存利用率低,只能使用一半,还须要保留一半的区域为了来回挪动存活对象。3d
标记整理:htm
标记整理是是标记清除的升级版,优势:解决内存碎片问题。缺点:整理阶段,因为移动了可用对象,须要去更新引用。仍是收拾屋子的那个例子,咱们把没用的物品扔掉,其他物品整齐的摆放起来。
接下来就是和咱们回收算法对应的回收器了。
Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
Serial(串行)垃圾收集器是最基本、发展历史最悠久的收集器;做用于新生代时采用采用复制算法;Serial Old收集器也就是Serial做用于老年代时采用标记整理算法。
采用单线程手机模式来收集。
ParNew收集器(-XX:+UseParNewGC)
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其他行为 (控制参数、收集算法、回收策略等等)和Serial收集器彻底同样。默认的收集线程数跟cpu核数相同,固然也能够用参数(-XX:ParallelGCThreads)指定收集线程数,可是通常不推荐修改。 而且单核CPU的服务器,优先考虑Serial收集器,甚至因为存在线程交互的开销,效果不必定强于Serial收集器,可能Serial收集器效果更好,parNew新生代采用复制算法,老年代采用标记-整理算法。 建议使用在新生代,后面会说为何。
Parallel Scavenge收集器(-XX:+UseParallelGC,- XX:+UseParallelOldGC)
Parallel Scavenge 收集器相似于ParNew 收集器,是Server 模式(内存大于2G,2个cpu)下的默认收集器, Parallel Scavenge更加关注于CPU的使用率,可能在回收的过程瞬间CPU使用率提升进行垃圾回收。Parallel Old收集器是Parallel Scavenge收集器的老年代版本。和上面的图同样 ,我就再也不贴图了。新生代采用复制算法,老年代采用标记-整理算法。
CMS收集器(-XX:+UseConcMarkSweepGC(old))
相对上面几个收集器来讲稍微复杂一些,先看图。
由图来看确实复杂了很多,可是并非一直处于STW阶段,中间还有并行的时候。咱们来看一下每个阶段都是作什么的。
初始化标记阶段:
这里STW时间很是短,微乎其微的,这里只标记GCRoot根直接引用的对象,不往下层去搜索更多的对象进行标记,效率很高,STW时间很短。
并发标记阶段:
并发标记阶段是最消耗时间的,在上一个阶段只标记GCRoot根的直接引用对象,这个阶段是把全部须要回收的对象都须要标记出来。可能引用不少,因此耗时较大。可是还好,他不会出现STW,不会停掉全部线程为其单一服务。
从新标记阶段:
在上一个相对比庞大的并发标记阶段,并无STW,可能会产生新的GCRoot根,或者说原有不须要回收的对象如今已经变为垃圾对象了,咱们在从新标记阶段再一次来作一下处理,这里又会出现STW现象,相比并发标记阶段时间也是很短的。
并发清理阶段:
恢复咱们的正常的线程,开始清理没有标记的对象,这里不会产生STW,在这个阶段再进来的新对象,或者产生对象的变动,CMS是不会继续处理的,会在下一次垃圾回收再来处理这些对象。
并发重置阶段:
清楚全部标记,为下一次垃圾回收作准备。
CMS垃圾收集器步骤比较多,可是咱们能够看出明显提升了效率,中间至少不是持续的STW的。可是也有CMS的弊端的,并发标记阶段和并发清理阶段很容易和咱们正常的线程抢占CPU的。再就是他的算法只是标记清理,并无整理内存碎片,但能够调配参数作到整理碎片的目的。最坑的问题来了,就是咱们在并发标记阶段,可能进来新的对象,原本咱们老年代就快满了,才进行的垃圾收回,这时这些对象过大过多,会再次执行CMS的垃圾回收,形成concurrent mode failure,这时会变动为Serial收集器,产生较长的STW时间。
CMS相关参数:
1. -XX:+UseConcMarkSweepGC:启用cms垃圾回收器
2. -XX:ConcGCThreads:并发的GC线程数目
3. -XX:+UseCMSCompactAtFullCollection:FullGC以后作压缩整理(减小碎片)
4. -XX:CMSFullGCsBeforeCompaction:多少次FullGC以后压缩一次,默认是0,表明每次 FullGC后都会压缩一次,依赖上面的参数,必须配置-XX:+UseCMSCompactAtFullCollection才生效
5. -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认 是92,这是百分比),可能出现JVM自我优化变动的现象,不是很稳定
6. -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),若是不指定,JVM仅在第一次使用设定值,后续则会自动调整,比上面的-XX:CMSInitiatingOccupancyFraction稳定不少
7. -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减小 老年代对年轻代的引用,下降CMS GC的标记阶段时的开销,通常CMS的GC耗时 80%都在从新标记阶段
今天先说这么多 ,还有一个G1收集器没有说,下次博客会说G1收集器,和常见的调优方式,有时间我再整理一份经常使用的命令。
看起来真的吃力的话,建议先看一下我前几篇JVM相关的博客,JVM内存模型的几篇博客 http://www.javashuo.com/article/p-pvpovcun-eh.html
最进弄了一个公众号,小菜技术,欢迎你们的加入