版权声明

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

性能调优概述

  • JVM(Java虚拟机)性能调优是一个复杂但重要的过程,目的是提高Java应用程序的性能和效率JVM性能调优通常包括监控Java虚拟机的运行情况、识别性能瓶颈、调整JVM设置以及优化Java代码。
  • JVM性能调优是一个持续的过程,需要根据应用的实际运行情况和性能目标进行。不同的应用和工作负载可能需要不同的调优策略。最好是以实际的性能监控数据为基础,逐步迭代优化。

性能调优步骤

  • 性能优化的步骤总共分为四个步骤,其中修复部分要具体问题具体分析且处理方式各不相同。
    JVM实战之性能调优[1](概述和方法)-LMLPHP

性能常见问题

  • 应用程序在运行过程中经常会出现性能问题。
  1. CPU占用率高,接近100甚至多核CPU下超过100(通过top命令查看)
top - 07:44:12 up 14:39,  1 user,  load average: 0.54, 0.44, 0.24
Tasks: 162 total,   1 running, 161 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.4 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   3930.3 total,   2090.6 free,    711.9 used,   1127.9 buff/cache
MiB Swap:   1965.1 total,   1965.1 free,      0.0 used.   3064.8 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                                          
    870 root      20   0 1640364 152040  83080 S   1.3   3.8   8:46.52 1panel                                                                                                                           
   8378 root      20   0   12920   3244   2764 R   1.0   0.1   0:00.15 top                                                                                                                              
   6802 systemd+  20   0 2271612 397192  35692 S   0.7   9.9   0:02.89 mysqld                                                                                                                           
   4967 root      20   0       0      0      0 I   0.3   0.0   0:01.01 kworker/1:0-pm                                                                                                                   
   6086 root      20   0 1933576  39512  26624 S   0.3   1.0   0:00.64 containerd                                                                                                                       
   6258 root      20   0       0      0      0 I   0.3   0.0   0:00.06 kworker/u8:4-events_unbound                                                                                                      
   6391 root      20   0 3364472  77564  46688 S   0.3   1.9   0:01.46 dockerd                                                                                                                          
      1 root      20   0  166108  10348   7228 S   0.0   0.3   0:09.31 systemd                
  1. 请求单个服务处理时间特别长(多服务使用skywalking等监控系统来判断哪一个环节性能低下)
    JVM实战之性能调优[1](概述和方法)-LMLPHP
  2. 程序启动之后运行正常,但运行一段时间之后无法处理任何的请求(内存和GC正常,线程耗尽)

性能调优方法

线程转储概念

  • 线程转储(Thread Dump)提供对所有运行中的线程当前状态的快照。其中包含线程名、优先级、线程ID、线程状态、线程栈信息等等内容,可以用来解决CPU占用率高、死锁等问题。
  • 线程转储可以通过jstack、VisualVM、JMC(Java Mission Control)等工具获取。

线程转储方式

  1. 方式一:通过jstack命令。jstack是JDK自带的工具,用于生成Java应用程序的线程转储。使用时需要提供Java进程的PID(Process ID)
# -l参数是可选的,用于生成关于锁的额外信息
jstack -l <pid> #<pid>是你想要获取线程转储的Java进程的进程ID
# 保存线程转储内容到本地
jstack -l <pid> > /path/to/directory/threadDump.txt
  1. 方式二:使用VisualVM等工具获取
    • 使用VisualVM打开local,找到运行的java程序,打开线程中的Thread Dump即可查看到线程转储的内容,如果需要保存到本地,右键java程序选择Save as选择路径即可保存到本地。
      JVM实战之性能调优[1](概述和方法)-LMLPHP
      JVM实战之性能调优[1](概述和方法)-LMLPHP
      JVM实战之性能调优[1](概述和方法)-LMLPHP

线程转储查看

线程转储(Thread Dump)中个核心内容

  • 名称: 线程名称,通过给线程设置合适的名称更容易“见名知意”
  • 优先级(prio):线程的优先级
  • Java ID(tid):JVM中线程的唯一ID
  • 本地 ID (nid):操作系统分配给线程的唯一ID
  • 状态:线程的状态,分为:
    • NEW – 新创建的线程,尚未开始执行
    • RUNNABLE –正在运行或准备执行
    • BLOCKED – 等待获取监视器锁以进入或重新进入同步块/方法
    • WAITING – 等待其他线程执行特定操作,没有时间限制
    • TIMED_WAITING – 等待其他线程在指定时间内执行特定操作
    • TERMINATED – 已完成执行
  • 栈追踪: 显示整个方法的栈帧信息
  • 线程转储的内容格式,案例如下,请参照上面的概念进行阅读和学习:
2024-03-28 07:51:40
Full thread dump Java HotSpot(TM) 64-Bit Server VM (19.0.2+7-44 mixed mode, sharing):

Threads class SMR info:
_java_thread_list=0x000002429e005240, length=30, elements={
0x00000242fe44b570, 0x00000242fe44dac0, 0x00000242fe450390, 0x00000242fe4508d0,
0x00000242fe450e10, 0x00000242fe455360, 0x00000242fe466280, 0x00000242fe47ec20,
0x00000242fe4811b0, 0x000002429b09ed20, 0x000002429b0a2e40, 0x000002429b262960,
0x000002429b3fd8e0, 0x000002429c025f00, 0x000002429c151cc0, 0x000002429c1654e0,
0x000002429c16e290, 0x000002429c16e7e0, 0x000002429c16d2a0, 0x000002429c16ed30,
0x000002429c16d7f0, 0x000002429c16f280, 0x000002429c16bd60, 0x000002429c16cd50,
0x000002429e893370, 0x000002429e8918e0, 0x000002429e8938c0, 0x000002429e88fe50,
0x000002429e893e10, 0x000002429e8908f0
}

"Reference Handler" #8 [11560] daemon prio=10 os_prio=2 cpu=0.00ms elapsed=64.15s tid=0x00000242fe44b570 nid=11560 waiting on condition  [0x00000068e58ff000]
   java.lang.Thread.State: RUNNABLE
        at java.lang.ref.Reference.waitForReferencePendingList(java.base@19.0.2/Native Method)
        at java.lang.ref.Reference.processPendingReferences(java.base@19.0.2/Reference.java:245)
        at java.lang.ref.Reference$ReferenceHandler.run(java.base@19.0.2/Reference.java:207)

   Locked ownable synchronizers:
        - None

"Finalizer" #9 [9920] daemon prio=8 os_prio=1 cpu=0.00ms elapsed=64.15s tid=0x00000242fe44dac0 nid=9920 in Object.wait()  [0x00000068e59fe000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait0(java.base@19.0.2/Native Method)
        - waiting on <no object reference available>
        at java.lang.Object.wait(java.base@19.0.2/Object.java:366)
        at java.lang.Object.wait(java.base@19.0.2/Object.java:339)
        at java.lang.ref.NativeReferenceQueue.await(java.base@19.0.2/NativeReferenceQueue.java:48)
        at java.lang.ref.ReferenceQueue.remove0(java.base@19.0.2/ReferenceQueue.java:158)
        at java.lang.ref.NativeReferenceQueue.remove(java.base@19.0.2/NativeReferenceQueue.java:89)
        - locked <0x00000000d019eba0> (a java.lang.ref.NativeReferenceQueue$Lock)
        at java.lang.ref.Finalizer$FinalizerThread.run(java.base@19.0.2/Finalizer.java:173)

   Locked ownable synchronizers:
        - None

线程转储可视化

  • 线程转储的可视化在线分析平台:
  1. jstack:免费,但内容有限
  2. fastthread:收费,但每个账号每个月有5次的分析余额,分析更全面
  • 使用方法:将保存的文件内容上传到网站即可,这里展示jstack的分析结果(赏心悦目多了)
    JVM实战之性能调优[1](概述和方法)-LMLPHP
04-01 12:15