这是小小的第五篇

前言

在进行性能调优的时候,通常会借助性能分析工具,常用的性能分析工具有perf,DTrace,分析系统资源的使用情况,这些情况根据CPU,内存,生成相关的文本数据,这些文本数据不容易直接理解,所以采用火焰图进行分析。用图表的形式展示出来。
本文将会展示具体实例。

火焰图

火焰图有不同的资源和事件类型,包括的主要数据有 CPU,Memory,Off-CPU,Hot/Cold。等等。
下文中,所描述的火焰图指的是CPU的火焰图。
图例 | Java混合模式分析之火焰图实例插图

小方块 每个小方块代表一个函数,对应一个栈帧,小方块的宽度表示该函数在采样期间出现的频率,宽度越宽表示出现的频率越高。

栈帧 用于记录函数的活动记录,保存函数局部变量,函数参数等信息。因此想要分析函数调用堆栈关系,这里就需要使用采样得到栈帧信息。

Y 表示函数的调用栈,体现了stack深度,每个垂直方向的顶端小方块表示正在使用CPU的栈的深度。

X 表示采用数据的总量,X表示数据在对调用进行合并之后的结果。根据函数的字母顺序进行排列。X不代表使用CPU的长短,只是说明该方法出现的次数,宽度越宽表示该方法出现的次数越多。X可以直接作为CPU的时间长短。

生成火焰图

采样数据 -> 使用脚本生成
图解如下

图例 | Java混合模式分析之火焰图实例插图1

工具

火焰图是根据任何包含stack traces 的 profile 数据生成,需要采集stack traces 数据,
列举如下的工具,采集相关的数据

Linux perf_events
Mac DTrace and Instruments
windows Xperf.exe

这里采用的是perf,在Linux系统下实验

安装

sudo yum install perf

生成数据

sudo perf record -F 99 -a -g -- sleep 30

此时会在当前目录下生成perf.data文件。

生成火焰图

sudo perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > out.svg

会自动在当前目录下查找perf.data文件,然后生成svg文件。

然后会自动生成相对应的火焰图

Java混合模式火焰图

安装 perf

perf --version

安装perf-map-agent

安装gcc和gcc-c++

sudo yum install gcc
sudo yum install gcc-c++

安装cmake

sudo mkdir /home/q/perf-tools
cd /home/q/perf-tools/cmake-2.8.6
sudo  ./configure --prefix=/home/q/cmake-2.8.6
sudo make && make install

安装perf-map-agent

git clone https://github.com/jvm-profiling-tools/perf-map-agent
cd /home/q/perf-tools/perf-map-agent
sudo /home/q/cmake-2.8.6/bin/cmake . && sudo make

配置Java选项

JDK >= Java 8 update 60 build 19
参数  **-XX:+PreserveFramePointer**

检查Jdk

java -version

检查JVM

ps wwp `pgrep -n java`|grep PreserveFramePointer  --color

设置 -XX:+PreserveFramePointer

-Xms24g -Xmx24g -server -XX:+DisableExplicitGC -Dqunar.logs=CATALINA_BASE/logs -Dqunar.cache=CATALINA_BASE/cache -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGC -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=1 -Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintSafepointStatistics -XX:ReservedCodeCacheSize=512m -XX:CMSInitiatingOccupancyFraction=50 -XX:+UseCMSInitiatingOccupancyOnly -XX:MaxTenuringThreshold=14 -XX:+PreserveFramePointer

安装FlameGraph

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

生成脚本

sudo ./gen-flame-graph.sh SLEEP_SECONDSPID

具体脚本内容

#!/bin/bash
SLEEP_SECONDS=1
PID=2
PERF_TOOLS_HOME=(pwd)
GEN_RESULTS_DIR=PERF_TOOLS_HOME/gen-flame-graphs-result
AGENT_HOME=PERF_TOOLS_HOME/perf-map-agent
AGENT_JAR=AGENT_HOME/attach-main.jar
AGENT_OUT=AGENT_HOME/out
FLAME_GRAPH_PL_HOME=PERF_TOOLS_HOME/FlameGraph
FLAME_GRAPH_GENERAGED_FILE=PERF_TOOLS_HOME/flamegraph-mixed-model-`date +%Y%m%d-%H%M%S`.svg
MAP_FILE=/tmp/perf-PID.map
function check_env(){
  if [[ ! -x JAVA_HOME ]]; then
      echo "ERROR: JAVA_HOME not set correctly; edit0 and fix"
      exit
  fi
  if [[ ! -x AGENT_HOME ]]; then
      echoAGENT_HOME
      echo "ERROR: AGENT_HOME not set correctly; edit 0 and fix."
      exit
  fi
  if [[ ! -xGEN_RESULTS_DIR ]]; then
      echo "ERROR: 'GEN_RESULTS_DIR' not found;Please mkdir a file :'GEN_RESULTS_DIR'"
      exit
  fi
}
#generate perf.data with command: perf record
function perf_record(){
  echo "Perf record for all processors with sleep 30 seconds."
  cmd_perf="sudo perf record -F 99 -a -g -- sleep 30"
  eval cmd_perf
  if [[ -e "./perf.data" ]]; then
     echo "SUCCESS: perf.data was generated."
  else
     echo "ERROR: perf.data not generated;edit0 and fix."
     exit
  fi
}
#agent mapping pid
function gen_map_file(){
  user=(ps ho user -pPID)
  echo "Agent mapping PID PID with useruser"
  cmd_agent="cd AGENT_HOME;java -cp attach-main.jar:JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce PID"
  cmd_agent="sudo -uuser sh -c 'cmd_agent'"
  evalcmd_agent
  if [[ -e "MAP_FILE" ]]; then
     echo "generate map file"
     chown rootMAP_FILE
     chmod 666 MAP_FILE
  else
     echo "ERROR:MAP_FILE not created."
     exit
  fi
}
#generate flame graph
function gen_flame_graph(){
  cmd_stack="sudo perf script|FLAME_GRAPH_PL_HOME/stackcollapse-perf.pl  --pid|FLAME_GRAPH_PL_HOME/flamegraph.pl --color=java --width=800 --minwidth=0.5 --title="PID flame graph"  --hash>FLAME_GRAPH_GENERAGED_FILE"
  eval $cmd_stack
}
check_env
perf_record
gen_map_file
gen_flame_graph
echo "SUCCESS: generate flame graph."

使用脚本

改脚本是对Java进程的8708采样30s,生成flamegraph-mixed-model-date +%Y%m%d-%H%M%S.svg的Java混合模式火焰图

sudo ./gen-flame-graph.sh 30 8708

生成的图片

图例 | Java混合模式分析之火焰图实例插图2