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

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

原文地址

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

01

02

03

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----

 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st

 2  0      0 104956    868 825812    0    0     1     5   16   25  0  0 100  0  0

r:CPU运行队列长度,值是运行队列中轻量级进程的实际数量

us:用户态CPU使用率

sy:系统态CPU使用率

id:CPU空闲率

2. top

01

02

03

04

05

06

07

08

09

10

11

top - 09:42:04 up 3 days, 3 min,  1 user,  load average: 0.00, 0.01, 0.05

Tasks: 108 total,   3 running, 105 sleeping,   0 stopped,   0 zombie

%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

KiB Mem :  1015472 total,   104568 free,    84224 used,   826680 buff/cache

KiB Swap:        0 total,        0 free,        0 used.   727760 avail Mem

 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                         

  784 root      20   0  553064  16340   5748 S  0.0  1.6   0:21.48 tuned                                                                                           

  743 root      20   0  110512  13140    676 S  0.0  1.3   0:00.13 dhclient                                                                                        

 9678 polkitd   20   0  527456  13128   4680 S  0.0  1.3   0:00.09 polkitd                                                                                         

    1 root      20   0   43684   6176   3804 S  0.0  0.6   0:05.08 systemd                                                                                         

上半部分是整个系统的统计信息,下半部分是进程的统计信息。

P 按CPU占用率排序
M 按内存占用率排序
T 按CPU占用时间排序
H 查看详细线程信息

二:内存

1. 系统在使用页面交换或虚拟内存时,访问swap中内存以及JVM垃圾回收swap中内存时,都存在内存置换(从swap中置换到内存),性能肯定有问题;

2. 锁竞争,一般来说让步时钟周期占用3%—5%或更多,说明java应用正面临锁竞争;

vmstat

free:可用空闲内存;

si:内存页面换入;

so:内存页面换出;

pidstat

01

02

03

04

05

06

07

08

09

[test ~]$ pidstat -w -I -p 7938 5

Linux 3.10.0-229.20.1.el7.x86_64 (test)     11/13/2016  _x86_64_    (1 CPU)

 

07:56:42 PM   UID       PID      cswch/s     nvcswch/s  Command

07:56:47 PM     0      7938      3500.00     0.00  java

07:56:52 PM     0      7938      3500.00     0.00  java

07:56:57 PM     0      7938      3500.00     0.00  java

 

Average:        0      7938      3500.00     0.00  java

cswch/s:让步式上下文切换;

例如:处理器为3.0GHz双核,每个处理器上下文切换3500/2=1750,耗费的时钟周期1750*80000=140000000,因此上下文切换所占比例为:140000000/3000000000=4.7

三:网络

分布式Java应用的性能和扩展性受限于网络带宽或网络I/O的性能。

nicstat

需要编译安装(https://blogs.oracle.com/timc/tags/linux)

01

02

03

    Time      Int   rKB/s   wKB/s   rPk/s   wPk/s    rAvs    wAvs %Util    Sat

13:25:57     eth0    2.34    1.77    2.84    2.29   842.6   789.6  0.00   0.00

13:25:57       lo    0.00    0.00    0.00    0.00   85.49   85.49  0.00   0.00

Int:网络接口设备名

rKb/s:每秒读取的KB数

wKb/s:每秒写入的KB数

%Util:网络使用率

Sat:饱和度

四:磁盘

iostat

01

02

03

04

05

06

07

08

09

[test ~]$ iostat -xm

Linux 3.10.0-229.20.1.el7.x86_64 (test)     11/13/2016  _x86_64_    (1 CPU)

 

avg-cpu:  %user   %nice %system %iowait  %steal   %idle

           0.26    0.00    0.22    0.06    0.24   99.23

 

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util

xvda              0.00     0.22    0.11    0.38     0.00     0.00    16.79     0.00    6.15    7.51    5.74   2.05   0.10

xvdb              0.00     0.00    0.00    0.00     0.00     0.00    18.07     0.00    7.04   10.93    6.36   2.75   0.00

五:统计

sar

sar 1 3 查看当前CPU数据,每一秒刷新一次,统计三次
sar -q 查看平均负载
sar -r 查看内存使用状况
sar -W 查看页面交换发生状况
sar –b 查看I/O和传送速率的统计信息

 

Java性能优化权威指南-读书笔记(二)-JVM性能调优-概述

概述:
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的日志和这个类似:

01

02

03

04

5.483: [GC (Allocation Failure)

[PSYoungGen: 142650K->16873K(145408K)]

168504K->48298K(189440K), 0.0769590 secs]

[Times: user=0.22 sys=0.00, real=0.08 secs]

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日志命令行选项

-XX:+PrintGCTimeStamps

打印此次垃圾回收距离jvm开始运行的所耗时间

-XX:+PrintGCDetails

打印垃圾回收的细节信息

-Xloggc:<filename>

将垃圾回收信息输出到指定文件

-XX:+PrintGCDateStamps

需要打印日历形式的时间戳选项

-XX:+PrintGCApplicationStoppedTime

-XX:+PrintGCApplicationConcurrentTime

打印应用程序由于执行VM安全点操作而阻塞的时间以及两个安全点操作之间应用程序的运行时间

-XX:+PrintSafepointStatistics

可以将垃圾回收的安全点与其他的安全点区分开

Java性能优化权威指南-读书笔记(三)-JVM性能调优-内存占用

新生代、老年代、永久代的概念不多说,这三个空间中任何一个不能满足内存分配请求时,就会发生垃圾收集。

新生代不满足内存分配请求时,发生Minor GC,老年代、永久代不满足内存分配请求时,发生Full GC,Minor GC比Full GC持续的时间要短很多。

所以内存空间设置的不合理就会频繁引起垃圾收集,以及OutOfMemoryError错误,严重影响程序性能。

Java堆大小计算法则

若你的应用部署在单独的服务器,且该服务器上只有这一个应用,那Java堆肯定是越大越好,但这种情况比较少。

Java堆大小可以参考下面这个表格:

空间命令行选项占用倍数
Java堆-Xms:最小堆大小
-Xmx:最大堆大小
3~4倍Full GC后老年代空间占用
永久代-XX:PermSize
-XX:MaxPerSize
1.2~1.5倍Full GC后永久代空间占用
新生代-Xmn1~1.5倍Full GC后老年代空间占用
老年代Java堆大小 - 新生代大小2~3倍Full GC后老年代空间占用

其中这些空间占用,都是指应用程序在稳定状态(指应用运行了一段时间)下,Full GC后Java堆占用的空间大小,即活跃数据的大小。

参照下面这个GC日志:

01

02

03

04

05

06

12.251:

[Full GC

[PSYoungGen: 15945K->0K(278528K)]

[ParOldGen: 30724K->29331K(67072K)] 46670K->29331K(345600K),

[PSPermGen: 34785K->34774K(1081344K)], 0.1022337 secs]

[Times: user=0.20 sys=0.00, real=0.10 secs]

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日志

如下面这个晋升日志:

01

02

Desired survivor size 1114112 bytes, new threshold 1 (max 6)

- age   1:    2213864 bytes,    2213864 total

Desired survivor size 1114112 bytes是Survivor空间大小 * 目标存活率(可以设定,默认50%)

new threshold 1 JVM内部计算出的晋升阈值 (max 6)设置的晋升阈值

age   1(对象年龄代)   2213864 bytes(这个年龄对象总大小),    2213864 total(所有年龄对象总大小)

Survivor目标存活率:指Minor GC后Survivor空间占用比率,如果太大,下次Minor GC后存活的对象就有可能放不下

 

从这个日志可以看出,存活对象2213864bytes大于期望Survivor大小为1124112bytes,所以对象年龄为1时,就提升到了老年代。

解决这个问题的方法就是增大Survivor空间,存活对象大小 / 目标存活率 = Survivor空间大小,本例中大概4.5M,所以修改JVM参数为:

01

-Xmx512m -Xms512m -Xmn50m -XX:SurvivorRatio=5

调整后的晋升日志大概如下,这个已不是上面那个应用了,所以Survivor空间不一样,能说明问题就行:

01

02

03

04

05

Desired survivor size 87359488 bytes, new threshold 15 (max 15)

- age   1:   20701528 bytes,   20701528 total

- age   2:    4924656 bytes,   25626184 total

- age   3:    3261000 bytes,   28887184 total

- age   4:    7941120 bytes,   36828304 total

调整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有用:

01

02

-XX:-UseAdaptiveSizePolicy     //关闭自适应调整

-XX:+PrintAdaptiveSizePolicy   //打印详细Survivor空间占用日志

如下面日志:

01

02

03

04

2016-12-11T16:44:03.653+0800: 5.042: [GC (Allocation Failure) AdaptiveSizePolicy::update_averages: 

survived: 10464720 

promoted: 1096456 

overflow: true

survived:“TO”Survivor空间占用大小;

promoted: 新生代提升至老年代的对象大小;

overflow:是否有Survivor空间的对象溢出到老年代;

从上面的日志可以看出,Minor GC后新生代存活对象大小10M,因为没有设置-XX:TargetSurvivorRatio,默认Survivor空间占用比率为50%,

所以Survivor空间应为20M。

 

找到稳定态下Full GC之间所有Minor GC中最大的存活对象大小,然后就可以调整Survivor空间大小。

原JVM参数如下:

01

-Xmx1024m –Xms1024m –Xmn50m -XX:SurvivorRatio=3

可以计算出:原Survivor空间:10M,原Eden空间:30M

现在增大Survivor空间到20M

保证Eden空间不变,则新生代大小为70M;

70M / (SurvivorRatio + 2)=20M,所以SurvivorRatio=1.5

保证老年代空间不变,则Java堆大小调整为1044M

所以最后JVM参数为:

01

-Xmx1044m -Xms1044m -Xmn70m -XX:SurvivorRatio=1.5

如果Java堆大小已经不能再增大,可以计算下Minor GC后,存活对象的最小值、最大值、平均值,如果不存在大幅波动,

可以尝试提高Survivor空间的占用百分比-XX:TargetSurvivorRatio=<n>,其默认为50%。

并行线程调优

-XX:ParallelGCThreads  

并行垃圾收集器的线程数,建议收集线程数设置的小于默认值,否则大量GC线程会影响应用性能

其他性能命令行选项

-XX:+AggressiveOpts自动优化,但有可能不太稳定
-XX:+DoEscapeAnalysis逃逸分析
-XX:+UseBiasedLocking偏向锁
-XX:+UseLargePages大页面支持

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值