一、Async-profiler 核心概念与安装
1.1 Async-profiler 是什么
bash
复制
下载
# Async-profiler 是一个低开销的Java分析器
# 特点:
# 1. 基于 HotSpot的AsyncGetCallTrace API
# 2. 支持多种事件:CPU、分配、锁、文件I/O等
# 3. 生成火焰图(Flame Graph)
# 4. 支持多种输出格式
# 项目地址:https://github.com/async-profiler/async-profiler
1.2 下载与安装
bash
复制
下载
# 1. 下载最新版本
wget https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.8.1/async-profiler-2.8.1-linux-x64.tar.gz
tar -xzf async-profiler-2.8.1-linux-x64.tar.gz
cd async-profiler-2.8.1-linux-x64
# 2. 权限设置(加载内核模块需要)
sudo chmod +x profiler.sh
sudo chown root:root libasyncProfiler.so
sudo chmod 755 libasyncProfiler.so
# 3. 加载内核模块(可选,提升性能)
sudo ./profiler.sh install
# 4. 验证安装
./profiler.sh -v
# 输出示例:
# Async-profiler 2.8.1 built on May 18 2023
# Copyright 2016-2023 Andrei Pangin
# Linux 5.15.0-78-generic #85-Ubuntu SMP x86_64
# Java HotSpot(TM) 64-Bit Server VM 17.0.7+8-LTS-224
1.3 基本使用示例
java
复制
下载
// 示例应用:用于分析的热点代码
public class HotSpotExample {
// 热点方法1:CPU密集型计算
public void cpuIntensiveTask() {
long sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += Math.sin(i) * Math.cos(i);
}
System.out.println("Sum: " + sum);
}
// 热点方法2:字符串操作
public void stringOperations() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append("item").append(i).append(",");
}
String result = sb.toString();
}
// 热点方法3:递归调用
public int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 热点方法4:I/O操作
public void fileOperations() throws IOException {
for (int i = 0; i < 100; i++) {
try (BufferedWriter writer = new BufferedWriter(
new FileWriter("temp" + i + ".txt"))) {
writer.write("Data " + i);
}
}
}
public static void main(String[] args) throws Exception {
HotSpotExample example = new HotSpotExample();
// 模拟不同负载
for (int i = 0; i < 100; i++) {
example.cpuIntensiveTask();
example.stringOperations();
example.fibonacci(30);
example.fileOperations();
Thread.sleep(10);
}
}
}
二、CPU 热点分析实战
2.1 基本分析命令
bash
复制
下载
# 1. 查找Java进程ID
jps -l
# 输出:
# 12345 com.example.MainApplication
# 12346 sun.tools.jps.Jps
# 2. 启动CPU分析(持续30秒)
./profiler.sh -d 30 -f cpu_profile.html 12345
# 3. 常用参数详解
./profiler.sh start [options] <pid> # 开始分析
./profiler.sh stop [options] <pid> <output> # 停止并输出结果
./profiler.sh status <pid> # 查看分析状态
./profiler.sh list <pid> # 列出支持的事件
# 核心参数:
# -d, --duration <sec> 分析持续时间
# -e, --event <event> 分析事件类型
# -f, --file <filename> 输出文件名
# -i, --interval <ns> 采样间隔(默认10ms)
# -t, --threads 包含线程信息
# -s, --simple 简化堆栈跟踪
# --alloc <bytes> 按分配大小采样
# --lock <duration> 按锁等待时间采样
2.2 不同事件类型分析
bash
复制
下载
# CPU 时间分析(默认)
./profiler.sh -e cpu -d 60 -f cpu_flame.svg <pid>
# 墙上时钟时间分析(包含阻塞时间)
./profiler.sh -e wall -d 60 -f wall_flame.svg <pid>
# 分配分析(按分配大小)
./profiler.sh -e alloc -d 60 --alloc 512 -f alloc_flame.svg <pid>
# 锁分析(按等待时间)
./profiler.sh -e lock -d 60 --lock 10ms -f lock_flame.svg <pid>
# 文件I/O分析
./profiler.sh -e file -d 60 -f file_flame.svg <pid>
# 完整的事件列表
./profiler.sh list <pid>
# 输出:
# Basic events:
# cpu
# alloc
# lock
# wall
# itimer
# Perf events:
# page-faults
# context-switches
# cycles
# instructions
# cache-misses
# branch-misses
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
2.3 实战分析脚本
bash
复制
下载
#!/bin/bash
# analyze.sh - 完整的性能分析脚本
PID=$1
DURATION=${2:-60}
OUTPUT_DIR="./profiles/$(date +%Y%m%d_%H%M%S)"
# 创建输出目录
mkdir -p $OUTPUT_DIR
echo "分析进程: $PID"
echo "持续时间: ${DURATION}秒"
echo "输出目录: $OUTPUT_DIR"
# 1. CPU 分析
echo "开始CPU分析..."
./profiler.sh -d $DURATION -f $OUTPUT_DIR/cpu_profile.html $PID
./profiler.sh -d $DURATION -f $OUTPUT_DIR/cpu_flame.svg --title "CPU Flame Graph" $PID
# 2. 分配分析
echo "开始分配分析..."
./profiler.sh -e alloc -d $DURATION --alloc 1024 -f $OUTPUT_DIR/alloc_profile.html $PID
# 3. 锁分析
echo "开始锁分析..."
./profiler.sh -e lock -d $DURATION --lock 5ms -f $OUTPUT_DIR/lock_profile.html $PID
# 4. 线程分析
echo "开始线程分析..."
./profiler.sh -d $DURATION -t -f $OUTPUT_DIR/threads.html $PID
# 5. 生成综合报告
echo "生成分析报告..."
cat > $OUTPUT_DIR/report.md << EOF
# 性能分析报告
## 基本信息
- 分析时间: $(date)
- 进程ID: $PID
- 分析时长: ${DURATION}秒
## 分析文件
- CPU分析: [cpu_profile.html](cpu_profile.html)
- CPU火焰图: [cpu_flame.svg](cpu_flame.svg)
- 分配分析: [alloc_profile.html](alloc_profile.html)
- 锁分析: [lock_profile.html](lock_profile.html)
- 线程分析: [threads.html](threads.html)
## 快速命令
\`\`\`bash
# 实时监控
./profiler.sh start $PID
./profiler.sh stop $PID output.svg
# 特定时间段分析
./profiler.sh -d 30 -f profile.svg $PID
\`\`\`
EOF
echo "分析完成!结果保存在: $OUTPUT_DIR"
2.4 Java Agent 模式
java
复制
下载
// 使用Java Agent进行自动分析
// 1. 启动时添加agent参数
// java -agentpath:/path/to/libasyncProfiler.so=start,event=cpu,file=profile.html -jar app.jar
// 2. 动态附加(无需重启)
// ./profiler.sh -d 60 -f profile.html <pid>
// 3. 在代码中控制分析
public class ProfilerController {
static {
// 加载async-profiler
System.loadLibrary("asyncProfiler");
}
public native void startProfiler(String event, long interval);
public native void stopProfiler(String filename);
public void analyzeCriticalSection() {
startProfiler("cpu", 10_000_000); // 10ms间隔
// 执行关键代码
performCriticalOperation();
stopProfiler("critical_section.svg");
}
private void performCriticalOperation() {
// 需要分析的关键代码
// ...
}
}
三、火焰图解读与分析方法
3.1 火焰图基本结构
svg
复制
下载
运行
<!-- 简化的火焰图SVG结构 -->
<svg>
<!-- 每个矩形代表一个栈帧 -->
<rect x="100" y="50" width="300" height="20" class="frame">
<title>java.lang.Thread.run() - 15.3% (153ms)</title>
</rect>
<!-- 子调用 -->
<rect x="100" y="70" width="200" height="20" class="frame">
<title>com.example.Service.process() - 10.2% (102ms)</title>
</rect>
<!-- 更深层调用 -->
<rect x="100" y="90" width="150" height="20" class="frame">
<title>com.example.Util.calculate() - 8.1% (81ms)</title>
</rect>
</svg>
3.2 火焰图解读指南
java
复制
下载
// 示例火焰图对应的代码模式
public class FlameGraphPatterns {
// 模式1:宽顶 - CPU热点
// 火焰图中顶部很宽的矩形
public void wideTopPattern() {
// 这个方法占用了大量CPU时间
while (true) {
heavyComputation(); // ← 热点
if (shouldStop()) break;
}
}
private void heavyComputation() {
// 实际的计算工作
for (int i = 0; i < 1000000; i++) {
Math.sqrt(i);
}
}
// 模式2:高楼 - 深度调用链
// 火焰图中很高很窄的栈
public void tallTowerPattern() {
deepRecursion(10); // ← 调用链很深
}
private void deepRecursion(int depth) {
if (depth == 0) return;
// 每次调用都增加栈深度
deepRecursion(depth - 1); // ← 递归调用
}
// 模式3:平台 - 平坦的调用
// 火焰图中多个相似的宽度
public void platformPattern() {
for (int i = 0; i < 100; i++) {
processItem(i); // ← 均匀分布的调用
}
}
private void processItem(int i) {
// 每个项目处理时间相似
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 模式4:孤岛 - 独立的热点
// 火焰图中分离的热点区域
public void isolatedIslandPattern() {
// 主逻辑
normalWork();
// 独立的热点(可能来自后台线程)
executor.submit(this::backgroundHotSpot);
moreNormalWork();
}
private void backgroundHotSpot() {
// 后台线程中的热点
while (true) {
backgroundComputation();
}
}
}
3.3 常见性能问题识别
java
复制
下载
public class PerformanceProblems {
// 问题1:CPU自旋等待
public void spinWaitProblem() {
while (!condition) {
// 空循环占用CPU
// 火焰图显示:窄而高的循环体
}
}
// 问题2:过度同步
public void overSynchronizationProblem() {
synchronized (this) {
// 锁范围过大
// 火焰图显示:锁方法占用大量宽度
doWork();
doMoreWork(); // ← 不需要同步的代码也在锁内
}
}
// 问题3:递归深度过大
public void deepRecursionProblem(int n) {
if (n <= 0) return;
// 深度递归可能导致栈溢出
// 火焰图显示:非常高的调用栈
deepRecursionProblem(n - 1);
}
// 问题4:大量小对象分配
public void allocationProblem() {
for (int i = 0; i < 1000000; i++) {
// 每次循环都创建新对象
String s = new String("item" + i); // ← 分配热点
process(s);
}
}
// 问题5:I/O阻塞
public void ioBlockingProblem() {
try {
// 同步I/O阻塞线程
// 火焰图显示:I/O方法占宽(wall-clock分析)
byte[] data = Files.readAllBytes(Path.of("largefile.txt"));
} catch (IOException e) {
e.printStackTrace();
}
}
// 问题6:正则表达式性能
public void regexPerformanceProblem() {
String input = "a".repeat(10000);
// 复杂的正则表达式
// 火焰图显示:Pattern.matcher占用大量CPU
boolean matches = input.matches("(a+)+b");
}
}
3.4 火焰图导航技巧
html
复制
下载
运行
<!-- 火焰图交互式解读指南 -->
<!DOCTYPE html>
<html>
<head>
<title>火焰图解读指南</title>
<style>
.flame-graph-tips {
margin: 20px;
padding: 20px;
border: 1px solid #ccc;
}
.hot-spot { color: red; font-weight: bold; }
.deep-stack { color: blue; }
.flat-pattern { color: green; }
</style>
</head>
<body>
<div class="flame-graph-tips">
<h2>火焰图导航技巧</h2>
<h3>1. 鼠标交互</h3>
<ul>
<li>悬停:查看方法详细信息(百分比、时间)</li>
<li>点击:放大到该调用栈</li>
<li>双击:重置视图</li>
<li>鼠标滚轮:水平缩放</li>
</ul>
<h3>2. 搜索功能</h3>
<ul>
<li>Ctrl+F:搜索方法名</li>
<li>高亮显示:匹配的栈帧会高亮</li>
<li>正则表达式:支持正则搜索</li>
</ul>
<h3>3. 颜色解读</h3>
<ul>
<li><span class="hot-spot">红色/橙色</span>:热点方法(CPU时间多)</li>
<li><span class="deep-stack">蓝色/紫色</span>:较深的调用栈</li>
<li><span class="flat-pattern">绿色</span>:平均分布的方法</li>
<li>颜色越暖,CPU占用越高</li>
</ul>
<h3>4. 关键指标</h3>
<ul>
<li>宽度 = CPU时间占比</li>
<li>高度 = 调用栈深度</li>
<li>顺序 = 按字母排序(不是调用顺序)</li>
<li>右侧百分比 = 该栈占总时间的比例</li>
</ul>
<h3>5. 分析方法</h3>
<ol>
<li>找最宽的栈帧(顶部热点)</li>
<li>向下追踪调用链(垂直方向)</li>
<li>查看平级调用(水平方向)</li>
<li>对比不同时间段火焰图</li>
<li>结合其他分析数据(分配、锁等)</li>
</ol>
</div>
</body>
</html>
四、高级分析技巧
4.1 按线程分析
bash
复制
下载
# 1. 按线程分组分析
./profiler.sh -d 60 -t -f threads.html <pid>
# 2. 分析特定线程
./profiler.sh -d 60 --threads "main|worker.*" -f specific_threads.svg <pid>
# 3. 线程状态分析
./profiler.sh -d 60 -e wall -f thread_states.svg <pid>
4.2 时间范围分析
bash
复制
下载
#!/bin/bash
# 分析应用启动阶段
PID=$1
echo "分析启动性能..."
# 1. 立即开始分析(启动阶段)
./profiler.sh start $PID
# 等待应用启动完成
sleep 30
# 2. 停止并保存启动阶段数据
./profiler.sh stop -f startup_profile.svg $PID
# 3. 分析稳定运行阶段
echo "分析稳定运行阶段..."
./profiler.sh -d 300 -f runtime_profile.svg $PID
# 4. 分析关闭阶段
echo "开始关闭阶段分析..."
./profiler.sh start $PID
# 触发关闭
kill -SIGTERM $PID
# 等待关闭完成
sleep 10
./profiler.sh stop -f shutdown_profile.svg $PID
4.3 对比分析
bash
复制
下载
#!/bin/bash
# compare_profiles.sh - 对比两个性能分析
PROFILE1=$1
PROFILE2=$2
# 生成差异火焰图
echo "生成差异分析..."
# 1. 转换为折叠格式
java -cp async-profiler.jar \
jfr2flame --collapse \
$PROFILE1.jfr > profile1.collapsed
java -cp async-profiler.jar \
jfr2flame --collapse \
$PROFILE2.jfr > profile2.collapsed
# 2. 计算差异
./flamegraph.pl \
--difference profile1.collapsed profile2.collapsed \
> diff_flame.svg
# 3. 生成对比报告
cat > comparison_report.html << EOF
<html>
<head>
<title>性能对比报告</title>
</head>
<body>
<h1>性能对比分析</h1>
<h2>阶段1: $(basename $PROFILE1)</h2>
<object data="${PROFILE1}.svg" width="100%" height="600"></object>
<h2>阶段2: $(basename $PROFILE2)</h2>
<object data="${PROFILE2}.svg" width="100%" height="600"></object>
<h2>差异分析</h2>
<object data="diff_flame.svg" width="100%" height="600"></object>
<h2>关键发现</h2>
<ul>
<li>红色: 阶段2中增加的热点</li>
<li>蓝色: 阶段2中减少的热点</li>
<li>灰色: 无显著变化</li>
</ul>
</body>
</html>
EOF
echo "对比分析完成!"
4.4 内存分配分析
java
复制
下载
// 内存分配热点示例
public class AllocationHotspots {
// 热点1:循环内创建对象
public void hotspot1_loopAllocation() {
List<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
// 每次迭代都创建新对象
list.add(new String("item-" + i)); // ← 分配热点
}
}
// 热点2:大对象分配
public void hotspot2_largeAllocation() {
// 分配大数组
byte[] buffer = new byte[10 * 1024 * 1024]; // 10MB
// 火焰图显示:byte[] 初始化占用宽度
}
// 热点3:临时对象
public void hotspot3_temporaryObjects() {
for (int i = 0; i < 10000; i++) {
// 创建临时对象,立即丢弃
String temp = processAndDiscard(i);
}
}
// 热点4:装箱操作
public void hotspot4_boxing() {
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
// 自动装箱创建Integer对象
numbers.add(i); // ← 分配热点
}
}
// 优化建议
public void optimizedVersion() {
// 1. 对象池
ObjectPool<ExpensiveObject> pool = new ObjectPool<>();
// 2. 重用对象
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.setLength(0); // 重用StringBuilder
sb.append("item-").append(i);
String s = sb.toString();
}
// 3. 避免装箱
IntStream.range(0, 100000)
.forEach(i -> processPrimitive(i));
}
}
4.5 锁竞争分析
java
复制
下载
// 锁竞争热点示例
public class LockContentionHotspots {
private final Object globalLock = new Object();
private final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
// 热点1:全局锁竞争
public void hotspot1_globalLock() {
synchronized (globalLock) { // ← 竞争热点
// 所有线程都竞争这个锁
processSharedResource();
}
}
// 热点2:锁粒度太粗
public void hotspot2_coarseGrainedLock() {
synchronized (this) { // ← 锁住整个对象
// 很多不相关的操作都在锁内
updateConfig();
processData(); // ← 其实不需要同步
saveToDisk();
}
}
// 热点3:嵌套锁
public void hotspot3_nestedLocks() {
synchronized (lockA) {
// 持有锁A时尝试获取锁B
synchronized (lockB) { // ← 死锁风险
// ...
}
}
}
// 热点4:长时间持有锁
public void hotspot4_longHolding() {
synchronized (resource) {
// 快速操作
prepareData();
// 慢速I/O操作(不应该在锁内)
saveToDatabase(); // ← 阻塞其他线程
// 更多操作
cleanUp();
}
}
// 优化版本
public void optimizedLocking() {
// 1. 减小锁粒度
private final Object[] segmentLocks = new Object[16];
// 2. 使用读写锁
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
// 3. 使用并发集合
private final ConcurrentHashMap<String, Data> concurrentMap =
new ConcurrentHashMap<>();
// 4. 无锁编程
private final AtomicLong counter = new AtomicLong();
// 5. 锁分段
public void segmentLock(String key) {
int segment = key.hashCode() & 15;
synchronized (segmentLocks[segment]) {
// 只锁住一个分段
}
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
五、集成与自动化
5.1 与构建工具集成
xml
复制
下载
运行
<!-- Maven pom.xml 配置 -->
<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
-agentpath:${profiler.home}/libasyncProfiler.so=start,event=cpu,file=target/profiles/test-%t.svg
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<executable>java</executable>
<arguments>
<argument>-agentpath:${profiler.home}/libasyncProfiler.so=start,event=cpu,file=profile.svg</argument>
<argument>-jar</argument>
<argument>${project.build.finalName}.jar</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>profiling</id>
<properties>
<profiler.home>/opt/async-profiler</profiler.home>
</properties>
</profile>
</profiles>
</project>
5.2 Docker 集成
dockerfile
复制
下载
# Dockerfile for profiling
FROM openjdk:17-jdk-slim
# 安装async-profiler
RUN apt-get update && apt-get install -y \
wget \
&& wget https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.8.1/async-profiler-2.8.1-linux-x64.tar.gz \
&& tar -xzf async-profiler-2.8.1-linux-x64.tar.gz \
&& mv async-profiler-2.8.1-linux-x64 /opt/async-profiler \
&& rm async-profiler-2.8.1-linux-x64.tar.gz
# 设置权限
RUN chmod +x /opt/async-profiler/profiler.sh
# 复制应用
COPY target/app.jar /app.jar
# 健康检查(包含profiler状态)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD /opt/async-profiler/profiler.sh status 1 || exit 1
# 启动命令(包含profiler agent)
ENTRYPOINT ["java", \
"-agentpath:/opt/async-profiler/libasyncProfiler.so=start,server", \
"-jar", "/app.jar"]
# 暴露profiler控制端口
EXPOSE 8080 9090
yaml
复制
下载
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
- "9090:9090" # profiler控制端口
volumes:
- ./profiles:/profiles
environment:
- PROFILER_OPTS=start,event=cpu,file=/profiles/profile.svg
command: >
java
-agentpath:/opt/async-profiler/libasyncProfiler.so=start,event=cpu,file=/profiles/profile.svg,server
-jar /app.jar
profiler-ui:
image: nginx:alpine
ports:
- "8081:80"
volumes:
- ./profiles:/usr/share/nginx/html/profiles
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
5.3 Kubernetes 集成
yaml
复制
下载
# kubernetes deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: profiled-app
spec:
replicas: 1
selector:
matchLabels:
app: profiled-app
template:
metadata:
labels:
app: profiled-app
spec:
containers:
- name: app
image: profiled-app:latest
ports:
- containerPort: 8080
- containerPort: 9090 # profiler端口
volumeMounts:
- name: profile-data
mountPath: /profiles
env:
- name: JAVA_TOOL_OPTIONS
value: "-agentpath:/opt/async-profiler/libasyncProfiler.so=start,event=cpu,file=/profiles/profile.svg,server"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1"
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
volumes:
- name: profile-data
emptyDir: {}
initContainers:
- name: init-profiler
image: busybox
command: ['sh', '-c', 'mkdir -p /profiles && chmod 777 /profiles']
volumeMounts:
- name: profile-data
mountPath: /profiles
---
# Service
apiVersion: v1
kind: Service
metadata:
name: profiled-app-service
spec:
selector:
app: profiled-app
ports:
- name: http
port: 8080
targetPort: 8080
- name: profiler
port: 9090
targetPort: 9090
---
# 分析任务Job
apiVersion: batch/v1
kind: Job
metadata:
name: profile-analysis
spec:
template:
spec:
containers:
- name: profiler
image: profiled-app:latest
command:
- /opt/async-profiler/profiler.sh
args:
- "-d"
- "300"
- "-f"
- "/profiles/production-profile.svg"
- "1"
volumeMounts:
- name: profile-data
mountPath: /profiles
restartPolicy: Never
volumes:
- name: profile-data
persistentVolumeClaim:
claimName: profile-pvc
5.4 自动化分析脚本
python
复制
下载
#!/usr/bin/env python3
"""
automated_profiling.py - 自动化性能分析脚本
"""
import subprocess
import time
import json
import os
from datetime import datetime
from typing import Dict, List, Optional
class AutomatedProfiler:
def __init__(self, profiler_path: str, output_dir: str = "./profiles"):
self.profiler_path = profiler_path
self.output_dir = output_dir
os.makedirs(output_dir, exist_ok=True)
def find_java_process(self, name_pattern: str) -> Optional[int]:
"""查找Java进程ID"""
try:
result = subprocess.run(
["jps", "-l"],
capture_output=True,
text=True,
check=True
)
for line in result.stdout.strip().split('\n'):
if line:
pid, process_name = line.split(' ', 1)
if name_pattern in process_name:
return int(pid)
except subprocess.CalledProcessError as e:
print(f"查找进程失败: {e}")
return None
def start_profiling(self, pid: int, duration: int = 60,
event: str = "cpu") -> str:
"""开始性能分析"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = os.path.join(
self.output_dir,
f"profile_{event}_{timestamp}.svg"
)
cmd = [
self.profiler_path,
"-d", str(duration),
"-e", event,
"-f", output_file,
str(pid)
]
print(f"开始分析: {' '.join(cmd)}")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
print(f"分析完成: {output_file}")
return output_file
except subprocess.CalledProcessError as e:
print(f"分析失败: {e.stderr}")
return None
def analyze_multiple_events(self, pid: int, duration: int = 60):
"""分析多个事件类型"""
events = ["cpu", "alloc", "lock", "wall"]
results = {}
for event in events:
print(f"\n分析事件: {event}")
output = self.start_profiling(pid, duration, event)
if output:
results[event] = output
# 如果是CPU分析,额外生成线程视图
if event == "cpu":
thread_output = self.start_thread_analysis(pid, duration)
results["threads"] = thread_output
return results
def start_thread_analysis(self, pid: int, duration: int) -> str:
"""线程分析"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = os.path.join(
self.output_dir,
f"threads_{timestamp}.html"
)
cmd = [
self.profiler_path,
"-d", str(duration),
"-t",
"-f", output_file,
str(pid)
]
subprocess.run(cmd, capture_output=True)
return output_file
def generate_report(self, profile_files: Dict[str, str]):
"""生成HTML报告"""
report_file = os.path.join(self.output_dir, "report.html")
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>性能分析报告</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
.profile {{ margin: 20px 0; border: 1px solid #ddd; padding: 15px; }}
img {{ max-width: 100%; height: auto; }}
.summary {{ background: #f5f5f5; padding: 15px; }}
</style>
</head>
<body>
<h1>性能分析报告</h1>
<div class="summary">
<p>生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<p>分析文件数: {len(profile_files)}</p>
</div>
"""
for event_type, file_path in profile_files.items():
if file_path.endswith('.svg'):
html += f"""
<div class="profile">
<h2>{event_type.upper()} 分析</h2>
<object data="{os.path.basename(file_path)}"
type="image/svg+xml"
width="100%"
height="600">
</object>
<p>文件: {os.path.basename(file_path)}</p>
</div>
"""
html += """
</body>
</html>
"""
with open(report_file, 'w') as f:
f.write(html)
print(f"报告已生成: {report_file}")
return report_file
def continuous_profiling(self, pid: int, interval: int = 300,
count: int = 12):
"""连续性能分析(用于监控)"""
profiles = []
for i in range(count):
print(f"\n第 {i+1}/{count} 次分析")
output = self.start_profiling(pid, interval, "cpu")
if output:
profiles.append(output)
if i < count - 1:
print(f"等待 {interval} 秒...")
time.sleep(interval)
return profiles
def main():
# 配置
PROFILER_PATH = "./profiler.sh"
APP_NAME = "com.example.MainApplication"
profiler = AutomatedProfiler(PROFILER_PATH)
# 查找进程
pid = profiler.find_java_process(APP_NAME)
if not pid:
print(f"未找到进程: {APP_NAME}")
return
print(f"找到进程: {pid}")
# 分析选项
import argparse
parser = argparse.ArgumentParser(description='自动化性能分析')
parser.add_argument('--mode', choices=['single', 'multi', 'continuous'],
default='multi', help='分析模式')
parser.add_argument('--duration', type=int, default=60,
help='每次分析时长(秒)')
parser.add_argument('--continuous-count', type=int, default=12,
help='连续分析次数')
args = parser.parse_args()
if args.mode == 'single':
# 单次CPU分析
profile = profiler.start_profiling(pid, args.duration, "cpu")
if profile:
print(f"分析结果: {profile}")
elif args.mode == 'multi':
# 多事件分析
profiles = profiler.analyze_multiple_events(pid, args.duration)
report = profiler.generate_report(profiles)
print(f"综合分析完成,报告: {report}")
elif args.mode == 'continuous':
# 连续监控
profiles = profiler.continuous_profiling(
pid, args.duration, args.continuous_count
)
print(f"连续分析完成,共 {len(profiles)} 个文件")
if __name__ == "__main__":
main()
六、实战案例分析
6.1 Web 应用性能分析
java
复制
下载
// Web应用常见性能问题
@RestController
public class WebController {
// 案例1:N+1查询问题
@GetMapping("/users")
public List<UserDTO> getUsers() {
List<User> users = userRepository.findAll(); // 第一次查询
return users.stream()
.map(user -> {
// 对每个用户单独查询(N次查询)
List<Order> orders = orderRepository.findByUserId(user.getId());
return convertToDTO(user, orders);
})
.collect(Collectors.toList());
}
// 案例2:大JSON序列化
@GetMapping("/report")
public Report getReport() {
Report report = generateLargeReport(); // 生成大对象
// 序列化大对象可能成为瓶颈
// 火焰图显示:Jackson ObjectMapper占用大量CPU
return report;
}
// 案例3:同步阻塞调用
@GetMapping("/aggregate")
public AggregateData aggregate() {
// 同步调用多个服务
Data data1 = restTemplate.getForObject("http://service1/data", Data.class);
Data data2 = restTemplate.getForObject("http://service2/data", Data.class);
Data data3 = restTemplate.getForObject("http://service3/data", Data.class);
// 每个调用都阻塞线程
return combine(data1, data2, data3);
}
// 案例4:缓存击穿
@Cacheable("products")
@GetMapping("/product/{id}")
public Product getProduct(@PathVariable String id) {
// 当缓存失效时,大量请求直接访问数据库
return productRepository.findById(id) // 数据库热点
.orElseThrow(() -> new NotFoundException("Product not found"));
}
// 优化版本
@GetMapping("/optimized/users")
public List<UserDTO> getUsersOptimized() {
// 使用JOIN查询一次性获取所有数据
List<User> users = userRepository.findAllWithOrders();
return users.stream()
.map(this::convertToDTO)
.collect(Collectors.toList());
}
@Async
@GetMapping("/async/aggregate")
public CompletableFuture<AggregateData> aggregateAsync() {
CompletableFuture<Data> future1 = CompletableFuture.supplyAsync(
() -> restTemplate.getForObject("http://service1/data", Data.class)
);
CompletableFuture<Data> future2 = CompletableFuture.supplyAsync(
() -> restTemplate.getForObject("http://service2/data", Data.class)
);
CompletableFuture<Data> future3 = CompletableFuture.supplyAsync(
() -> restTemplate.getForObject("http://service3/data", Data.class)
);
return CompletableFuture.allOf(future1, future2, future3)
.thenApply(v -> combine(future1.join(), future2.join(), future3.join()));
}
}
6.2 大数据处理分析
java
复制
下载
// 大数据处理性能问题
public class BigDataProcessor {
// 案例1:GC压力过大
public void processLargeDataset(List<Record> records) {
List<Result> results = new ArrayList<>();
for (Record record : records) { // 百万条记录
// 处理每条记录都创建新对象
Result result = processRecord(record);
results.add(result); // ArrayList扩容压力
// 中间对象不断创建和回收
String intermediate = transform(record);
Result temp = calculate(intermediate);
}
}
// 案例2:过度序列化/反序列化
public void distributedProcessing() {
// 节点间大量数据传输
byte[] serialized = serialize(largeObject); // CPU热点
sendOverNetwork(serialized);
// 接收方
byte[] received = receiveFromNetwork();
LargeObject obj = deserialize(received); // 另一个CPU热点
}
// 案例3:Shuffle阶段性能瓶颈
public void sparkLikeProcessing() {
// Map阶段
List<Pair<String, Integer>> mapped = data.stream()
.map(record -> new Pair<>(record.getKey(), 1))
.collect(Collectors.toList());
// Shuffle阶段(按Key分组)
Map<String, List<Pair<String, Integer>>> grouped = mapped.stream()
.collect(Collectors.groupingBy(Pair::getKey));
// Reduce阶段
Map<String, Integer> reduced = grouped.entrySet().stream()
.map(entry -> new Pair<>(
entry.getKey(),
entry.getValue().stream()
.mapToInt(Pair::getValue)
.sum()
))
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
// 案例4:内存外排问题
public void externalSortIssue() {
List<Record> sorted = records.stream()
.sorted(Comparator.comparing(Record::getTimestamp)) // 内存排序
.collect(Collectors.toList());
// 当数据量超过内存时,性能急剧下降
}
// 优化策略
public void optimizedProcessing() {
// 1. 使用原始类型集合
IntArrayList intList = new IntArrayList();
Long2IntOpenHashMap map = new Long2IntOpenHashMap();
// 2. 对象复用
RecordProcessor processor = new RecordProcessor();
for (Record record : records) {
processor.process(record); // 内部重用缓冲区
}
// 3. 流式处理
records.stream()
.map(this::processRecord)
.forEach(this::handleResult); // 避免收集到List
// 4. 分批处理
int batchSize = 1000;
for (int i = 0; i < records.size(); i += batchSize) {
List<Record> batch = records.subList(i, Math.min(i + batchSize, records.size()));
processBatch(batch);
}
// 5. 使用堆外内存
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024 * 1024);
}
}
七、性能优化检查清单
markdown
复制
下载
# Async-profiler 性能优化检查清单
## 数据收集阶段
- [ ] 确定分析目标(CPU、内存、锁等)
- [ ] 选择合适的采样间隔(默认10ms)
- [ ] 设置足够的分析时长(至少60秒)
- [ ] 在生产环境采样时注意性能影响
- [ ] 记录应用状态(QPS、内存使用等)
## 火焰图分析阶段
### 第一步:整体观察
- [ ] 确认火焰图整体形状
- [ ] 识别最宽的热点(顶部)
- [ ] 注意异常模式(平顶、高楼、孤岛)
### 第二步:热点深入
- [ ] 点击热点方法查看详细信息
- [ ] 追踪调用链(自上而下)
- [ ] 查看平级调用
- [ ] 搜索特定方法或包名
### 第三步:问题识别
- [ ] CPU自旋(空循环)
- [ ] 锁竞争(synchronized块)
- [ ] 过度分配(new操作)
- [ ] 递归过深
- [ ] I/O阻塞
- [ ] 正则表达式
## 优化实施阶段
### CPU优化
- [ ] 算法优化(降低复杂度)
- [ ] 循环优化(减少迭代次数)
- [ ] 缓存计算结果
- [ ] 使用更高效的数据结构
- [ ] 批量处理替代单条处理
### 内存优化
- [ ] 减少对象创建(对象池、重用)
- [ ] 避免装箱操作
- [ ] 使用原始类型集合
- [ ] 及时释放资源
- [ ] 调整GC参数
### I/O优化
- [ ] 使用缓冲区
- [ ] 异步I/O
- [ ] 批量读写
- [ ] 压缩数据
- [ ] 缓存频繁访问的数据
### 并发优化
- [ ] 减小锁粒度
- [ ] 使用读写锁
- [ ] 无锁数据结构
- [ ] 线程池调优
- [ ] 避免死锁
## 验证阶段
- [ ] 优化后重新分析
- [ ] 对比优化前后火焰图
- [ ] 监控性能指标改善
- [ ] 压力测试验证
- [ ] 记录优化效果
## 报告生成
- [ ] 保存原始分析数据
- [ ] 记录优化措施
- [ ] 量化性能提升
- [ ] 分享最佳实践
- [ ] 更新性能基准
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
八、常见问题与解决方案
java
复制
下载
// Async-profiler 常见问题解答
public class FAQSolutions {
/**
* 问题1:无法附加到JVM进程
* 解决方案:
*/
public void solution1_cannotAttach() {
// 1. 检查权限
// sudo chmod 666 /proc/sys/kernel/perf_event_paranoid
// sudo chmod 666 /proc/sys/kernel/kptr_restrict
// 2. 使用正确的用户
// 确保以与JVM相同的用户运行profiler
// 3. 检查JVM版本
// Async-profiler需要Java 7+,完整功能需要Java 8u60+
// 4. 使用agent模式启动
// java -agentpath:/path/to/libasyncProfiler.so=start -jar app.jar
}
/**
* 问题2:采样数据不准确
* 解决方案:
*/
public void solution2_inaccurateSampling() {
// 1. 增加采样时长
// ./profiler.sh -d 300 <pid> # 5分钟
// 2. 减小采样间隔
// ./profiler.sh -i 1ms <pid> # 1毫秒间隔
// 3. 使用合适的事件类型
// CPU时间 vs 墙上时钟时间
// 4. 分析稳定状态
// 避免在启动/关闭阶段采样
}
/**
* 问题3:火焰图显示未知方法
* 解决方案:
*/
public void solution3_unknownMethods() {
// 1. 生成调试符号
// -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
// 2. 包含内联方法
// ./profiler.sh --include-inlined <pid>
// 3. 查看JIT编译代码
// -XX:+PrintCompilation -XX:+PrintInlining
// 4. 使用简单模式
// ./profiler.sh -s <pid>
}
/**
* 问题4:分析影响应用性能
* 解决方案:
*/
public void solution4_performanceImpact() {
// 1. 增加采样间隔
// ./profiler.sh -i 100ms <pid> # 100毫秒间隔
// 2. 减少分析时长
// ./profiler.sh -d 10 <pid> # 只分析10秒
// 3. 使用安全模式
// ./profiler.sh --safe-mode <pid>
// 4. 离线分析
// 收集性能数据,离线生成火焰图
}
/**
* 问题5:无法分析容器内应用
* 解决方案:
*/
public void solution5_containerIssues() {
// 1. 在容器内运行profiler
// docker exec -it <container> ./profiler.sh <pid>
// 2. 使用特权模式
// docker run --privileged ...
// 3. 挂载profiler二进制文件
// docker run -v /host/profiler:/profiler ...
// 4. 使用sidecar容器
// 通过共享的PID命名空间访问
}
/**
* 问题6:内存分析不准确
* 解决方案:
*/
public void solution6_memoryIssues() {
// 1. 调整分配阈值
// ./profiler.sh -e alloc --alloc 256 <pid> # 只跟踪>256B的分配
// 2. 结合GC日志
// -Xlog:gc* -XX:+PrintGCDetails
// 3. 使用多个阈值分析
// 小对象、中等对象、大对象分别分析
// 4. 注意TLAB分配
// TLAB内部分配可能不被跟踪
}
}
总结:最佳实践
-
选择合适的分析时机:避开应用启动/关闭阶段
-
足够的采样时间:至少60秒,覆盖完整业务场景
-
多种事件结合:CPU、分配、锁分析结合使用
-
对比分析:优化前后对比,量化改进效果
-
持续监控:在生产环境定期采样,建立性能基准
-
团队共享:建立性能分析文化,分享火焰图解读经验
-
自动化:集成到CI/CD流水线,自动性能回归测试
通过Async-profiler,可以快速定位性能瓶颈,数据驱动地进行优化,显著提升应用性能。