第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > Java性能优化权威指南--笔记

Java性能优化权威指南--笔记

时间:2018-12-20 17:13:33

相关推荐

Java性能优化权威指南--笔记

出处:/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文地址

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线程会影响应用性能

其他性能命令行选项

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。