7. JVM调优
代大小的调优
在不采用G1的情况下,通常minor GC会快于Full GC,在代调优的参数上,最关键的是:
-Xms -Xmx -Xmn -XX:SurvivorRadio -XX:MaxTenuringThreshold
Xms和Xmx设定为相同的值
- 避免运行时要不断的扩展JVM的内存空间,这个值决定了JVM heap所能使用的最大空间
Xmn决定了新生代(New Generation)大小
- 新生代中Eden、S0、S1三个区域的比例,通过-XX:SurvivorRadio控制
XX:MaxTenuringThreshold控制对象经历多少次Minor GC后才转入旧生代,通常又被称为:新生代存活周期
- 此参数只在串行GC有效,其他GC方式由Sun JDK自��决定
避免新生代设置过小
-
新生代过小:
minor GC次数更加频繁
minor对象直接进入旧生代,会触发full GC
调整的原则:
- 不调大JVM Heap的情况下,尽可能放大新生代的空间,尽量让对象在新生代minor GC阶段被回收,但新生代空间也不可过大
- 能够调大JVM Heap的情况下,可以按照增大的新生代空间大小增加JVM Heap大小,保证旧生代空间够用
避免新生代设置过大
大多数场景下应该比旧生代小,通常的比例是新生代占用JVM Heap空间的1/3
避免Survivor区过大或过小
默认情况下,新生代中:Eden区、S0、S1的比例是: 8:1:1,某些情况下,-XX:SurvivorRadio合理设置该比例,也可以带来一些优化效果。
合理设置新生代存活周期
-XX:MaxTenuringThreshold,默认的周期是15次。
增大了存活周期后,对象在minor GC阶段被回收的机会增加了,但同时带来的是survivor区被占用,此值只有在串行GC和ParNewGC时可调整
实际操作
GC监测
- gc的日志拿下来后可使用GCLogViewer或gchisto进行分析。
1 | jstat –gcutil [pid] [intervel] [count] |
图形化的情况下可直接用jvisualvm进行分析。
查看内存的消耗状况
- 长期消耗,可以直接dump,然后MAT(内存分析工具)查看即可
- 短期消耗,图形界面情况下,可使用jvisualvm的memory profiler或jprofiler。
步骤
- 评估现状
- 设定目标
- 尝试调优
- 衡量调优
- 细微调整
设定目标
- 降低Full GC的执行频率?
- 降低Full GC的消耗时间?
- 降低Full GC所造成的应用停顿时间?
- 降低Minor GC执行频率?
- 降低Minor GC消耗时间?
例如某系统的GC调优目标:降低Full GC执行频率的同时,尽可能降低minor GC的执行频率、消耗时间以及GC对应用造成的停顿时间
衡量调优
衡量工具
- 打印GC日志信息
- jmap:(由于每个版本jvm的默认值可能会有改变,建议还是用jmap首先观察下目前每个代的内存大小、GC方式
- 运行状况监测工具:jstat、jvisualvm、sar 、gclogviewer
应收集的信息
- minor gc的执行频率;full gc的执行频率,每次GC耗时多少?
- 高峰期什么状况?
- minor gc回收的效果如何?survivor的消耗状况如何,每次有多少对象会进入老生代?
- full gc回收的效果如何?(简单的memory leak判断方法)
- 系统的load、cpu消耗、qps or tps、响应时间
QPS每秒查询率:是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。在因特网上,作为域名服务器的机器性能经常用每秒查询率来衡量。对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力。
TPS(Transaction Per Second):每秒钟系统能够处理的交易或事务的数量。
尝试调优
降低Full GC执行频率,这个调优时的通常瓶颈。老生代本身占用的内存空间就一直偏高,所以只要稍微放点对象到老生代,就full GC了
-
通常原因:系统缓存的东西太多;
-
例如:使用oracle 10g驱动时preparedstatement cache太大;
-
查找办法:现执行Dump然后再进行MAT分析;
-
情况一:Minor GC后总是有对象不断的进入老生代,导致老生代不断的满
- 通常原因:Survivor太小了
- 系统表现:系统响应太慢、请求量太大、每次请求分配的内存太多、分配的对象太大…
- 查找办法:分析两次minor GC之间到底哪些地方分配了内存;
- 利用jstat观察Survivor的消耗状况,-XX:PrintHeapAtGC,输出GC前后的详细信息;
对于系统响应慢可以采用系统优化,不是GC优化的内容;
-
情况二:老生代的内存占用一直偏高
调优方法:
- 扩大老生代的大小(减少新生代的大小或调大heap的大小)
- 减少new注意对minor gc的影响并且同时有可能造成full gc还是严重
- 调大heap注意full gc的时间的延长,cpu够强悍嘛,os是32 bit的吗
- 程序优化(去掉一些不必要的缓存)
情况三:Minor GC后总是有对象不断的进入老生代
前提:这些进入老生代的对象在full GC时大部分都会被回收
调优方法:
- 降低Minor GC的执行频率;
- 让对象尽量在MinorGC中就被回收掉:增大Eden区、增大survivor、增大TenuringThreshold;注意这些可能会造成minor gc执行频繁;
- 切换成CMS GC:老生代还没有满就回收掉,从而降低FullGC触发的可能性;
- 程序优化:提升响应速度、降低每次请求分配的内存、
情况四:降低单次Full GC的执行时间
通常原因:老生代太大了…
调优方法:
- 是并行GC吗
- 升级CPU
- 减小Heap或老生代
情况五:降低Minor GC执行频率
通常原因:每次请求分配的内存多、请求量大
通常办法:
- 扩大heap、扩大新生代、扩大eden。
注意点:降低每次请求分配的内存;横向增加机器的数量分担请求的数量。
情况六:降低Minor GC执行时间
通常原因:新生代太大了,响应速度太慢了,导致每次Minor GC时存活的对象多
通常办法:
- 减小点新生代
- 增加CPU的数量
- 升级CPU的配置
- 加快系统的响应速度
细微调整
首先需要了解以下情况:
- 当响应速度下降到多少或请求量上涨到多少时,系统会宕掉?
- 参数调整后系统多久会执行一次Minor GC,多久会执行一次Full GC,高峰期会如何?
需要计算的量:
- 每次请求平均需要分配多少内存?系统的平均响应时间是多少呢?请求量是多少、多常时间执行一次Minor GC、Full GC?
- 现有参数下,应该是多久一次Minor GC、Full GC,对比真实状况,做一定的调整;
必杀技:提升响应速度、降低每次请求分配的内存