JVM实战之性能调优[2](线程转储案例认识和分析)

2024-03-29 10:04

本文主要是介绍JVM实战之性能调优[2](线程转储案例认识和分析),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 版权声明
  • 案例1:CPU占用率高问题
    • 问题描述
    • 解决思路
    • 补充内容
  • 案例2:接口响应时间长问题
    • 问题描述
    • 解决思路
    • Arthas trace命令
    • Arthas watch命令
    • 解决问题
  • 案例3:定位偏底层性能问题
    • 问题描述
    • 解决思路:Arthas火焰图
    • 问题解决
  • 案例4:线程被耗尽问题
    • 问题描述
    • 解决思路
    • 问题解决
    • 死锁代码优化

版权声明

  • 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利人所有。本博客的目的仅为个人学习和交流之用,并非商业用途。
  • 我在整理学习笔记的过程中尽力确保准确性,但无法保证内容的完整性和时效性。本博客的内容可能会随着时间的推移而过时或需要更新。
  • 若您是黑马程序员或相关权利人,如有任何侵犯版权的地方,请您及时联系我,我将立即予以删除或进行必要的修改。
  • 对于其他读者,请在阅读本博客内容时保持遵守相关法律法规和道德准则,谨慎参考,并自行承担因此产生的风险和责任。
  • 本博客中的部分观点和意见仅代表我个人,不代表黑马程序员的立场。
  • 由于作者精力有限,有关代码的演示和工具的使用和操作,请食用官方的B站JVM教程视频

案例1:CPU占用率高问题

问题描述

  • 监控人员通过prometheus的告警发现CPU占用率一直处于很高的情况,通过top命令看到是由于Java程序引起的,希望能快速定位导致性能问题的代码。

解决思路

  1. 通过top –c 命令找到CPU占用率高的进程,获取进程ID
    在这里插入图片描述
  2. 使用top -p 进程ID单独监控某个进程,按 H H H查看到所有的线程以及线程对应的CPU使用率,找到CPU使用率特别高的线程,并记录线程ID。
    在这里插入图片描述
  3. 使用 jstack 进程ID 命令可以查看到所有线程正在执行的栈信息。使用 jstack 进程ID > 文件名 保存到文件中方便查看。
    在这里插入图片描述
  4. 找到 n i d 线程 I D nid线程ID nid线程ID相同的栈信息,需要将之前记录下的十进制线程号转换成16进制。通过 p r i n t f ′ printf '%x\n' printf 线程ID 命令直接获得16进制下的线程ID
    在这里插入图片描述
  5. 找到栈信息对应的源代码,并分析问题产生原因

补充内容

  • 在定位CPU占用率高的问题时,需要关注的是状态为 R U N N A B L E RUNNABLE RUNNABLE的线程。但实际上,有一些线程执行本地方法时并不会消耗CPU,而只是在等待。但 JVM 仍然会将它们标识成“RUNNABLE”状态。
    在这里插入图片描述

案例2:接口响应时间长问题

问题描述

  • 程序运行过程中,发现有几个接口的响应时间特别长,需要快速定位执行过程中出现性能问题的代码。

解决思路

  • 确定出现性能问题的方法,借助于arthas定位到具体的方法(在方法嵌套比较深的情况下)
    在这里插入图片描述

Arthas trace命令

  • 使用arthas的trace命令,可以展示出整个方法的调用路径以及每一个方法的执行耗时。
  • 语法格式
    trace 类名 方法名
    

⚫ 添加--skipJDKMethod false 参数可以输出JDK核心包中的方法及耗时。
⚫ 添加 #cost > 毫秒值 参数,只会显示耗时超过该毫秒值的调用。
⚫ 添加 –n 数值 参数,最多显示该数值条数的数据。
⚫ 所有监控都结束之后,输入 s t o p stop stop结束监控,重置arthas增强的对象
在这里插入图片描述

Arthas watch命令

  • 使用trace定位到性能较低的方法后,使用watch命令监控该方法,可以获得更为详细的方法信息。
  • 语法格式
    watch 类名 方法名 ‘{params, returnObj}’ ‘#cost>毫秒值' -x 2
    
  • {params, returnObj} 代表打印参数和返回值。
  • -x 代表打印的结果中如果有嵌套(比如对象里有属性),最多只展开2层。允许设置的最大值为4。
    在这里插入图片描述

解决问题

  1. 通过arthas的trace命令,首先找到性能较差的具体方法,如果访问量比较大,建议设置最小的耗时,精确的找到耗时比较高的调用。
  2. 通过watch命令,查看此调用的参数和返回值,关注参数,在开发环境或者测试环境模拟现象,通过debug找到具体的问题根源。
  3. 使用stop命令将所有增强的对象恢复。

案例3:定位偏底层性能问题

问题描述

  • 接口中使用for循环向ArrayList中添加数据,但是最终发现执行时间比较长,需要定位是导致的性能低下的原因
@GetMapping("/profile1")
public void test6() throws InterruptedException {ArrayList<Integer> objects = new ArrayList<>();for (Integer i = 0; i < 20000000; i++) {objects.add(i);}
}

解决思路:Arthas火焰图

  • 使用Arthas提供性能火焰图的功能,查看方法的执行耗时
  • 使用arthas的profile命令,生成性能监控的火焰图
    profiler start #1 开始监控方法执行性能
    profiler stop --format html #2 以HTML的方式生成火焰图
    
  • 火焰图中一般找绿色部分Java中栈顶上比较平的部分,很可能就是性能的瓶颈
    在这里插入图片描述
  • 偏底层的性能问题,特别是由于JDK中某些方法被大量调用导致的性能低下,可以使用火焰图非常直观的找到原因

问题解决

  • 案例中是由于创建ArrayList时没有手动指定容量,导致使用默认的容量而在添加对象过程中发生了多次的扩容,扩容需要将原来数组中的元素复制到新的数组中,消耗了大量的时间。
  • 优化后的代码
@GetMapping("/profile2")public void test7() throws InterruptedException {ArrayList<Integer> objects = new ArrayList<>(20000000);for (Integer i = 0; i < 20000000; i++) {objects.add(i);}}

案例4:线程被耗尽问题

问题描述

  • 程序在启动运行一段时间之后,就无法接受任何请求。将程序重启之后继续运行,依然会出现相同的情况。

解决思路

  • 线程耗尽问题,一般是由于执行时间过长,分析方法分成两步:
    1. 检测是否有死锁产生,无法自动解除的死锁会将线程永远阻塞。
    2. 如果没有死锁,再使用案例1的打印线程栈的方法检测线程正在执行的方法,一般大量出现的方法就是慢方法。
  • 死锁:两个或以上的线程因为争夺资源而造成互相等待的现象

问题解决

线程死锁可以通过三种方法定位问题:

  1. jstack -l 进程ID > 文件名将线程栈保存到本地,在文件中搜索deadlock即可找到死锁位置
    在这里插入图片描述
  2. 开发环境中使用visual vm或者Jconsole工具,检测死锁。使用线程快照生成工具查看死锁的根源。生产环境的服务一般不会允许使用这两种工具连接。
    在这里插入图片描述
  3. 使用fastthread自动检测线程问题(Fastthread,是一款在线的AI自动线程问题检测工具,可以提供线程分析报告。通过报告查看是否存在死锁问题。)
    在这里插入图片描述

死锁代码优化

  • 问题代码
private Object obj1 = new Object();
private Object obj2 = new Object();
@GetMapping("/deadlock1")
public String test1() throws InterruptedException {synchronized (obj1){Thread.sleep(5000);synchronized (obj2){return "返回成功";}}
@GetMapping("/deadlock2")
public String test2() throws InterruptedException {synchronized (obj2){Thread.sleep(5000);synchronized (obj1){return "返回成功";}}    
  • 优化代码
private Object obj1 = new Object();
private Object obj2 = new Object();
private Lock lock1 = new ReentrantLock();
private Lock lock2 = new ReentrantLock();@GetMapping("/deadlock1")
public String test1() throws InterruptedException {boolean b1 = lock1.tryLock(1, TimeUnit.SECONDS);if(b1){try {Thread.sleep(5000);boolean b2 = lock2.tryLock(1, TimeUnit.SECONDS);if(b2){try{return "返回成功";}finally {lock2.unlock();}}}finally {lock1.unlock();}}return "处理失败";
}@GetMapping("/deadlock2")
public String test2() throws InterruptedException {boolean b1 = lock2.tryLock(1, TimeUnit.SECONDS);if(b1){try {Thread.sleep(5000);boolean b2 = lock1.tryLock(1, TimeUnit.SECONDS);if(b2){try{return "返回成功";}finally {lock1.unlock();}}}finally {lock2.unlock();}}return "处理失败";
}

这篇关于JVM实战之性能调优[2](线程转储案例认识和分析)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/858299

相关文章

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

springboot项目中整合高德地图的实践

《springboot项目中整合高德地图的实践》:本文主要介绍springboot项目中整合高德地图的实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一:高德开放平台的使用二:创建数据库(我是用的是mysql)三:Springboot所需的依赖(根据你的需求再

spring中的ImportSelector接口示例详解

《spring中的ImportSelector接口示例详解》Spring的ImportSelector接口用于动态选择配置类,实现条件化和模块化配置,关键方法selectImports根据注解信息返回... 目录一、核心作用二、关键方法三、扩展功能四、使用示例五、工作原理六、应用场景七、自定义实现Impor

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.