云峰服务器 1 概述 1.1 背景介绍 鲲鹏DevKit Java性能分析工具是一款针对鲲鹏平台的性能分析和调优工具,Java性能分析工具是针对基于鲲鹏的服务器上运行的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,打开命令终端窗口。
执行自动部署命令如下:
复制
hcd deploy--password 远端服务器密码 --time 3600--password 待部署项目所在ECS的root用户密码(至少8个字符)--time value 待部署资源的保留期(单位为秒,至少600秒,默认600秒)。当前案例预估需要40分钟,可以配置time为1小时保留期。该命令会自动部署鲲鹏服务器。首次部署会直接执行,旧资源未到期时重复部署,会提示是否删除前面创建的资源,可以删除旧资源再次部署。
可以看到鲲鹏服务器链接:https://113.44.69.106:8084,表示部署成功,记录部署远端服务器公网IP,如截图中对应的就是:113.44.69.106 。
在开发者空间,我的开发资源下,找到鲲鹏沙箱环境,点击使用详情,可以看到我们刚刚自动部署的鲲鹏服务器,创建时间及资源释放时间等信息。
2.2 安装鲲鹏Devkit插件
云主机桌面单击鼠标右键,在菜单中选择Open Terminal Here打开终端。
通过ssh连接云服务器,如果有yes/no选择输入yes,然后输入 云主机密码,出现Welcome to XXX代表连接成功。
复制
sshroot@云主机IP输入地址wget https://kunpeng-repoXXX下载鲲鹏DevKit压缩包:
复制
wgethttps://kunpeng-repo.obs.cn-north-4.myhuaweicloud.com/Kunpeng%20DevKit/Kunpeng%20DevKit%2024.0.RC3/DevKit-All-24.0.RC3-Linux-Kunpeng.tar.gz稍作等待,下载完成执行ls可以看到下载的压缩包。
解压文件.
复制
tar-zvxfDevKit-All-24.0.RC3-Linux-Kunpeng.tar.gz进入文件夹,可以看到install.sh,执行安装。
复制
cdDevKit-All-24.0.RC3-Linux-Kunpengsudo./install.sh如下图步骤,第一个默认回车即可。
出现如下图所示时选择2,去选择插件(1是安装全部,如选错可以ctrl + c停止,重复上面 sudo ./install.sh指令即可)。
本案例只用到java性能分析,选择6,后面选项根据提示操作,多数默认回车即可,直至安装成功。
安装成功可以看到https://192.168.2.191:8086 。
执行命令,修改端口。
如下命令的第一个ip为2.1自动部署的鲲鹏服务器IP,第二个ip为上图安装成功的IP。
复制
iptables-tnat-APREROUTING-dXX.XX.XX.XX-ptcp--dport8086-jDNAT--to-destinationXX.XX.XX.XX:80862.3 配置在线分析环境
免费云服务器套餐
通过浏览器访问鲲鹏服务器,添加目标节点,以配置在线分析环境。
打开浏览器,输入https://XX.XX.XX.XX:8086(IP为2.1自动部署的鲲鹏服务器 IP),如果提示风险,点击接受并继续。
创建密码,并登录。
登录完成后添加目标环境:点击左上角调优 添加,在添加目标环境弹窗中输入:用户名,云服务器密码,点击确定;跳出来弹窗点击确定即可。
2.4 执行编译代码
云主机桌面单击鼠标右键,在菜单中选择Open Terminal Here打开终端。
通过ssh连接云服务器,如果有yes/no选择输入yes,然后输入 云主机密码,出现Welcome to XXX代表连接成功。
复制
sshroot@云主机IP进入到云主机后,执行如下命令查看当前云主机的JDK安装情况。
复制
java-version可以看到JDK默认是安装配置好的,输入vim DeadLock.java,回车进入,点击i进入编辑模式。
复制
云服务器都能干嘛
vimDeadLock.javaDeadLock.java 代码地址,如跳转不了请输入以下地址复制代码:https://github.com/kunpengcompute/devkitdemo/blob/main/Hyper_tuner/testdemo/基于java性能分析工具的死锁调优实践/DeadLock.java
DeadLock.java代码如下(因格式可能存在问题建议进入链接进行复制):
复制
publicclassDeadLock{privatestaticfinal Integer lockOne =newInteger(1);privatestaticfinal Integer lockTwo =newInteger(2);publicstaticvoidmain(String[] args){newThread(()->{try{ System.out.println("thread1 is running"); synchronized (lockOne){ System.out.println("thread1 get lock obj1"); Thread.sleep(1000L); synchronized (lockTwo){ System.out.println("thread12 get lock obj2"); Thread.sleep(1000L); } } }catch(InterruptedException e){ e.printStackTrace(); } }).start();newThread(()->{try{ System.out.println("thread2 is running"); synchronized (lockTwo){ System.out.println("thread2 get lock obj2"); Thread.sleep(1000L); synchronized (lockOne){ System.out.println("thread2 get lock obj1"); Thread.sleep(1000L); } } }catch(InterruptedException e){ e.printStackTrace(); } }).start(); } }代码复制完毕,键盘点击esc,然后输入:wq保存退出。
复制
javacDeadLock.javajavaDeadLock编译java文件,然后执行代码,可以看到执行成功。
2.5 死锁在线分析
进入浏览器界面,点击调优,点击root@XXX,点击DeadLock的在线分析。
对程序进行在线分析,在概览页签下观察每种状态的线程数,发现有两个线程处于阻塞状态,有死锁的嫌疑。
切换到CPU页签下的线程列表,找到阻塞状态的线程。观察一段时间发现发两个线程一直处于阻塞状态。
执行多次线程转储操作:在CPU下,点击执行线程转储,转储成功后点击线程转储下的锁分析图。发现两个阻塞中的线程发生了死锁。
选择CPU页签中线程转储下的原始数据,根据线程转储的原始数据得到死锁的相关信息。
点击优化建议的查看详情,查看死锁问题优化建议。
此时,我们已经获取到阻塞的原因及优化建议,点击停止分析。
2.6 修改代码,再次编译分析
找到阻塞的原因,接下来参考优化建议进行代码修改,解决死锁问题。
优化点解析说明:
共享变量的使用及操作修改后的DeadLock类中定义了一个volatile修饰的共享变量i,并且在每个线程获取锁成功并执行完同步代码块后,会对i进行自增操作(i++)。而修改前的DeadLock类中没有这样的共享变量及相关操作。
复制
privatestaticvolatileInteger i =1;原理说明:volatile关键字保证可见性
volatile关键字确保了共享变量i在多个线程之间的可见性。当一个线程修改了i的值,其他线程能够立即看到这个修改。这样,线程能够根据i的最新值来正确判断是否进入同步代码块获取锁,保证了整个机制的正确性。如果没有volatile关键字,线程可能会使用i的旧值,导致程序逻辑错误,无法有效避免死锁。
线程执行条件判断在修改后的DeadLock类中,每个线程内部有一个while(true)循环,并且根据共享变量i的奇偶性来决定是否获取锁。当i为奇数时,第一个线程尝试获取锁;当i为偶数时,第二个线程尝试获取锁。而修改前DeadLock类中,两个线程启动后直接尝试获取锁,没有基于共享变量的条件判断。
复制
while(true){if(i %2==1){synchronized(lockOne){System.out.println("thread1 get lock obj1");Thread.sleep(1000L);synchronized(lockTwo){System.out.println("thread12 get lock obj2");Thread.sleep(1000L); } }i++; } }while(true){if(i %2==0){synchronized(lockOne){System.out.println("thread1 get lock obj1");Thread.sleep(1000L);synchronized(lockTwo){System.out.println("thread12 get lock obj2");Thread.sleep(1000L); } }i++; } }原理说明:打破循环等待条件
在修改前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进入编辑模式。
复制
vimDeadLock.java修改后代码如下:
复制
publicclassDeadLock{privatestaticfinal Integer lockOne =newInteger(1);privatestaticfinal Integer lockTwo =newInteger(2);privatestaticvolatileInteger i =1;publicstaticvoidmain(String[] args){newThread(()->{try{ System.out.println("thread1 is running");while(true){if(i %2==1){ synchronized (lockOne){ System.out.println("thread1 get lock obj1"); Thread.sleep(1000L); synchronized (lockTwo){ System.out.println("thread12 get lock obj2"); Thread.sleep(1000L); } } i++; } } }catch(InterruptedException e){ e.printStackTrace(); } }).start();newThread(()->{try{ System.out.println("thread2 is running");while(true){if(i %2==0){ synchronized (lockOne){ System.out.println("thread1 get lock obj1"); Thread.sleep(1000L); synchronized (lockTwo){ System.out.println("thread12 get lock obj2"); Thread.sleep(1000L); } } i++; } } }catch(InterruptedException e){ e.printStackTrace(); } }).start(); } }代码变动部分请参考下图,代码修改完毕后,键盘点击esc,然后输入:wq保存退出。
编译java文件,然后执行代码:
复制
javacDeadLock.javajavaDeadLock可以看到执行成功。运行效果如下:
回到浏览器,点击DeadLock 的在线分析。
查看优化后程序,调整两个线程持有锁的顺序后,程序不再发生死锁。
可以看到建议中数量少了一条CPU死锁已经不存在,问题已被解决。点击停止分析,终端停止代码。
总结:Java中的死锁问题一旦发生很难定位具体的代码位置,因为程序干扰因素比较多,所以涉及加锁解锁代码逻辑地方一定要仔细。建议如果涉及加锁的代码逻辑,程序是通过新起线程去执行或者是在线程池中执行,一定给线程设置有特定业务逻辑的名称,一旦发生问题也好定位。
在进行其他程序调优时,需要根据鲲鹏DevKit Java性能分析工具采集的实际结果和对应的优化建议进行调优操作。具体的调优思路可以参考本次实践。
至此,利用鲲鹏DevKit剖析Java死锁问题的性能分析实操全部结束。
云服务器地址是什么意思

发表评论
最近发表
标签列表