性能调优利器——PerfJ

PerfJ是我业余时间开发的一个小工具,现在来说不建议用在生产环境,因为还没有得到更多案例的证明。

平时我们在做Java程序Profiling时,基本上使用YJP, hprof, jvisualvm这样的工具,还有for i in {1..100}; do jstack $pid > $i; done;这些工具基本上两个原理,一个是sampling(采样),一个是instrument(注入一些信息到方法里面,然后统计)。

这些工具有些问题不能解决:

如果是网络io,磁盘io等问题,基本只能看到epollWait, socket/file的read这一层,看不到更深层次的原因;
如果看到Java程序占用CPU很高,用上面工具只能估计。先用top看到线程级别,看哪个线程CPU搞,然后再jstack。

即系统工具都无法上达Java这一层。

讲师:周敏(@MinZhou

整理:以亭

1. Principle behind PerfJ

PerfJ是我业余时间开发的一个小工具,现在来说不建议用在生产环境,因为还没有得到更多案例的证明。

平时我们在做Java程序Profiling时,基本上使用YJP, hprof, jvisualvm这样的工具,还有for i in {1..100}; do jstack $pid > $i; done;这些工具基本上两个原理,一个是sampling(采样),一个是instrument(注入一些信息到方法里面,然后统计)。

这些工具有些问题不能解决:

  • 如果是网络io,磁盘io等问题,基本只能看到epollWait, socket/file的read这一层,看不到更深层次的原因;
  • 如果看到Java程序占用CPU很高,用上面工具只能估计。先用top看到线程级别,看哪个线程CPU搞,然后再jstack。

即系统工具都无法上达Java这一层。

PerfJ的原理

linux perf 是一个强大的profiling, tracing的工具集, 包含在kernel代码树里,但perf对于java的程序没有办法。

首先, java很多程序被Just in time compiling了, perf不知道java函数的原始名字。

其次,JVM由于历史原因把frame pointer的register给去了,所以无法爬栈。

PerfJ其实只是linux perf的一个包装。解决了第一个问题,JIT,原理是通过JVMTI把perfj attach到目标JVM,将它JIT后各函数的原始函数名和地址的对照拿出来,写到/tmp/perf-$pid.map文件中,之后perf会加载这个文件,从而统计出java程序的一些信息。第二个问题可以通过升级JDK到8u60 b19以上解决(见后面)。

2. Install perf, PerfJ, java 8u60 b19

安装前准备:

Linux 64bit 物理机;

 

(1)安装perf工具

centos系:yum install perf.x86_64;

ubuntu:apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`

(2)安装PerfJ

checkout the source from github

git clone https://github.com/coderplay/PerfJ.git

and run below in the future buildingcd PerfJ

./gradlew releaseTarGz

如果没有装gcc,可以到http://blog.minzhou.info/PerfJ/PerfJ-1.0.tgz这里下载,但不保证都能用 🙂

(3)下载火焰图生成工具FlameGraph

git clone https://github.com/brendangregg/FlameGraph.git

  • 安装最新JDK

https://jdk8.java.net/download.html

务必是jdk 8u60 b19以上!

如果打不开,可以到这里下载:http://share.weiyun.com/cd36ccd7f6e837bd4699f3a1448f31e8

下载后解压,修改PATH和JAVA_HOME环境变量。

export JAVA_HOME=新jdk路径

export PATH=$JAVA_HOME/bin:$PATH

3. Show some flame graph

http://blog.minzhou.info/perfj/perfj.svg

1

图中,绿色是Java代码,黄色是JVM代码,红色是Kernel代码,宽度表示占用CPU时间。

4. Building PerfJ and untar the ball

解压perfj-1.0.tgz

cd perfj-1.0/

运行:bin/perfj list, 查看所支持的事件。运行截图:

2

5. Context Switch

下载模拟context switch的java代码:http://blog/minzhou.info/perfj/ContextSwitchTest.java

编译:javac –cp . ContextSwitchTest.java

运行:java –cp . ContextSwitchTest

开启另一窗口,运行vmstat 1,发现cs很高。

使用java –cp . –XX:+PreserveFramePointer ContextSwitchTest,开启frame pointer

同时另一窗口运行:bin/perfj record -e cs  -g -p `pgrep -f ContextSwitchTest`

跑一段时间后,ctrl + C,然后bin/perfj report –stdio

得到结果如下:

3

如果出现这种无法打开map文件错误:

4

可能是perfj版本问题。下载这个:http://blog.minzhou.info/perfj/perfj-1.0.tgz(打不开?用http://share.weiyun.com/ae0cb80416b3ca3d97fc1877aa1bde4a)就好了。

从这个程序的stack可以看出Unsafe.park会导致当前线程让出cpu, 引发context switch。

刚才那段代码是用来看context switch在哪里发生,以前的工具是没有办法的。

6. CPU cache miss example

下载代码:blog.minzhou.info/PerfJ/L1CacheMiss.java

编译:javac -cp . L1CacheMiss.java

运行:java -cp . -Xmx2g -XX:+PreserveFramePointer L1CacheMiss

捕获:bin/PerfJ record -e  L1-dcache-load-misses  -g -p `pgrep -f CacheMiss`

报告:bin/perfj report –stdio

cache hit比cache miss快几十倍。

5

通过上面看到,94%的cache miss是cachemiss这个函数造成的。

7. Homework , Threadpool comparison

时间关系,未做介绍

8. More complex examples, io request profiling

下载可执行程序:http://blog.minzhou.info/PerfJ/leveldb-benchmark.jar

直接运行: java -cp leveldb-benchmark.jar  -XX:+PreserveFramePointer org.iq80.leveldb.benchmark.DbBenchmark  –benchmarks=fillrandom  –num=100000000

捕获:bin/perfj record -F 99 -g -p `pgrep -f DbBenchmark`

-F是指sampling的频率为99Hz。

报告:bin/perfj report –stdio

9. Off cpu analysis

在cpu上运行的有时候不占程序最主要时间,有时候往往是网络在等,有时候是写了一个sleep代码,这个往往很难找。我们继续用刚才的leveldb 例子:

运行: java -cp leveldb-benchmark.jar -XX:+PreserveFramePointer org.iq80.leveldb.benchmark.DbBenchmark –benchmarks=fillrandom –num=100000000

捕获:bin/PerfJ record -e sched:sched_stat_sleep -e sched:sched_switch  -e sched:sched_process_exit -g -o ~/perf.data.raw -p `pgrep -f Benchmark`

因为我们可以知道系统scheduler的trace point,所以我们record三个事件,然后通过这三件事件的时间差,知道哪个是off CPU之后不干活。

注入:bin/PerfJ inject -v -s -i ~/perf.data.raw -o ~/perf.data

报告: bin/PerfJ report –stdio –show-total-period -i ~/perf.data

6

可以看到截屏的这些stack虽然不占CPU,但在off cpu的时候一直歇着,很有可能拖慢程序的执行。

我们可以看到LockSupport.park的jni实现里有JVM sleep,所以在抢java concurrent锁的时候,如果冲突很大,还可能被锁sleep一下了。大家可以线下在网络代码里试试off cpu。

这里有很多示例:

http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html

http://www.brendangregg.com/perf.html

大部分是perf的用法,大家也可以用perfj试试,大部分都可以用。

例如上面leveldb的例子我们可以分析IO:

bin/perfj record -e block:block_rq_issue -F 99 -g -p `pgrep -f DbBenchmark`

用这个看物理设备的块请求是谁操作的,是哪些java代码调用的,然后我可以看到这个stack:

7

知道了java代码位置,可以用ftrace看这个io request的io size,用了多少时间,是random io还是seq io。

https://github.com/brendangregg/perf-tools/blob/master/examples/iosnoop_example.txt

# ./iosnoop Tracing block I/O… Ctrl-C to end.

COMM             PID    TYPE DEV      BLOCK        BYTES     LATmssupervise        1809   W    202,1    17039968     4096       1.32supervise        1809   W    202,1    17039976     4096       1.30tar              14794  RM   202,1    8457608      4096       7.53tar              14794  RM   202,1    8470336      4096      14.90tar              14794  RM   202,1    8470368      4096       0.27tar              14794  RM   202,1    8470784      4096       7.74tar              14794  RM   202,1    8470360      4096       0.25tar              14794  RM   202,1    8469968      4096       0.24tar              14794  RM   202,1    8470240      4096       0.24tar              14794  RM   202,1    8470392      4096       0.23tar              14794  RM   202,1    8470544      4096       5.96tar              14794  RM   202,1    8470552      4096       0.27tar              14794  RM   202,1    8470384      4096       0.24[…]

 

像这样,perfj找到系统的事件之后,然后用ftrace跟踪详细的信息。

10. CPU flame graph

捕获成功后,会在当前目录下生成perf.data。利用这个文件可以绘制火焰图。

sudo mv perf.data ../FlameGraph

cd ../FlameGraph/

sudo perf script | ./stackcollapse-perf.pl > out.perf-folded

./flamegraph.pl out.perf-folded –colors java > PerfJ.svg

GreenTeaJUG活动 第16期 性能调优利器——PerfJ by 周敏

时间:2015-06-28
地点:网络
组织:以亭

主题:Perfj
讲师:周敏(@MinZhou

周敏

周敏, 暨南大学计算机硕士毕业学位. 主研方向: 大数据、分布式系统. 先后在阿里巴巴担任技术专家, 美国LinkedIn公司Staff Engineer , 现任美国Tango公司Senior Staff Engineer, 带领大数据基础研发团队. 多个Apache开源项目贡献者及提交者. 曾帮助Apache Spark团队获100TB数据排序世界记录. 现定居美国旧金山湾区.

Java作为服务端程序近年来大规模网站服务、大数据、机器学习等方面应用越来越广, 服务端程序的性能越来越关键。当前Java的性能剖析器,例如jvisualvm, jprofiler, YJP都只能从Java代码层面分析Java程序的性能, 无法从JVM, 系统甚至硬件层面找出性能的关键点. 而Linux系统层的perf, systemtap等工具能够查到磁盘IO, 网络IO, CPU, 内存等性能问题,但无法上达至Java层. 中间的这个断层使我们分析Java程序性能非常不便. 最近由演讲者开发的PerfJ可以让开发者从下自上地分析Java程序的性能. 演讲者将阐述perfj的原理, 举例怎么用perfj找到由于CPU cache miss引发的性能问题, 以及分析系统context switch造成的性能问题.

活动形式:微信群讨论(报名成功后获取入群二维码)

活动报名请按照如下格式发邮件到event@greenteajug.cn

姓名:XXX
电话号码:xxxxxxxxxxx
邮箱:xxxx@xxx.xxx
公司:xxxx有限公司
职位:xx工程师
Java使用年限:x年

GreenTeaJUG活动 第4期 杭州

时间:2013-2-23
地点:杭州文三路华星时代3楼
组织: @JianhaoMo

会场:万松书院(偏向应用层)

主题:淘宝WEBOS.面向积木编程.分形架构
讲师:@琴上的日月

主题:Velocity的优化
讲师:@淘宝君山

主题:淘宝前台应用性能优化实践
讲师:@淘宝叔同

主题:各种API的性能、编码优化技巧
讲师:@温高铁

会场:三味书屋(偏向底层)

主题:Java程序员也要了解CPU
讲师:@MinZhou

主题:Intrinsic Methods in HotSpot VM
讲师:@RednaxelaFX

主题:让Java的世界能够利用PMU的历史
讲师:@王王争

主题:HotSpot VM Serviceability Agent的演示
讲师:@RednaxelaFX