免费用鲲鹏资源!华为开发者空间实操:DevKit Java 性能分析,新手也能上手

1 概述
1.1 背景介绍
鲲鹏 DevKit Java 性能分析工具是一款针对鲲鹏平台的性能分析和调优工具,Java 性能分析工具是针对基于鲲鹏的服务器上运行的 Java 程序的性能分析和优化工具,能图形化显示 Java 程序的堆、线程、锁、垃圾回收等信息,收集热点函数、定位程序瓶颈点,帮助用户采取针对性优。
1.2 适用对象
企业
个人开发者
高校学生
1.3 案例时间
本案例总时长预计 40 分钟。
1.4 案例流程

说明:
① 自动部署鲲鹏服务器;② 安装鲲鹏 Devkit 插件;③ 配置在线分析环境,通过浏览器访问鲲鹏服务器,添加目标节点;④ 执行、编译死锁代码;⑤ 死锁在线分析;⑥ 修改代码,编译执行;⑦ 在线分析。
1.5 资源总览
本案例预计花费总计 0 元。
利用鲲鹏DevKit剖析Java死锁问题的性能分析👈👈👈👈完整版 案例体验请点这里进行查看。
2 操作步骤
2.1 自动部署鲲鹏服务器
在云主机桌面右键选择“Open Terminal Here”,打开命令终端窗口。

执行自动部署命令如下:
该命令会自动部署鲲鹏服务器。首次部署会直接执行,旧资源未到期时重复部署,会提示是否删除前面创建的资源,可以删除旧资源再次部署。

可以看到鲲鹏服务器链接:https://113.44.69.106:8084,表示部署成功,记录部署远端服务器公网IP,如截图中对应的就是:113.44.69.106 。

在开发者空间,我的开发资源下,找到鲲鹏沙箱环境,点击“使用详情”,可以看到我们刚刚自动部署的鲲鹏服务器,创建时间及资源释放时间等信息。

2.2 安装鲲鹏 Devkit 插件
云主机桌面单击鼠标右键,在菜单中选择“Open Terminal Here”打开终端。

通过 ssh 连接云服务器,如果有 yes/no 选择输入“yes”,然后输入 “云主机密码”,出现“Welcome to XXX”代表连接成功。

输入地址“wget https://kunpeng-repoXXX”下载鲲鹏DevKit压缩包:

稍作等待,下载完成执行“ls”可以看到下载的压缩包。

解压文件.

进入文件夹,可以看到“install.sh”,执行安装。

如下图步骤,第一个默认回车即可。

出现如下图所示时选择“2”,去选择插件(1 是安装全部,如选错可以“ctrl + c”停止,重复上面 “sudo ./install.sh”指令即可)。

本案例只用到 java 性能分析,选择“6”,后面选项根据提示操作,多数默认回车即可,直至安装成功。

安装成功可以看到“https://192.168.2.191:8086” 。

执行命令,修改端口。
如下命令的第一个 ip 为 2.1 自动部署的鲲鹏服务器 IP,第二个 ip 为上图安装成功的 IP。

2.3 配置在线分析环境
通过浏览器访问鲲鹏服务器,添加目标节点,以配置在线分析环境。
打开浏览器,输入“https://XX.XX.XX.XX:8086”(IP为2.1自动部署的鲲鹏服务器 IP),如果提示风险,点击接受并继续。

创建密码,并登录。

登录完成后添加目标环境:点击左上角“调优 添加”,在添加目标环境弹窗中输入:“用户名”,“云服务器密码”,点击“确定”;跳出来弹窗点击“确定”即可。

2.4 执行编译代码
云主机桌面单击鼠标右键,在菜单中选择“Open Terminal Here”打开终端。

通过 ssh 连接云服务器,如果有 yes/no 选择输入“yes”,然后输入 “云主机密码”,出现“Welcome to XXX”代表连接成功。
进入到云主机后,执行如下命令查看当前云主机的 JDK 安装情况。

可以看到 JDK 默认是安装配置好的,输入“vim DeadLock.java”,回车进入,点击“i”进入编辑模式。
DeadLock.java 代码地址,如跳转不了请输入以下地址复制代码:https://github.com/kunpengcompute/devkitdemo/blob/main/Hyper_tuner/testdemo/基于java性能分析工具的死锁调优实践/DeadLock.java
DeadLock.java 代码如下(因格式可能存在问题建议进入链接进行复制):
代码复制完毕,键盘点击“esc”,然后输入“:wq”保存退出。

编译 java 文件,然后执行代码,可以看到执行成功。

2.5 死锁在线分析
进入浏览器界面,点击“调优”,点击“root@XXX”,点击 DeadLock 的“在线分析”。

对程序进行在线分析,在概览页签下观察每种状态的线程数,发现有两个线程处于阻塞状态,有死锁的嫌疑。

切换到 CPU 页签下的线程列表,找到阻塞状态的线程。观察一段时间发现发两个线程一直处于阻塞状态。

执行多次线程转储操作:在 CPU 下,点击“执行线程转储”,转储成功后点击“线程转储”下的锁分析图。发现两个阻塞中的线程发生了死锁。

选择 CPU 页签中线程转储下的原始数据,根据线程转储的原始数据得到死锁的相关信息。
点击优化建议的“查看详情”,查看死锁问题优化建议。

此时,我们已经获取到阻塞的原因及优化建议,点击“停止分析”。

2.6 修改代码,再次编译分析
找到阻塞的原因,接下来参考优化建议进行代码修改,解决死锁问题。
优化点解析说明:
共享变量的使用及操作
修改后的 DeadLock 类中定义了一个 volatile 修饰的共享变量 i,并且在每个线程获取锁成功并执行完同步代码块后,会对 i 进行自增操作(i++)。而修改前的 DeadLock 类中没有这样的共享变量及相关操作。
原理说明:volatile 关键字保证可见性
volatile 关键字确保了共享变量 i 在多个线程之间的可见性。当一个线程修改了 i 的值,其他线程能够立即看到这个修改。这样,线程能够根据 i 的最新值来正确判断是否进入同步代码块获取锁,保证了整个机制的正确性。如果没有 volatile 关键字,线程可能会使用 i 的旧值,导致程序逻辑错误,无法有效避免死锁。
线程执行条件判断
在修改后的 DeadLock 类中,每个线程内部有一个 while(true)循环,并且根据共享变量 i 的奇偶性来决定是否获取锁。当 i 为奇数时,第一个线程尝试获取锁;当 i 为偶数时,第二个线程尝试获取锁。而修改前 DeadLock 类中,两个线程启动后直接尝试获取锁,没有基于共享变量的条件判断。
原理说明:打破循环等待条件
在修改前 DeadLock 中,线程 1 先获取 lockOne 锁,然后等待 lockTwo 锁;而线程 2 先获取 lockTwo 锁,然后等待 lockOne 锁,形成了循环等待,导致死锁。
在修改后 DeadLock 中,通过引入共享变量 i 和条件判断,使得两个线程不会同时去竞争 lockOne 和 lockTwo 锁。例如,当 i 为奇数时,只有线程 1 会去尝试获取 lockOne 锁,线程 2 此时不会竞争 lockOne 锁,从而打破了循环等待的条件。当线程 1 获取锁并执行完同步代码块后,i 变为偶数,此时线程 2 才有可能去获取 lockOne 锁,避免了死锁的发生。
回到终端,先停止程序:ctrl + z ,输入“vim DeadLock.java”,回车进入,点击“i”进入编辑模式。
修改后代码如下:
代码变动部分请参考下图,代码修改完毕后,键盘点击“esc”,然后输入“:wq”保存退出。

编译 java 文件,然后执行代码:
可以看到执行成功。运行效果如下:

回到浏览器,点击 DeadLock 的“在线分析”。

查看优化后程序,调整两个线程持有锁的顺序后,程序不再发生死锁。

可以看到建议中数量少了一条 CPU 死锁已经不存在,问题已被解决。点击“停止分析”,终端停止代码。

总结:Java 中的死锁问题一旦发生很难定位具体的代码位置,因为程序干扰因素比较多,所以涉及加锁解锁代码逻辑地方一定要仔细。建议如果涉及加锁的代码逻辑,程序是通过新起线程去执行或者是在线程池中执行,一定给线程设置有特定业务逻辑的名称,一旦发生问题也好定位。
在进行其他程序调优时,需要根据鲲鹏 DevKit Java 性能分析工具采集的实际结果和对应的优化建议进行调优操作。具体的调优思路可以参考本次实践。
至此,利用鲲鹏 DevKit 剖析 Java 死锁问题的性能分析实操全部结束。
评论