版权声明
- 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利人所有。本博客的目的仅为个人学习和交流之用,并非商业用途。
- 我在整理学习笔记的过程中尽力确保准确性,但无法保证内容的完整性和时效性。本博客的内容可能会随着时间的推移而过时或需要更新。
- 若您是黑马程序员或相关权利人,如有任何侵犯版权的地方,请您及时联系我,我将立即予以删除或进行必要的修改。
- 对于其他读者,请在阅读本博客内容时保持遵守相关法律法规和道德准则,谨慎参考,并自行承担因此产生的风险和责任。
- 本博客中的部分观点和意见仅代表我个人,不代表黑马程序员的立场。
性能调优概述
- JVM(Java虚拟机)性能调优是一个复杂但重要的过程,目的是提高Java应用程序的性能和效率。JVM性能调优通常包括监控Java虚拟机的运行情况、识别性能瓶颈、调整JVM设置以及优化Java代码。
- JVM性能调优是一个持续的过程,需要根据应用的实际运行情况和性能目标进行。不同的应用和工作负载可能需要不同的调优策略。最好是以实际的性能监控数据为基础,逐步迭代优化。
性能调优步骤
- 性能优化的步骤总共分为四个步骤,其中修复部分要具体问题具体分析且处理方式各不相同。
性能常见问题
- 应用程序在运行过程中经常会出现性能问题。
- 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
- 请求单个服务处理时间特别长(多服务使用skywalking等监控系统来判断哪一个环节性能低下)
- 程序启动之后运行正常,但运行一段时间之后无法处理任何的请求(内存和GC正常,线程耗尽)
性能调优方法
线程转储概念
- 线程转储(Thread Dump)提供对所有运行中的线程当前状态的快照。其中包含线程名、优先级、线程ID、线程状态、线程栈信息等等内容,可以用来解决CPU占用率高、死锁等问题。
- 线程转储可以通过jstack、VisualVM、JMC(Java Mission Control)等工具获取。
线程转储方式
- 方式一:通过jstack命令。jstack是JDK自带的工具,用于生成Java应用程序的线程转储。使用时需要提供Java进程的PID(Process ID)
# -l参数是可选的,用于生成关于锁的额外信息
jstack -l <pid> #<pid>是你想要获取线程转储的Java进程的进程ID
# 保存线程转储内容到本地
jstack -l <pid> > /path/to/directory/threadDump.txt
- 方式二:使用VisualVM等工具获取
- 使用VisualVM打开local,找到运行的java程序,打开线程中的
Thread Dump
即可查看到线程转储的内容,如果需要保存到本地,右键java程序选择Save as
选择路径即可保存到本地。
- 使用VisualVM打开local,找到运行的java程序,打开线程中的
线程转储查看
线程转储(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
线程转储可视化
- 线程转储的可视化在线分析平台:
- jstack:免费,但内容有限
- fastthread:收费,但每个账号每个月有5次的分析余额,分析更全面
- 使用方法:将保存的文件内容上传到网站即可,这里展示jstack的分析结果(赏心悦目多了)