出处:/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文地址
Java性能优化权威指南-读书笔记(一)-操作系统性能监控工具
Java性能优化权威指南-读书笔记(二)-JVM性能调优-概述
Java性能优化权威指南-读书笔记(三)-JVM性能调优-内存占用
Java性能优化权威指南-读书笔记(四)-JVM性能调优-延迟
Java性能优化权威指南-读书笔记(五)-JVM性能调优-吞吐量
目录
Java性能优化权威指南-读书笔记(一)-操作系统性能监控工具
一:CPU
1. vmstat
2. top
二:内存
vmstat
pidstat
三:网络
nicstat
四:磁盘
iostat
五:统计
sar
Java性能优化权威指南-读书笔记(二)-JVM性能调优-概述
JVM调优工作流程
应用程序的系统需求
选择JVM的部署模式
JVM运行模式
垃圾收集调优基础
基本原则
GC日志
Java性能优化权威指南-读书笔记(三)-JVM性能调优-内存占用
Java堆大小计算法则
Java性能优化权威指南-读书笔记(四)-JVM性能调优-延迟
JVM延迟优化
新生代
老年代
CMS收集器调优
CMS主要特性
Survivor空间调优
初始化CMS收集周期
降低CMS重新标记阶段占用时间
Java性能优化权威指南-读书笔记(五)-JVM性能调优-吞吐量
CMS吞吐调优
ParallelGC吞吐调优
Survivor调优
并行线程调优
其他性能命令行选项
Java性能优化权威指南-读书笔记(一)-操作系统性能监控工具
一:CPU
1. 用户态CPU是指执行应用程序代码的时间占总CPU时间的百分比。
系统态CPU是指应用执行操作系统调用的时间占总CPU时间的百分比。系统态CPU高意味着共享资源有竞争或者I/O设备之间有大量的交互。
提高应用性能和扩展性的一个目标就是尽可能降低系统态CPU使用率。
2. CPU运行队列就是那些已经准备好运行、正等待可用CPU的轻量级进程。
当运行队列长度达到处理器的4倍或者更多时,系统的相应就非常迟缓了。
解决运行队列长有两种办法:
1). 增加CPU以分担负载;
2). 分析系统中运行的应用,改进CPU使用率;
1. vmstat
r:CPU运行队列长度,值是运行队列中轻量级进程的实际数量
us:用户态CPU使用率
sy:系统态CPU使用率
id:CPU空闲率
2. top
上半部分是整个系统的统计信息,下半部分是进程的统计信息。
P 按CPU占用率排序
M 按内存占用率排序
T 按CPU占用时间排序
H 查看详细线程信息
二:内存
1. 系统在使用页面交换或虚拟内存时,访问swap中内存以及JVM垃圾回收swap中内存时,都存在内存置换(从swap中置换到内存),性能肯定有问题;
2. 锁竞争,一般来说让步时钟周期占用3%—5%或更多,说明java应用正面临锁竞争;
vmstat
free:可用空闲内存;
si:内存页面换入;
so:内存页面换出;
pidstat
cswch/s:让步式上下文切换;
例如:处理器为3.0GHz双核,每个处理器上下文切换3500/2=1750,耗费的时钟周期1750*80000=140000000,因此上下文切换所占比例为:140000000/3000000000=4.7
三:网络
分布式Java应用的性能和扩展性受限于网络带宽或网络I/O的性能。
nicstat
需要编译安装(/timc/tags/linux)
Int:网络接口设备名
rKb/s:每秒读取的KB数
wKb/s:每秒写入的KB数
%Util:网络使用率
Sat:饱和度
四:磁盘
iostat
五:统计
sar
sar 1 3 查看当前CPU数据,每一秒刷新一次,统计三次
sar -q 查看平均负载
sar -r 查看内存使用状况
sar -W 查看页面交换发生状况
sar –b 查看I/O和传送速率的统计信息
Java性能优化权威指南-读书笔记(二)-JVM性能调优-概述
概述:
JVM性能调优没有一个非常固定的设置,比如堆大小设置多少,老年代设置多少。而是要根据实际的应用程序的系统需求,实际的活跃内存等确定。
正文:
JVM调优工作流程
整个调优过程是不断重复的一个迭代,后面的步骤有可能影响前面的配置,可能需要重新调优。
应用程序的系统需求
确定应用程序的系统需求是性能调优的基础,后面的调优都会依赖这个要求。一个应用不会无休止地调优下去。
1.可用性
2.可管理性
3.启动时间
4.吞吐量
TPS: 每秒多少次事务
QPS: 每秒多少次查询
5.延迟
比如关键请求必须60ms完成响应
6.内存占用
选择JVM的部署模式
单JVM部署模式:可以用更多的物理内存
多JVM部署模式:减少了单点,不过分布式部署也解决了这个问题
JVM运行模式
32位JVM:
内存空间限制为4G,关键是还进一步受限于操作系统,Windows大约1.5G,Linux大约3G。
64位JVM:
对象指针的长度从32位变为64位,导致CPU高速缓存可以缓存的指针变少,降低了缓存效率。可以开启指针压缩,解决这个问题,指针压缩在堆小于等于26GB时,性能最好。JVM会根据堆大小自动开启这个。
垃圾收集调优基础
基本原则
1. 每次MinorGC都尽可能多地收集垃圾对象。可以减少FullGC的频率,因为FullGC的持续时间总是最长;
2. 处理吞吐量和延迟问题时,GC能使用的内存越大,垃圾收集的效果越好,应用越流畅;
3. 在这三个属性(吞吐量、延迟、内存占用)中任意选择两个进行JVM垃圾收集器调优,因为三个属性肯定不能同时满足;
GC日志
GC日志是收集调优所需信息的最好途径,下面是一次MinorGC的日志,FullGC的日志和这个类似:
1). 各属性说明
5.483:是JVM启动到现在的时间戳
Allocation Failure:Eden区分配内存失败,导致GC
142650K(新生代回收前大小)->16873K(新生代回收后大小)(145408K(新生代总大小))
168504K(回收前堆占用大小)->48298K(回收后堆占用大小)(189440K(堆总大小))
Times:user(GC非操作系统指令占用的CPU时间)sys(GC操作系统调用占用的CPU时间)real(实际占用的CPU时间)
2). 计算老年代方法
根据上面这个MinorGC日志,可以推算出老年代在GC前后的大小。
GC前:168504K(回收前堆占用大小)-142650K(新生代回收前大小)=25854K
GC后:48298K(回收后堆占用大小)-16873K(新生代回收后大小)=31425K
3). GC日志命令行选项
Java性能优化权威指南-读书笔记(三)-JVM性能调优-内存占用
新生代、老年代、永久代的概念不多说,这三个空间中任何一个不能满足内存分配请求时,就会发生垃圾收集。
新生代不满足内存分配请求时,发生Minor GC,老年代、永久代不满足内存分配请求时,发生Full GC,Minor GC比Full GC持续的时间要短很多。
所以内存空间设置的不合理就会频繁引起垃圾收集,以及OutOfMemoryError错误,严重影响程序性能。
Java堆大小计算法则
若你的应用部署在单独的服务器,且该服务器上只有这一个应用,那Java堆肯定是越大越好,但这种情况比较少。
Java堆大小可以参考下面这个表格:
其中这些空间占用,都是指应用程序在稳定状态(指应用运行了一段时间)下,Full GC后Java堆占用的空间大小,即活跃数据的大小。
参照下面这个GC日志:
1). Java堆大小:29331K(约30M) * (3~4),即90M~120M
2). 永久代大小:34774K(约34M)* (1.2~1.5),即41M~51M
3). 新生代大小:29331K(约30M) * (1~1.5),即30M~45M
4). 老年代大小:(90M~120M) – (30M~45M),即60M~75M
这个计算法则,只是起到指导性的意见,实际操作中,还是需要根据实际情况来应变。
在调整后面两项“延迟、吞吐量”时,这些堆的大小还需要进一步的调整。
Java性能优化权威指南-读书笔记(四)-JVM性能调优-延迟
延迟指服务器处理一个请求所花费的时间,单位一般是ms、s。
本文主要讲降低延迟可以做的服务器端JVM优化。
JVM延迟优化
新生代
新生代大小决定了应用平均延迟
如果平均Minor GC持续时间大于应用程序平均延迟性要求,可以适当减小新生代空间大小;
如果Minor GC频率大于应用程序平均延迟性要求,可以适当增大新生代空间;
老年代
老年代大小决定了应用最差延迟
FullGC频率大于应用程序最大FullGC频率要求,可以适当增大老年代空间大小;
FullGC持续时间大于应用程序最差延迟性要求,可以使用CMS垃圾收集器;
CMS收集器调优
CMS主要特性
CMS为老年代收集器,收集线程能与应用程序线程实现最大的并行度,降低了延迟。
CMS并不进行压缩,所以老年代使用空闲列表分配内存,在一定程度上增加了新生代晋升老年代时,耗费的时间。
但一旦老年代溢出就会触发Stop-The-World压缩式垃圾收集。
所以,CMS的优化大概有下面几点:
1. 避免用尽老年代空间;
2. 解决老年代碎片化问题,解决方法包括压缩、MinorGC回收原则;
3. 降低初始标记阶段和重新标记阶段占用的时间,因为这两个阶段应用程序线程会被阻塞;
Survivor空间调优
调整Survivor空间,是为了让其有足够空间容纳存活对象足够长的时间,直到几个周期后对象老化,解决上面的第一、二条问题。
Survivor空间的大小可以通过下面这个参数调整:
-XX:SurvivorRatio=<ratio>
<ratio>必须大于0,-XX:SurvivorRatio=<ratio>表示单个Survivor和Eden的比率。
survivor空间大小 = –Xmn/(-XX:SurvivorRatio=<ratio> + 2)
监控晋升阈值
-XX:MaxTenuringThreshold=n //设置最大晋升阈值
-XX:+PrintTenuringDistribution //打印晋升的分布或对象年龄分布到GC日志
如下面这个晋升日志:
Desired survivor size 1114112 bytes是Survivor空间大小 * 目标存活率(可以设定,默认50%)
new threshold 1JVM内部计算出的晋升阈值(max 6)设置的晋升阈值
age 1(对象年龄代)2213864 bytes(这个年龄对象总大小),2213864 total(所有年龄对象总大小)
Survivor目标存活率:指Minor GC后Survivor空间占用比率,如果太大,下次Minor GC后存活的对象就有可能放不下
从这个日志可以看出,存活对象2213864bytes大于期望Survivor大小为1124112bytes,所以对象年龄为1时,就提升到了老年代。
解决这个问题的方法就是增大Survivor空间,存活对象大小 / 目标存活率 = Survivor空间大小,本例中大概4.5M,所以修改JVM参数为:
调整后的晋升日志大概如下,这个已不是上面那个应用了,所以Survivor空间不一样,能说明问题就行:
调整Survivor空间的一个重要原则:
调整Survivor空间时,应保持Eden空间不变,否则会导致Minor GC频率变小。
初始化CMS收集周期
CMS中发生的Stop-The-World压缩式垃圾收集可以在GC日志中查找并发模式失效(concurrent mode failure)定位。
如果有这个问题,就需要调整CMS收集周期。
-XX:CMSInitiatingOccupancyFraction=<percent> //设置CMS垃圾收集周期在老年代空间占用达到多少是启动
-XX:+UseCMSInitiatingOccupancyOnly //告知JVM总是使用 CMSInitiatingOccupancyFraction比率启动CMS,否则只是在CMS第一次启动时
原则:CMSInitiatingOccupancyFraction设置的比率至少是老年代活跃数据的1.5倍
如果老年代空间消耗的比较慢,可以稍晚启动CMS,即将CMSInitiatingOccupancyFraction设定的更大;
如果老年代空间消耗的很快,可以将CMSInitiatingOccupancyFraction设定的更小,但不能低于活跃数据占用的比率;
降低CMS重新标记阶段占用时间
-XX:ParallelGCThreads //控制重新标记的线程数,建议将CMS的收集线程数设置的小于默认值,否则大量GC线程会影响应用性能
-XX:+CMSScavengeBeforeRemark//在CMS进入重新标记阶段先进行一次Minor GC,可以减少引用老年代的新生代对象数量,降低重新标记阶段的工作量
-XX:+ParallelRefProcEnabled //使用多线程处理引用,不过在JDK6u25和6u26上有BUG
Java性能优化权威指南-读书笔记(五)-JVM性能调优-吞吐量
吞吐量是指,应用程序的TPS: 每秒多少次事务,QPS: 每秒多少次查询等性能指标。
吞吐量调优就是减少垃圾收集器消耗的CPU周期数,从而将更多的CPU周期用于执行应用程序。
CMS吞吐调优
CMS包括Minor GC所带来的开销应该小于10%,如果垃圾收集的开销在3%或更少,说明通过调优吞吐量,提升性能的空间就极其有限了。
可用的调优方法如下:
1. 增大新生代空间,以降低Minor GC频率,减少CPU周期占用;
2. 增加老年代空间,以降低CMS频率,并可以减少老年代内存碎片;
3. 优化CMS周期的启动条件,尽可能在较晚的时候进行;
总的来说,就是减少垃圾收集占用的CPU周期。
ParallelGC吞吐调优
这里说的ParallelGC是指开启了下面两个JVM参数
-XX:+UseParallelGC
-XX:+UseParallelOldGC
对ParallelGC调优的目标是尽可能避免发生Full GC,这就需要优化对象老化频率,可以调整Survivor空间实现对对象老化的优化。
使用ParallelGC时,垃圾收集的开销应小于5%,如果已经减少到1%甚至更少,那基本上就已经达到了极限。
Survivor调优
ParallelGC默认可以自动调整Survivor空间,大部分应用用自动调整已经可以,对要求比较高的应用就需要关闭自动调整,进行手动调整。
为JVM添加下面两个参数,只针对ParallelGC有用:
如下面日志:
survived:“TO”Survivor空间占用大小;
promoted: 新生代提升至老年代的对象大小;
overflow:是否有Survivor空间的对象溢出到老年代;
从上面的日志可以看出,Minor GC后新生代存活对象大小10M,因为没有设置-XX:TargetSurvivorRatio,默认Survivor空间占用比率为50%,
所以Survivor空间应为20M。
找到稳定态下Full GC之间所有Minor GC中最大的存活对象大小,然后就可以调整Survivor空间大小。
原JVM参数如下:
可以计算出:原Survivor空间:10M,原Eden空间:30M
现在增大Survivor空间到20M
保证Eden空间不变,则新生代大小为70M;
70M / (SurvivorRatio + 2)=20M,所以SurvivorRatio=1.5
保证老年代空间不变,则Java堆大小调整为1044M
所以最后JVM参数为:
如果Java堆大小已经不能再增大,可以计算下Minor GC后,存活对象的最小值、最大值、平均值,如果不存在大幅波动,
可以尝试提高Survivor空间的占用百分比-XX:TargetSurvivorRatio=<n>,其默认为50%。
并行线程调优
-XX:ParallelGCThreads
并行垃圾收集器的线程数,建议收集线程数设置的小于默认值,否则大量GC线程会影响应用性能