JVM 如何使用性能分析工具定位代码中的性能问题?

核心思想: 通过工具观察程序在特定负载下的运行状态,识别消耗资源最多的代码段(热点代码)、异常的内存分配模式或线程阻塞情况,然后针对性的优化代码。

通用步骤:

  1. 确定问题: 首先明确遇到了什么性能问题,例如:CPU 使用率过高、内存持续增长最终 OOM、响应时间突然变慢、吞吐量下降等。
  2. 选择工具: 根据问题的类型和你的环境选择合适的性能分析工具。
  3. 连接或启动分析: 将工具连接到目标 JVM 进程,或者启动带有特定分析功能的 JVM。
  4. 施加负载: 模拟实际的用户负载,让问题得以重现。
  5. 收集数据: 在负载持续期间,使用工具收集性能数据(CPU 采样、内存快照、线程转储等)。
  6. 分析数据: 使用工具提供的分析功能,解读收集到的数据。
  7. 定位代码: 根据分析结果,确定具体是哪个类、哪个方法、哪行代码导致了性能问题。
  8. 优化代码: 针对定位到的问题代码进行优化。
  9. 验证效果: 重新运行负载测试,验证优化是否解决了问题并达到了性能目标。

常用工具及其定位代码问题的方法:

  1. jstack (线程堆栈分析)

    • 作用: 获取 JVM 中所有线程的堆栈信息。主要用于分析线程阻塞、死锁、以及线程在做什么(例如是否在等待 I/O、等待锁)。
    • 定位代码问题:
      • 高 CPU 但堆栈显示大量 WAITING/TIMED_WAITING: 可能线程在等待某个条件或锁,但等待时间过长。查看这些线程的堆栈,能看到它们在哪个方法、哪个锁上等待。
      • 大量 BLOCKED 状态线程: 表明存在严重的锁竞争。查看这些 BLOCKED 线程的堆栈,以及它们试图获取的锁(“waiting for monitor…”),再看拥有这个锁的线程(“owned by…”)在做什么。这能帮助我们定位竞争锁的同步代码块。
      • 死锁: jstack 会在最后输出明确报告,可以帮助我们发现死锁,并列出涉及的线程和锁。
      • 线程长时间停留在某个方法: 如果看到很多线程的堆栈顶部停留在某个特定方法,并且状态不是 BLOCKED/WAITING,可能这个方法本身执行缓慢。
    • 使用方法: jstack <pid> (pid 是 Java 进程 ID)。可以多次采集(例如每隔几秒采集一次),以便观察线程状态的变化。
  2. jmap (内存分析 - 堆转储)

    • 作用: 生成 JVM 堆内存的快照(Heap Dump),或者打印堆内存的统计信息。主要用于分析内存使用情况、查找内存泄漏。
    • 定位代码问题:
      • 内存泄漏: 生成堆转储文件 (jmap -dump:format=b,file=heap.bin <pid>) 后,使用 Eclipse MAT (Memory Analyzer Tool) 或 VisualVM 的 HeapWalker 打开分析。这些工具可以计算对象的“保留大小”(即该对象被垃圾回收后能释放的总内存),列出按保留大小排序的对象,找出最大的对象集合。通过分析对象的引用链(Paths To GC Roots),可以找到为什么这些对象没有被回收,通常能定位到是哪个类、哪个静态变量、哪个集合等持有了不必要的引用。
      • 对象创建率过高/大对象: 分析堆转储也能看到各种对象的实例数量和总大小。如果某个类的对象数量异常庞大,或者有些对象占用了大量内存,我们需要检查创建这些对象的代码。虽然 jmap 本身不提供创建时机的追踪,但结合代码逻辑分析堆内容,可以回溯到创建点。
    • 使用方法: jmap -dump:format=b,file=/path/to/heap.hprof <pid>。分析 .hprof 文件通常需要专业的 GUI 工具。
  3. jstat (JVM 统计监控)

    • 作用: 监控 JVM 的各种运行时统计信息,如 GC 情况、堆内存使用、类加载等。
    • 定位代码问题:
      • GC 频繁或停顿长: jstat -gc <pid> <interval> <count> 可以实时输出 Young GC (YGC) 和 Full GC (FGC) 的次数和耗时。如果 YGC 次数非常多,或者 FGC 频繁且耗时很长,说明内存分配和回收是瓶颈。这本身不直接指向代码,但它指示我们需要关注代码中的对象创建行为和内存使用模式。频繁的 YGC 可能意味着新生代太小或对象创建速度太快;频繁 FGC 可能意味着老年代满得快,需要检查是否有大量对象晋升或存在内存泄漏。
    • 使用方法: jstat -gc <pid> 2s 10 (每 2 秒输出一次,共 10 次)。
  4. VisualVM (集成工具)

    • 作用: 一个免费的、集成的可视化工具,可以监控 CPU、内存、线程,并进行 CPU 和内存分析(Profiling)。支持插件扩展。
    • 定位代码问题:
      • CPU 性能分析 (Profiler -> CPU): 这是 VisualVM 定位 CPU 热点代码的主要功能。它可以通过采样(Sampling)或插桩(Instrumentation)两种方式记录方法调用的耗时。运行 CPU Profiler 一段时间后,它会列出消耗 CPU 时间最多的方法列表(按百分比排序)。点击具体方法,可以查看它的调用者(Callers)和被调用者(Callees),以及完整的调用树(Call Tree)。通过分析调用树,可以精确地找到是哪个方法(及其调用路径)占用了大量的 CPU 时间。
      • 内存性能分析 (Profiler -> Memory): 可以记录一段时间内的对象创建情况,显示哪些类创建的对象最多,以及它们占用的内存。结合 Heap Dump 功能(Monitor -> Heap Dump),进行内存泄漏分析(与 MAT 功能类似,查找大对象和引用链)。
      • 线程分析 (Threads): 提供实时的线程状态视图,可以方便地看到 BLOCKED, WAITING, RUNNABLE 状态的线程数量,并可以一键生成线程转储进行分析(类似于 jstack,但可视化)。
    • 使用方法: 启动 VisualVM,连接到本地或远程的 Java 进程。
  5. JMC (Java Mission Control) / JFR (Java Flight Recorder)

    • 作用: Oracle 官方推荐的强大工具集。JFR 以极低的开销收集 JVM 和应用程序的事件数据(包括 GC、线程活动、I/O、锁、JIT 编译、方法执行等)。JMC 用于打开并分析 JFR 记录文件。
    • 定位代码问题:
      • CPU 热点: JFR 记录的方法采样事件能精确地展示哪些方法在 CPU 上运行时间最长,JMC 提供火焰图(Flame Graph)或树状图等多种视图来分析 CPU 采样数据,非常直观地找到热点方法及其调用链。
      • 锁竞争: JFR 会记录线程等待锁的事件,JMC 的 Lock Analysis 视图能清晰地显示哪些锁竞争最激烈,哪些线程等待时间最长,以及发生在哪个类的哪个方法中。
      • I/O 瓶颈: JFR 记录文件 I/O、Socket I/O 等事件,可以定位代码中低效的 I/O 操作。
      • GC 瓶颈: JFR 详细记录 GC 事件,JMC 提供丰富的 GC 分析视图,结合 CPU 使用率分析,能确定 GC 是否是导致高 CPU 的原因,以及哪些代码行为(如大量对象创建)导致了 GC 压力。
      • 异常与错误: JFR 记录异常抛出事件,能帮你找到代码中频繁发生异常的位置(即使异常被捕获)。
    • 使用方法:
      • 启动 JFR recording: 使用 jcmd <pid> JFR.start ... 或在 JVM 启动参数中设置 -XX:+UnlockCommercialFeatures -XX:+FlightRecorder (对于旧版本 Oracle JDK) 或 -XX:StartFlightRecording=... (对于 OpenJDK)。
      • 生成 JFR 文件: 使用 jcmd <pid> JFR.dump ... 或在 recording 结束时自动生成。
      • 分析: 启动 JMC,打开生成的 .jfr 文件进行分析。
  6. Async-Profiler

    • 作用: 一个采样式的低开销性能分析工具,支持分析 CPU、堆分配、锁竞争、I/O 等。可以直接 attach 到正在运行的 JVM。
    • 定位代码问题: 提供火焰图、树状图等多种输出格式,能快速准确地定位 CPU 热点、高内存分配点、锁竞争发生的代码位置,对 C/C++ 代码和 JVM 内部活动也有很好的支持。
    • 使用方法: 作为一个 native 库加载或通过 async-profiler.sh 脚本运行。例如 async-profiler.sh start <pid> -e cpu -f profile.html

定位代码问题时的技巧:

  • 结合多种工具: 通常不会只使用一个工具。例如,先用 jstat 或 VisualVM 监控 GC 和 CPU 趋势,发现问题后,用 jstack 分析线程状态,用 VisualVM 或 JMC/JFR 进行 CPU/Memory Profiling,如果怀疑内存泄漏则使用 jmap/MAT 分析堆转储。
  • 关注热点: 性能分析工具通常会列出消耗资源最多的前 N 个方法或类。优先分析这些“热点”。
  • 分析调用树/引用链: 不要只看单个方法消耗的时间或单个类占用的内存,更重要的是理解它是如何被调用的(调用树)或为什么没有被回收(引用链)。这能帮助你找到问题的源头。
  • 多点采样: 对于线程分析 (jstack) 或某些 Profiling 工具,采集单次数据可能不够,需要间隔一定时间多次采集,观察状态的变化。
  • 理解工具的工作原理: 知道工具是采样式还是插桩式,以及其开销,有助于更准确地使用和解读结果。
  • 联系代码逻辑: 分析结果最终需要回到代码层面。结合应用的业务逻辑和代码实现,我们要理解为什么这段代码会成为瓶颈。
  • 环境一致性: 尽量在与生产环境相似的环境中进行性能分析。

熟练使用这些工具,并结合对 JVM 运行时原理和自身代码逻辑的理解,就能有效地定位并解决 Java 应用程序的性能问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79142.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python虚假新闻检测识别

程序示例精选 Python虚假新闻检测识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《Python虚假新闻检测识别》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应…

网络原理 - 12(HTTP/HTTPS - 3 - 响应)

目录 认识“状态码”&#xff08;status code&#xff09; 200 OK 404 Not Found 403 Forbidden 405 Method Not Allowed 500 Internal Server Error 504 Gateway Timeout 302 Move temporarily 301 Moved Permanently 418 I am a teaport 状态码小结&#xff1a; …

Spring Boot中集成Guava Cache或者Caffeine

一、在Spring Boot(1.x版本)中集成Guava Cache 注意&#xff1a; Spring Boot 2.x用户&#xff1a;优先使用Caffeine&#xff0c;性能更优且维护活跃。 1. 添加依赖 在pom.xml中添加Guava依赖&#xff1a; <dependency><groupId>com.google.guava</groupId&…

Linux工作台文件操作命令全流程解析

全文目录 1 确认当前工作路径2 导航与目录管理2.1 关键命令2.2 逻辑衔接 3 文件基础操作3.1 创建 → 备份 → 重命名 → 清理3.2 文件查看和编辑3.3 文件链接3.4 文件diff 4 文件权限与所有权管理5 文件打包与归档6 参考文献 写在前面 shell是一种命令解释器&#xff0c;它提供…

LeetCode第183题_从不订购的客户

LeetCode 第183题&#xff1a;从不订购的客户 题目描述 表: Customers ---------------------- | Column Name | Type | ---------------------- | id | int | | name | varchar | ---------------------- id 是该表的主键。 该表包含消费者的 id 和…

c语言的常用关键字

c语言的常用关键字 c语言的关键字表示数据类型的关键字autocharfloatdoubleintlongshortvoidsignedstruct、enum、unionunsigned 表示分支语句的关键字ifelseswitchbreakcasecontinuedefault 表示循环语句的关键字whiledoforgoto 用于修饰变量或函数的关键字constconst修饰变量…

MCU通用输入输出端口(GPIO)设计指南

在嵌入式系统开发中&#xff0c;MCU的GPIO接口是一个基础但非常实用的功能模块。GPIO全称是通用输入输出端口&#xff0c;它让MCU可以灵活地与外部设备进行交互。 GPIO的主要特点包括&#xff1a; 多功能性&#xff1a;每个引脚都可以单独配置为输入或输出 可编程性&#xff…

STM32完整内存地址空间分配详解

在STM32这类基于ARM Cortex-M的32位微控制器中&#xff0c;整个4GB的地址空间(从0x00000000到0xFFFFFFFF)有着非常系统化的分配方案&#xff0c;每个区域都有其特定的用途。下面我将详细介绍这些地址区域的分配及其功能&#xff1a; STM32完整内存地址空间分配详解(0x00000000…

实现水平垂直居中的多种方法

在前端开发中&#xff0c;元素的居中是一个常见但又经常让人头疼的问题。本文将全面总结各种CSS居中方法&#xff0c;特别是如何实现一个div的水平垂直居中。 为什么居中这么重要&#xff1f; 居中布局是现代网页设计中最基础也最重要的布局方式之一。无论是导航菜单、登录框…

如何实现服务的自动扩缩容(Auto Scaling)

在云计算和分布式系统的时代,系统的弹性和适应性已成为企业构建高效IT基础设施的核心需求。自动扩缩容(Auto Scaling)作为一种关键技术,旨在根据实时负载变化动态调整计算资源,以确保系统性能稳定,同时优化资源利用效率。简单来说,自动扩缩容是指系统能够根据预设规则或…

uniapp+vue3+ts 使用canvas实现安卓端、ios端及微信小程序端二维码生成及下载

加粗样式uniapp多端生成带二维码海报并保存至相册的实现 在微信小程序开发中&#xff0c;我们常常会遇到生成带有二维码的海报并保存到手机相册的需求&#xff0c;比如分享活动海报、产品宣传海报等。今天就来和大家分享一下如何通过代码实现这一功能。 准备工作 在开始之前&am…

架构师面试(三十八):注册中心架构模式

题目 在微服务系统中&#xff0c;当服务达到一定数量时&#xff0c;通常需要引入【注册中心】组件&#xff0c;以方便服务发现。 大家有没有思考过&#xff0c;注册中心存在的最根本的原因是什么呢&#xff1f;注册中心在企业中的最佳实践是怎样的&#xff1f;注册中心的服务…

Day.js和Moment.js对比,日期时间库怎么选?

在JavaScript的日期处理库中&#xff0c;Moment.js 和 Day.js 是两个非常流行的选择。本文将基于从npmtrends的数据&#xff0c;对这两个库进行详细的对比分析。 Moment.js的重度使用者。凡是遇到时间和日期的操作&#xff0c;就把Moment.js引用上。 直到有天我发现加载的mome…

罗默如何用木星卫星“宇宙钟表”测量光速?

一、17世纪的“宇宙级实验” 1676年&#xff0c;丹麦天文学家奥勒罗默&#xff08;Ole Rmer&#xff09;在巴黎天文台做出惊人发现&#xff1a; 木星卫星的“迟到早退”现象&#xff0c;竟能揭示光速的秘密&#xff01; 通过观察木卫一&#xff08;Io&#xff09;的轨道周期变…

deepseek 技巧整理

1、导出word 和excel 功能&#xff0c;在使用以下提示词。 请帮我列出减肥期间可以吃的水果&#xff0c;并分析该水果含有的营养元素&#xff0c;以表格的形式星现。1.要以html的方式输出 2.要可以直接运行 3.页面要提供可以直接下载word和excel功能

思考软件框架

数据库是达梦数据库 假定里面有40张表&#xff0c;软件的业务逻辑比较复杂。 当然&#xff0c;依然是对数据库中数据的增&#xff0c;删&#xff0c;改&#xff0c;查&#xff0c;组合&#xff0c;显示。 但是也涉及到多种软件&#xff0c;多台设备之间的通信。 我们可以使用…

探索 Disruptor:高性能并发框架的奥秘

在当今的软件开发领域&#xff0c;处理高并发场景是一项极具挑战性的任务。传统的并发解决方案&#xff0c;如基于锁的队列&#xff0c;往往在高负载下表现出性能瓶颈。而 Disruptor 作为一个高性能的并发框架&#xff0c;凭借其独特的设计和先进的技术&#xff0c;在处理海量数…

前端面经-VUE3篇--vue3基础知识(一)插值表达式、ref、reactive

一、计算属性(computed) 计算属性&#xff08;Computed Properties&#xff09;是 Vue 中一种特殊的响应式数据&#xff0c;它能基于已有的响应式数据动态计算出新的数据。 计算属性有以下特性&#xff1a; 自动缓存&#xff1a;只有当它依赖的响应式数据发生变化时&#xff…

数据结构6 · BinaryTree二叉树模板

代码函数功能顺序如下&#xff1a; 1&#xff1a;destroy&#xff1a;递归删除树 2&#xff1a;copy&#xff1a;复制二叉树 3&#xff1a;preOrder&#xff1a;递归前序遍历 4&#xff1a;inOrder&#xff1a;递归中序遍历 5&#xff1a;postOrder&#xff1a;递归后续遍…

C++/SDL进阶游戏开发 —— 双人塔防游戏(代号:村庄保卫战 13)

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 十…