HotSpot的Young区诊断和优化

在双11之前,做了一些UMP GC优化的事情,和大家分享下问题查找和优化的思路。

一.   一些GC基础知识

1.      大部分jvm都有分代的概念,堆被分成2个部分,一个Young区,一个old区


2.      -Xmx设置堆的最大值,-Xmn设置young区的大小,减一下就是old区的大小

3.      Young区又分为Eden,survivor(s0,s1,大小通过SuvivorRatio指定)区,新生对象一般都在eden区分配,Hotspot为了优化对象创建的效率,给每个线程默认分配了部分内存TLAB,线程先在自己的TLAB空间创建对象,如果不够则去eden区分配。Survivor区是为了让生命周期短的对象尽量在young区就被回收掉,也就是每次young区执行gc之后,eden区和s0/s1中的一个都会被清空,只剩下s0/s1的一个存放对象

4.      Hotspot的Young区gc有3种算法,Serial,ParNew,PS

5.     一般Young区触发gc的条件是eden区满

6.     ParNew算法是stop-the-world的,也是说执行的时候所有用户线程会暂停

7.      更多gc知识可以毕玄大师的分享,http://www.docin.com/p-417999249.html

二.   ParNewGC运行时间分析

1.      Gc root

Young gc运行时,gc root一般是从线程的栈帧出发查找活跃对象,还有一个不可忽视的root是old区的对象,比如在运行期某个线程修改了某个old区对象的某个引用,则这个对象也需要作为gc root进行扫描以决定哪些young区的对象还活着,为了避免young gc时扫描整个old区,hotspot提供了dirty card机制来优化old区的扫描。


2.      Dirty card

Hotspot将old区按512字节的page进行划分,存放到内部的dirty card列表中,当线程修改某一个old区对象的某个引用时,就会更新这个对象对应的dirtycard为’脏数据’。这样young gc的时候就需要扫描这些dirty page中的对象来决定young区哪些对象还活着。


3.      所以Young gc的时间,就可以这样表述: Tyoung =Tstack_scan + Tcard_scan +Told_scan+ Tcopy 。 Tstack_scan 是扫描线程栈找出活跃对象的时间,一般比较快。Tcard_scan  扫描dirtycard表的时间,取决于old区的大小。Told_scan 如果某一页page有脏数据,则把它作为gc root扫描,取决于old区的大小和具体业务是否有产生’脏数据’。Tcopy 是复制存活对象到s0/s1,更新引用地址的时间,取决于存活对象数量,大小。其中主要影响的时间是Told_scan+ Tcopy 。

三.   UMP的jvm参数和gc方式

为了了解UMP应用的gc情况,我们先看下线上ump应用的启动参数: ps –ef|grep java


1.      ump的堆设为4G,young区为1600m,suvivorRatio为10,则eden区大小为1600* (1-2/12)=1333.33M,s0=s1=133.33M

2.      –XX:+UseConcMarkSweepGC,ump的old回收使用了cms算法,这样默认的young区的gc算法就是ParNew

3.      –verbose:gc打印gc信息到gc.log。观察gc.log:


a.      Young使用了ParNew算法,从图中可知,一次young gc之后,当时young区的gc从1400541K减少到了40011K(只在一个survivor区),ParNew耗时0.0267970s,可见每次young gc效率是很高的。

b.      1501888K是eden+s0或s1的大小

c.       当时jvm堆总大小从2254921K降到了895376k,总耗时0.0271140s

d.      Gc时间在用户代码执行是0.11s,系统调用时间是0.00,真实时间是0.03s,可以看出这里ParNew是起了多线程来执行的

e.      可以通过jstack的线程信息看出ParNew的gc线程数量:

可以看出默认情况下jvm是起了和cpu数量一样的ParNew线程,ump是5个。

四.   Young区诊断

一般情况下,young gc的频率是比较高的,每次new都会在eden区创建新的对象。当然young gc的频率太高,会对应用有影响,毕竟是stop-the-world,应用会停顿。当我们发现young gc频率太高了,我们就会想知道到底是哪些对象导致,会不会有大对象?会不会有多余的对象?我们需要找出这些对象,可以借助于一些工具。

1.      Mat - http://www.eclipse.org/mat/

Eclipse的mat插件是分析java堆的神器。我们使用jmap dump内存,注意jmap会stop-the-world的:

sudo -u admin /opt/taobao/java/bin/jmap -histo 22314>>jmap

将这个dump文件拉下来,启动mat分析。但是不幸的是,默认的mat只会分析存活的对象,但是young区中其实有很多对象是dead的。这个时候我们就需要mat的一个扩展选项:


保留unreachable的对象,这样我们就可以看到有哪些对象是new了之后立马就变成unreachable了,这些对象是优化的重点。这个需要mat比较新的版本才有~~


a.      打开dominator_tree栏,可以看到当前堆里对象的分布,’unreachable’的就是那些临时对象,在mat里可以看到这些对象的属性和引用它的对象,比较容易发现问题J

b.     Shallow heap指对象本生的大小,retained heap指该对象被回收之后将一块回收的那些对象的总大小(比如内部属性)

2.      TBJmap -https://github.com/jlusdy/TBJMap

是叔同大师写的一个jmap扩展,基于Serviceability Agent实现,会阻塞应用,建议使用前先把应用offline。使用比较简单:

java -classpath/home/admin/tbjmap.jar:/java/lib/sa-jdi.jar sun.jvm.hotspot.tools.TBJMap -histoPID > jmap_output.log

可以看到哪些大对象占据了eden区


五.   Young区优化

优化的主要目标是减少应用因为younggc带来的停顿时间,可以取一段时间的总停顿时间做为benchmark。

1.      jvm系统层面

a.      Eden调大,比如调大Xmn,或者调大SuvivorRatio以增大eden区容量,这样就可以存放更多的对象,减少young gc频率。但是代价是单次young gc的时间要变长,所以总停顿时间未必就会降低,需要反复实践。

b.      增加young gc线程,通过XX:ParallelGCThreads设置,默认是逻辑cpu个数,应该说这个值已经是合理的了,因为gc都是cpu-bound的任务,适当增加线程数未必会带来好的效果,不过可以增加cpu以提高并发度。

c.       调小TenuringThreshold,减少对象在young区的停留时间,将压力转移给old区J

2.      应用层面

a.      减少每次请求创建的对象数量,这里有很多代码技巧,最常见的:

1.      使用object cache

2.      熟悉各种Collection的原理和使用场景,如果可以预见容量,则设置初始容量,比如用hashmap,默认的数组大小是16,如果只有2个元素,则可以new HashMap(2)这样用。

3.      减少复杂对象包装,在jvm中一个对象的大小可以简单表述: header8+klassoop4(开启压缩指针,不开的话是8)+ sizeof(属性)+pading(可选),也就是说每个对象都有12个字节固定消耗,如果对象嵌套复杂的话,带来的消耗也就多了。

b.      降低tps,比如增加机器,流控等

六.   小结

本文简单介绍了,hotspotjvm下的young区性能诊断和优化。Young区的优化比较难做,更多的需要从业务上进行优化,我们平时在写业务代码的时候也要注意下对gc的影响~~

七.   资料

毕玄大神的gc分享,必看 http://www.docin.com/p-417999249.html

计算一个对象的大小 http://sizeof.sourceforge.net/

Shallow和retained大小解释 http://kenwublog.com/understand-shallow-and-retained-size-in-hprofling

Cms gc日志解读 https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs

Mat手册 http://wiki.eclipse.org/index.php/MemoryAnalyzer

 

 

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值