尽管Go
1.5刚刚发布,仍然相对较新,但是Go团队已经在致力于改进其新的低延迟并发垃圾收集器,希望Go更适合新的应用领域。Google工程师Austin
Clements和Rick
Hudson如是说。

1、serial收集器

serial是一个单线程的垃圾收集器,它只会使用一个cpu、一个收集线程工作;它在进行gc的时候,必须暂停其他所有线程到它工作结束(这种暂停往往让人难以接受)。

对于单个cpu的情况下,serial是没有线程交互的开销,效率要好于其他。例如在一个简单的桌面应用,只有几十M的内存,他的停顿时间可能只有几十毫秒,所以一般默认的client模式下都是用的serial做默认垃圾收集器。

serial收集器

hbase gc调优(CMS与G1)

Go 1.5用新的垃圾收集器代替了原来的STW(stop-the-world)
GC,解决了延迟问题。当负载较重时,每50ms,新的GC的活动时间可以控制在10ms以下,从而使Go程序在一般情况下能运行得更快些。在更极端的情况下,停顿可以从300ms降到4ms。

2、parnew收集器

parnew其实就是serial的多线程版本,parnew在单线程的情况下甚至不如serialparnew是除了serial之外唯一能和CMS配合的。

parnew默认开启收集线程数和cpu的数量相同,我们可以利用-XX:ParallelGCThreads参数来控制它开启的收集线程数。

parnew收集器

一:hbase gc调优目的:

在HBase中,有两个在内存中的结构消费了绝大多数的heap空间。BlockCache缓存读操作的HFileblock,Memstore缓存近期的写操作,基于HBase是一个响应时间敏感,并且需要对GC时间可控的应用的出发点。

Go
1.6的目标是进一步稳定GC,并在以下几个方面做出改进:

3、parallel scavenge收集器

parallel scavenge主要就是关注吞吐量。
所谓吞吐量:运行用户代码的世界/(运行用户代码时间+GC花费的时间)。
parallel scavenge收集器中,提供了2个参数来控制吞吐量:

  • -XX:GCTimeRatio:gc时间占用的总比例,也就是吞吐量的倒数。
  • -XX:MaxGCPauseMillis:最大的暂停毫秒数(这个数值并非越小越好,如果把他设置小了,系统会根据这个值调整空间的大小,也就会加快GC的频率)

parallel scavenge可以设置开启-XX:UseAdaptiveSizePolicy,开启这个参数之后就无需关注新生代大小eden和survivor等比例,晋升老年代对象年龄的这些细节了。

parallel scavenge的工作细节

二:hbase的gc调优本文通过CMS与g1两种算法进行设置参考。

需要大致了解两种垃圾收集器的概念及不同

  • 状态协调(State coordination):Go 1.5
    GC有个主要瓶颈是从Go
    1.4继承来的,源自其集中式的GC协调器(coordinator),这是一个单独的goroutine,它会将工作进一步分派给worker
    goroutine去完成。一种解决方案是用去中心化的状态机代替集中式协调器。这样修改有个额外的好处,使得重新设计标记完成屏障(mark completion
    barrier)成为可能,因为它已经变得非常凌乱,而且性能很差。

  • 信用系统(Credit system):Go
    1.5在两个不同的地方使用了一个信用系统:一个是确保清扫(sweeping)在一个GC周期和下一次触发堆操作(the
    next heap
    trigger)之间完成,一个是确保扫描(scanning)在触发堆操作(an heap trigger)和随后实现堆处理目标之间完成。改进信用系统的一种建议方法是,使其操作总是在black阶段进行,以避免未完成的分配操作进入下一个GC周期。

  • 标记结束(Mark
    termination):
    根据Clements和Hudson的介绍,在Go
    1.5中,标记结束阶段是停顿时间的大头。这里的目标是尝试并确保大部分应用可以在10ms停顿的阈值下运行,这也是Go
    1.5在很多情况下已经实现了的。希望所做修改的复杂度较低或中等,比如把finalizer扫描从标记结束阶段移到并发扫描,这样对于每1GB大小的堆,应该可以节省1ms,以及去掉一个成本很高的计数循环,对于较大的堆,这个循环占去了标记阶段的另外一半。

  • sweeper和scavenger:某些程序会在sweeper上消耗大量时间,在这上面投入些精力,应该有性能改进。一个非常激进的方案是完全去掉sweeper。还有一个不那么激进的方案,可以在GC阶段最后,尽早释放较大的对象,并且在所有的系统上支持scavenger,不管物理页面是多大。

澳门新葡萄京官网首页 ,4、serial old收集器

serial收集器的老年代版本,使用标记整理算法,主要有两个作用:

  • jdk5之前和parallel scavenge配合使用
  • 作为cms失败的后备收集方案

三:几个基本概念:

  1. Full GC == Major GC指的是对老年代/永久代的stop the world的GC
  2. Full GC的次数 = 老年代GC时 stop the world的次数
  3. Full GC的时间 = 老年代GC时 stop the world的总时间
  4. CMS 不等于Full GC,我们可以看到CMS分为多个阶段,只有stop the
    world的阶段被计算到了Full
    GC的次数和时间,而和业务线程并发的GC的次数和时间则不被认为是Full
    GC
  5. Full GC本身不会先进行Minor GC,我们可以配置,让Full
    GC之前先进行一次Minor
    GC,因为老年代很多对象都会引用到新生代的对象,先进行一次Minor
    GC可以提高老年代GC的速度。比如老年代使用CMS时,设置CMSScavengeBeforeRemark优化,让CMS
    remark之前先进行一次Minor GC。

上面只是对计划所做改进的一个概览,欲全面了解,可以阅读原始文档。文档中还有进一步指向GitHub
issues的链接,这些issues记录 了每个改变背后的理由以及建议方案。

5、parallel old收集器

是parallel收集器的老年代版本,用于和parallel收集器搭配组合的,因为parallel收集器不能和cms组合,但是和serial
old收集器效率又太低。
对吞吐量和CPU敏感度特别关注的应用可以使用parallel+parallel old的组合。

四:CMS调优:

转载自:infoq.com/cn

6、CMS收集器

CMS的适用特点:

  • 希望JAVA垃圾回收器回收垃圾的时间尽可能短;
  • 应用运行在多CPU的机器上,有足够的CPU资源;
  • 有比较多生命周期长的对象;
  • 希望应用的响应时间短。

CMS的过程:

  1. 初始标记:标记一下GC
    ROOT能直接关联的对象,速度很快,这个阶段是会STW。
  2. 并发标记:GC ROOT的引用链的查找过程,标记能关联到GC
    ROOT的对象,这一个阶段是不需要STW的。
  3. 重新标记:在并发标记阶段,应用的线程可能产生新的垃圾,所以需要重新标记,这个阶段也是会STW。
  4. 并发清除:这个阶段就是真正的回收垃圾的对象的阶段,无需STW。

澳门新葡萄京官网首页 1

CMS运行过程

CMS的缺点:

  1. 对cpu比较敏感。
  2. 可能出现浮动垃圾:在并发清除阶段,用户还是继续使用的,这时候就会有新的垃圾出现,CMS只能等下一次GC才能清除掉他们。
  3. CMS运行期间预留内存不够的话,就会出现concurrent Mode Failure,这时候就会启动serial收集器进行收集。
  4. CMS基于标记清除算法实现,会产生内存碎片空间。碎片空间过多就会对大对象的分配空间造成麻烦。为了解决碎片问题,CMS提供一个参数来控制是否在GC的时候整合内存中的碎片,这个碎片整合的操作是无法并发的,会延长STW的时间。

1.CMS收集器在老年代内存回收中执行的阶段说明:

(1) 初始标记 (Initial Mark) (Stop the World
Event,所有应用线程暂停):在老年代(old generation)中的对象,
如果从年轻代(young generation)中能访问到, 则被 “标记,marked”
为可达的(reachable).对象在旧一代“标志”可以包括这些对象可能可以从年轻一代。暂停时间一般持续时间较短,相对小的收集暂停时间.

(2) 并发标记 (Concurrent
Marking)
:在Java应用程序线程运行的同时遍历老年代(tenured
generation)的可达对象图。扫描从被标记的对象开始,直到遍历完从root可达的所有对象.
调整器(mutators)在并发阶段的2、3、5阶段执行,在这些阶段中新分配的所有对象(包括被提升的对象)都立刻标记为存活状态.

(3) 再次标记(Remark):(Stop the World Event, 所有应用线程暂停)
查找在并发标记阶段漏过的对象,这些对象是在并发收集器完成对象跟踪之后由应用线程更新的.

(4) 并发清理(Concurrent Sweep):回收在标记阶段(marking
phases)确定为不可及的对象.
死对象的回收将此对象占用的空间增加到一个空闲列表(free
list),供以后的分配使用。死对象的合并可能在此时发生.
请注意,存活的对象并没有被移动.

(5) 重置(Resetting):清理数据结构,为下一个并发收集做准备.

7、G1收集器

G1的特点:

  • 利用多CPU来缩短STW的时间
  • 可以独立管理整个堆(使用分代算法)
  • 整体是基于标记-整理,局部使用复制算法,不会产生碎片空间
  • 可以预测停顿:G1吧整个堆分成多个Region,然后计算每个Region里面的垃圾大小(根据回收所获得的空间大小和回收所需要的时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。

G1的运行过程:

  • 初始标记:标记一下GC
    ROOT能直接关联的对象,速度很快,这个阶段是会STW。
  • 并发标记:在GC
    ROOT中运用可达性分析算法,找出存活的对象,耗时较长,但是无需STW。
  • 最终标记:修正并发标记期间用户线程对垃圾对象的修改,需要停顿线程,但是可以并行执行。
  • 筛选回收:先计算回收各个Region的价值,然后根据用户需求来进行回收。

澳门新葡萄京官网首页 2

G1运行示意图

2.CMS的失效模式(CMS Failure)

并发模式失败

第一种失败的模式,是简单的并发模式失败。最好的一个例子:假设有一个8GB堆,已经使用了7GB。当CMS的收集开始第一阶段,它欢快的隆隆的做着并发标记。与此同时,有更多的数据被分配到老年代。如果老年代增长的速度太快,在CMS完成第一阶段标记工作之前就填满了全部老年代。这时候因为没有自由空间,CMS就无法工作!CMS必须放弃并行工作,并回落到停止世界(stop-the-world)单线程复制收集算法。此算法开始搬迁堆,检查所有活动对象,并释放了所有死角。长时间的停顿后,该程序可能会继续。

但我们可以很容易的通过调整JVM参数避免并发模式失败:我们只需要鼓励CMS提前开始工作!设置-XX:CMSInitiatingOccupancyFraction

N,其中N是堆在开始收集过程中的百分比。HBase仔细的计算了内存使用,以保持其只使用60%的堆空间,所以我们通常将此值设置为大约70。(译者注:同时也可以考虑Old区的30%要比Young区大,这样即使Young区在CMS之前全部搬迁到Old区也不会把Old区填满)

碎片导致的CMS失败

这种故障模式是多一点复杂。回想一下,CMS收集不搬迁对象,而是简单地跟踪所有堆的自由空间,而且自有空间是分开的。作为一个思想实验,想象我拨出1亿个对象,每个1KB,这正是1GB堆的总用量为1GB。然后,我释放所有奇数对象,所以我有500MB的自有空间,然而自由空间都是1KB的块。如果我需要分配一个2KB的对象,尽管我表面上有500MB免费空间,依然会无处可放。这就是所谓的内存碎片。因为CMS不搬迁对象,不管如何让CMS提前启动,都不可以解决这个问题!发生此问题时,CM再次回落到复制收集器,该方法能够压缩所有的对象并释放。

3.CMS收集器参数设置参考:

-Xmx64g -Xms64g -Xmn2g -Xss256k 
-XX:MaxPermSize=256m -XX:SurvivorRatio=2  
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC 
-XX:+CMSParallelRemarkEnabled -XX:MaxTenuringThreshold=15 -XX:+UseCMSCompactAtFullCollection  -XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=75 -XX:-DisableExplicitGC 
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/usr/local/deploy/logs/gc.log

4.参数说明:

Xmx 分配给JVM最大堆内存

Xms 分配给JVM初始堆内存

Xmn 分配给young区的内存大小

Xss 分配给每个线程的堆栈大小

-XX:MaxPermSize 分配给持久代的大小

-XX:SurvivorRatio
表示young区中Eden与Survivor区的内存大小比例,默认为8.

-XX:+UseConcMarkSweepGC 该标志首先是激活CMS收集器。默认HotSpot
JVM使用的是并行收集器。

-XX:+UseParNewGC
当使用CMS收集器时,该标志激活年轻代使用多线程并行执行垃圾回收。这令人很惊讶,我们不能简单在并行收集器中重用-XX:UserParNewGC标志,因为概念上年轻代用的算法是一样的。然而,对于CMS收集器,年轻代GC算法和老年代GC算法是不同的,因此年轻代GC有两种不同的实现,并且是两个不同的标志

-XX:+CMSParallelRemarkEnabled
当该标志被启用时,并发的CMS阶段将以多线程执行(因此,多个GC线程会与所有的应用程序线程并行工作)。该标志已经默认开启,如果顺序执行更好,这取决于所使用的硬件,多线程执行可以通过-XX:-CMSConcurremntMTEnabled禁用。

-XX:MaxTenuringThreshold
如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代.对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活
时间,增加在年轻代即被回收的概率,该参数只有在串行GC时才有效.

-XX:+UseCMSCompactAtFullCollectionCMS 是不会移动内存的, 因此,
这个非常容易产生碎片, 导致内存不够用, 因此,
内存的压缩这个时候就会被启用。
增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片

-XX:+UseCMSInitiatingOccupancyOnly 禁止hostspot自行触发CMS GC

-XX:CMSInitiatingOccupancyFraction
使用cms作为垃圾回收使用70%后开始CMS收集

-XX:-DisableExplicitGC 关闭System.gc()

五:g1垃圾收集器:

1.概念介绍:

G1
(Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器.
以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征. 在Oracle
JDK 7 update 4 及以上版本中得到完全支持, 专为以下应用程序设计:
可以像CMS收集器一样,GC操作与应用的线程一起并发执行
紧凑的空闲内存区间且没有很长的GC停顿时间.
需要可预测的GC暂停耗时.
不想牺牲太多吞吐量性能.
启动后不需要请求更大的Java堆.
G1的长期目标是取代CMS(Concurrent Mark-Sweep Collector, 并发标记-清除).
因为特性的不同使G1成为比CMS更好的解决方案.
一个区别是,G1是一款压缩型的收集器.G1通过有效的压缩完全避免了对细微空闲内存空间的分配,不用依赖于regions,这不仅大大简化了收集器,而且还消除了潜在的内存碎片问题。除压缩以外,G1的垃圾收集停顿也比CMS容易估计,也允许用户自定义所希望的停顿参数(pause
targets)

2.G1收集器在老年代堆内存中执行阶段.(注意有些阶段也是年轻代垃圾收集的一部分):

(1) 初始标记(Initial Mark):(Stop the World
Event,所有应用线程暂停) 此时会有一次 stop the world(STW)暂停事件.
在G1中, 这附加在(piggybacked on)一次正常的年轻代GC.
标记可能有引用指向老年代对象的survivor区(根regions).

(2) 扫描根区域(Root Region Scanning):扫描 survivor
区中引用到老年代的引用. 这个阶段应用程序的线程会继续运行.
在年轻代GC可能发生之前此阶段必须完成.

(3) 并发标记(Concurrent Marking):在整个堆中查找活着的对象.
此阶段应用程序的线程正在运行. 此阶段可以被年轻代GC打断(interrupted).

(4) 再次标记(Remark):(Stop the World Event,所有应用线程暂停)
完成堆内存中存活对象的标记. 使用一个叫做
snapshot-at-the-beginning(SATB, 起始快照)的算法,
该算法比CMS所使用的算法要快速的多.

(5) 清理(Cleanup):(Stop the World
Event,所有应用线程暂停,并发执行)

在存活对象和完全空闲的区域上执行统计(accounting). (Stop the world)

擦写 Remembered Sets. (Stop the world)

重置空heap区并将他们返还给空闲列表(free list). (Concurrent, 并发)

(*) 拷贝(Copying) (Stop the World Event,所有应用线程暂停)
产生STW事件来转移或拷贝存活的对象到新的未使用的heap区(new unused
regions). 只在年轻代发生时日志会记录为 [GC pause (young)].
如果在年轻代和老年代一起执行则会被日志记录为 [GC Pause (mixed)].

3.G1收集器参数配置参考:

-XX:+UseG1GC 
-Xms100g -Xmx100g 
-XX:MaxGCPauseMillis=100 
–XX:+ParallelRefProcEnabled 
-XX:-ResizePLAB 
-XX:ParallelGCThreads=28
Promotion Local Allocation Buffers(PLABs)是在Young代回收时被使用。并且是多线程。每个需要分配空间的对象被拷贝到Survior或者Old代。PLABs 需要避免使用线程共享的数据结构为了管理空闲内存。每个GC线程有一个PLAB用于一个Survival区和一个Old区。我们需要重新配置PLAB的大小来避免GC线程间的大量通信,这也是影响GC的一个变量
-XX:G1NewSizePercent=1修改Eden大小
-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps 
-XX:+PrintAdaptiveSizePolicy

4.G1收集器参数说明:

-XX:+UseG1GC 使用 G1 (Garbage First) 垃圾收集器

-XX:MaxGCPauseMillis=n 设置最大GC停顿时间(GC pause
time)指标(target). 这是一个软性指标(soft goal), JVM
会尽量去达成这个目标.

-XX:InitiatingHeapOccupancyPercent=n
启动并发GC周期时的堆内存占用百分比.
G1之类的垃圾收集器用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比.
值为 0 则表示”一直执行GC循环”. 默认值为 45.

-XX:NewRatio=n 新生代与老生代(new/old
generation)的大小比例(Ratio). 默认值为 2.

-XX:SurvivorRatio=n eden/survivor 空间大小的比例(Ratio). 默认值为
8.

-XX:MaxTenuringThreshold=n 提升年老代的最大临界值(tenuring
threshold). 默认值为 15.

-XX:ParallelGCThreads=n
设置垃圾收集器在并行阶段使用的线程数,默认值随JVM运行的平台不同而不同.

-XX:ConcGCThreads=n 并发垃圾收集器使用的线程数量.
默认值随JVM运行的平台不同而不同.

-XX:G1ReservePercent=n
设置堆内存保留为假天花板的总量,以降低提升失败的可能性. 默认值是 10.

-XX:G1HeapRegionSize=n
使用G1时Java堆会被分为大小统一的的区(region)。此参数可以指定每个heap区的大小.
默认值将根据 heap size 算出最优解. 最小值为 1Mb, 最大值为 32Mb.