收集算法(标记-清理、复制、标记-整理、分代收集)是内存回收的方法论,垃圾收集器就是内存回收的具体实现。java
主要有7个gc器,以下图。算法
Serial收集器是单线程的收集器。
单线程:1.不只仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工做,2.且在垃圾收集时,必须暂停其余全部的工做线程,直到它收集结束。
Stop the world:是VM在后台自动发起和自动完成的,在用户不可见状况下把用户正常工做的线程所有停掉。安全
因为Stop The World,给用户带来不良体验,好比,计算机每运行一段时间就会暂停响应几分钟来处理垃圾收集。数据结构
1)简单而高效(与其余收集器的单线程比);
2)对于限定单个CPU的环境来讲,Serial收集器因为没有线程交互的开销,专心作垃圾收集天然能够得到最高的单线程收集效率。多线程
1)VM运行在Client模式下的默认新生代收集器;
2)在用户的桌面应用场景中,停顿时间彻底能够控制在几十毫秒最多一百多毫秒之内,不频繁发生,是可接受的并发
ParNew收集器是Serial收集器多线程版本(是GC线程的多线程,并行)。
• 并行:Parallel指多条垃圾收集线程并行工做,但此时用户线程仍然处于等待状态(多个处理器同时处理多条指令);
• 并发:Concurrent指用户线程与垃圾收集线程同时执行(但并不必定是并行的,可能交替执行),用户程序在继续运行,而垃圾收集程序运行于另外一个CPU上(同一时刻只能有一条指令执行,多个进程指令是交替执行)。性能
在单CPU的环境中绝对不会有比Serial收集器更好的效果,甚至存在线程交互的开销。网站
1)除了Serial收集器外,只有ParNew收集器能与CMS收集器配合工做。
2)CMS(Concurrent Mark Sweep)第一次实现让垃圾收集线程与用户线程(基本上)同时工做。操作系统
运行在Server模式下的VM首选新生代收集器。线程
• 使用-XX:+UseConcMarkSweepGC选项后默认新生代收集器为ParNew收集器;
• 使用-XX:+UseParNewGC选项强制指定使用ParNew收集器;
• 使用-XX:ParallelGCThreads参数限制垃圾收集的线程数;
1)Parallel Scavenge收集器是一个新生代收集器,使用复制算法的收集器,并行的多线程收集器。更关注吞吐量。
2)吞吐量:CPU用于运行用户代码的时间与cpu总消耗时间的比值,即
如虚拟机总共运行100分钟,垃圾收集花费1分钟,则吞吐量是99%;吞吐量高效率利用CPU时间,尽快完成程序的运算任务,主要适合后台运算而不须要太多交互的任务。
3)停顿时间:如CMS等收集器关注点尽量缩短垃圾收集时用户线程的停顿时间,停顿时间越短就越适合须要与用户交互的程序,良好的响应速度能够提高用户体验(适合交互)
1)用户精确控制吞吐量
• 使用-XX:MaxGCPauseMillis参数:控制最大垃圾收集停顿时间
• 使用-XX:GCTimeRatio参数:直接设置吞吐量大小
• 使用-XX:+UseAdaptiveSizePolicy开关参数:GC自适应的调节策略
2)MaxGCPauseMillis参数容许的值是一个大于0的毫秒数,收集器尽量保证内存回收时间不超过设定值。
GC停顿时间缩短牺牲吞吐量和新生代空间——若将MaxGCPauseMillis该值调小带来的问题:系统把新生代调小一些,收集发生更频繁一些,吞吐量降低。
GCTimeRatio参数值是一个大于0且小于100的整数,即垃圾收集时间占总时间的比率,至关于吞吐量的倒数。如设置为19,则最大GC时间占1/(1+19)=5%,默认值为99.则最大容许1/(1+99)=1%的垃圾收集时间。
UseAdaptiveSizePolicy开关参数:VM会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大吞吐量。自适应调节策略是Parallel Scavenge收集器与ParNew收集器的重要区别。
主要适合后台运算而不须要太多交互的任务
Serial Old收集器是Serial收集器的老年代版本,是一个单线程收集器,使用“标记-整理”算法。
1)主要给Client模式下的VM使用。
2)若在Server模式下用,两大用途:1.在JDK1.5及以前的版本中与Parallel Scavenge收集器搭配使用;2.做为CMS收集器备选,并在Concurrent Mode Failure时使用。
Parallel Old是Parallel Scavenge收集器的老年代版本,是一个多线程收集器,使用“标记-整理”算法。在JDK1.6开始提供。
注重吞吐量以及CPU资源敏感的场合,优先考虑Parallel Scavenge + Parallel Old收集器。适合吞吐量优先。
CMS收集器(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器,是基于“标记-清除”算法。
初始标记——并发标记——从新标记——并发清除
• 初始标记:CMS initial mark仅仅是标记一下GC Roots能直接关联的对象,速度快;须要stop the world
• 并发标记:CMS concurrent mark是进行GC Roots Tracing的过程;
• 从新标记:CMS remark是修正并发标记期间因用户程序继续运做而致使标记产生变更的那一部分对象的标记记录,停顿时间比初始标记长,比并发标记短;须要stop the world
• 并发清除:CMS concurrent sweep,清除算法会在收集结束时产生大量空间碎片,有可能致使没有足够大的连续空间来分配当前对象而触发一次Full GC。
1)CMS收集器对CPU资源很是敏感;
2)CMS收集器没法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败(备选用Serial Old)而致使另外一次Full GC的产生;
3)CMS是一款基于“标记-清除”算法的收集器,在收集结束后会产生大量空间碎片。
缺点具体分析
1)对CPU资源敏感:在并发阶段会占用一部分线程而致使应用程序变慢,总吞吐量下降;(解决方法是“增量式并发收集器”,但不提倡使用,i-CMS收集器是与单CPU年代PC机操做系统使用抢占式模拟多任务机制的思想,在并发标记、清理的时候让GC线程、用户线程交替执行,尽可能减小GC线程的独占资源的时间)
2)没法处理浮动垃圾:CMS并发清理阶段用户线程还在运行,会产生新的垃圾,这部分垃圾出如今标记过程以后,CMS没法在当次收集中处理它们,只好留到下一次GC时再处理。CMS须要预留一部分提供并发收集时的程序运行使用,CMS收集时老年代不能填满再收集。
3)收集后产生大量空间碎片:“标记-清除”算法的缺点,解决方案是使用-XX:+UseCMSCompactAtFullCollection和-XX:CMSFullGCsBeforeCompaction参数
并发收集;低停顿(并发低停顿收集器)
在互联网站或者B/S系统的服务端上,重视服务的响应速度,但愿系统停顿时间最短,给用户带来较好的体验。
G1(Garbage-First)收集器是一款面向服务端应用的垃圾收集器,为了代替JDK1.5中发布的CMS收集器。将整个Java堆划分为多个大小相等的独立区域。####(Region),保留新生代和老年代概念,但再也不是物理隔离,是一部分Region的集合(不须要连续)。
并发与并行、分代收集、空间整合、可预测的停顿
• 并发与并行:G1能充分利用多CPU、多核环境下的硬件优点,使用多个CPU缩短storp-the-world停顿时间;G1可经过并发的方式使得java程序运行;
• 分代收集:能够独立管理整个GC堆,采用不一样的方式处理新建立的对象和已经存活一段时间、熬过屡次GC的旧对象以获取更好的收集效果;
• 空间整合:总体上基于“标记-整理”算法,局部(两个Region之间)基于“复制”算法,G1运行期间不会产生内存空间碎片,收集后能提供规整的可用内存,有利于程序长时间运行,分配大对象时不会由于没法得到连续内存空间而提早触发下一次GC;
• 可预测的停顿:相比于CMS的另外一优点,能让使用者明确指定在一个长度为M毫秒的时间片断内,消耗在垃圾收集上的时间不得超过N毫秒。由于能够有计划地避免在整个java堆上进行全区域的垃圾收集。G1跟踪各个Region内垃圾堆积的价值大小(回收所得到的空间大小+回收所需时间的经验值),在后台维护一个优先列表,根据容许的收集时间,回收价值最大的Region(Garbage-First的由来)。
Region之间的对象引用以及其余收集器中的新生代与老年代之间的对象引用,VM都是经过Remember Set来避免全堆扫描。G1中每一个Region中都有一个与之对应的Remember Set:
1)VM发现程序对Reference类型的数据进行写操做时,会产生一个Write Barrier暂时中断写操做;
2)检查Reference引用的对象是否处于不一样的Region之中;若是是,便经过CardTable把相关引用信息记录到被引用对象所属的Region的Remember Set之中;
3)当进行内存回收时,在GC根节点的枚举范围中加入Remember Set便可保证不对全堆扫描;
初始标记——并发标记——最终标记——筛选回收
筛选回收:live data counting and evacuation,对各个Region的回收价值和成本进行排序,根据用户所指望的GC停顿时间来指定回收计划。
1)Safepoint:在HotSpot的实现中,使用一组称为OopMap的数据结构,在类加载完成的时候,HotSpot把对象带内什么偏移量上是什么类型的数据都计算出来,在JIT编译过程当中,也会在特定的位置记录下栈和寄存器中哪些位置是引用,这些特定的位置就是安全点。
2)程序执行时并不是在全部的地方都能停顿下来开始GC,只有到达安全点时才能暂停。
3)安全点的选定:标准是“是否具备让程序长时间执行的特征”,不能太少以致于让GC等待时间太长,也不能过于频繁以致于过度增大运行时的负荷。
如何在GC发生时让全部线程都“跑”到最近的安全点停顿?两种方案:抢先式中断和主动式中断。
抢先式中断:不须要线程的执行代码主动去配合,在GC发生时,首先把全部线程所有中断,若是发现有线程中断的地方不在安全点上,就恢复线程,让它“跑”到安全点上。(如今几乎不用)
主动式中断:当GC须要中断线程的时候,不直接对线程操做,仅仅设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就本身中断挂起。轮询标志的地方和安全点是重合的,另外再加上建立对象须要分配内存的地方。
1)safepoint保证程序执行时,在不太长的时间内就会遇到可进入的GC的safepoint。
2)若程序不执行,则没有分配CPU时间,如线程处于Sleep或Blocked状态,没法响应JVM的中断请求,此时须要安全区域解决,在一段代码片断中,引用关系不会发生变化,在这个区域中的任意地方开始GC都是安全的。
主要参考了《深刻理解Java虚拟机》这本书,这本书是真心好,值得一读。