【JVM性能调优】jstack和线程dump分析

jstack生成的Thread Dump日志.docx 系统线程状态 (Native Thread Status) 系统线程有如下状态: deadlock 死锁线程,一般指多个线程用期间进入了相互资源占用,导致一直等待无法释放的情况。 runnable 一般指该线程正在执行状态中,该线程占用了资源,正在处理某个操作,如通过SQL语句查询数据库、对某个文件进行写入等。 blocked 线程正处于阻塞状态,指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。 waiting on condition 线程正处于等待资源或等待某个条件的发生,具体的原因需要结合下面堆栈信息进行分析。 (1)如果堆栈信息明确是应用代码,则证明该线程正在等待资源,一般是大量读取某种资源且该资源采用了资源锁的情况下,线程进入等待状态,等待资源的读取,或者正在等待其他线程的执行等。 (2)如果发现有大量的线程都正处于这种状态,并且堆栈信息中得知正等待网络读写,这是因为网络阻塞导致线程无法执行,很有可能是一个网络瓶颈的征兆: 网络非常繁忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写; 网络可能是空闲的,但由于路由或防火墙等原因,导致包无法正常到达; 所以一定要结合系统的一些性能观察工具进行综合分析,比如netstat统计单位时间的发送包的数量,看是否很明显超过了所在网络带宽的限制;观察CPU的利用率,看系统态的CPU时间是否明显大于用户态的CPU时间。这些都指向由于网络带宽所限导致的网络瓶颈。 (3)还有一种常见的情况是该线程在 sleep,等待 sleep 的时间到了,将被唤醒。 waiting for monitor entry 或 in Object.wait() Moniter 是Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者class的锁,每个对象都有,也仅有一个 Monitor。 从上图可以看出,每个Monitor在某个时刻只能被一个线程拥有,该线程就是 "Active Thread",而其他线程都是 "Waiting Thread",分别在两个队列 "Entry Set""Waint Set"里面等待。其中在 "Entry Set" 中等待的线程状态是 waiting for monitor entry,在 "Wait Set" 中等待的线程状态是 in Object.wait()。 (1)"Entry Set"里面的线程。 我们称被 synchronized 保护起来的代码段为临界区,对应的代码如下: synchronized(obj){} 当一个线程申请进入临界区时,它就进入了 "Entry Set" 队列中,这时候有两种可能性: 该Monitor不被其他线程拥有,"Entry Set"里面也没有其他等待的线程。本线程即成为相应类或者对象的Monitor的Owner,执行临界区里面的代码;此时在Thread Dump中显示线程处于 "Runnable" 状态。 该Monitor被其他线程拥有,本线程在 "Entry Set" 队列中等待。此时在Thread Dump中显示线程处于 "waiting for monity entry" 状态。 临界区的设置是为了保证其内部的代码执行的原子性完整性,但因为临界区在任何时间只允许线程串行通过,这我们使用多线程的初衷是相反的。如果在多线程程序中大量使用synchronized,或者不适当的使用它,会造成大量线程在临界区的入口等待,造成系统的性能大幅下降。如果在Thread Dump中发现这个情况,应该审视源码并对其进行改进。 (2)"Wait Set"里面的线程线程获得了Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则用对象(通常是被synchronized的对象)的wait()方法,放弃Monitor,进入 "Wait Set"队列。只有当别的线程在该对象上用了 notify()或者notifyAll()方法,"Wait Set"队列中的线程才得到机会去竞争,但是只有一个线程获得对象的Monitor,恢复到运行态。"Wait Set"中的线程在Thread Dump中显示的状态为 in Object.wait()。通常来说, 通常来说,当CPU很忙的时候关注 Runnable 状态的线程,反之则关注 waiting for monitor entry 状态的线程JVM线程运行状态 (JVM Thread Status)
### JVM性能的方法与最佳实践 #### 一、JVM性能的基础理论 JVM性能的核心目标在于化应用程序的运行效率以及资源利用率。通过JVM的各种参数,可以显著提升程序的表现。基础理论涵盖了垃圾回收机制(GC)、堆内存分配策略等内容[^1]。 #### 二、JVM性能的关键参数 为了实现高效的性能,需要熟悉并合理设置一系列重要的JVM参数。这些参数主要包括但不限于以下几个方面: - **堆内存配置** 堆内存分为年轻代(Young Generation)老年代(Tenured/Old Generation),其比例可以通过`-XX:NewRatio=<value>`来设定,默认情况下新旧代的比例为2:8。此外,还可以指定初始堆大小(`-Xms`)最大堆大小(`-Xmx`)。默认的最大堆内存约为物理内存的四分之一,在某些环境中可能接近450MB,而初始化大小通常较小,约30MB左右[^2]。 - **垃圾回收器的选择** 不同的应用场景适合不同的垃圾回收算法。常见的有Serial GC(-XX:+UseSerialGC), Parallel GC (-XX:+UseParallelGC), CMS (Concurrent Mark Sweep, -XX:+UseConcMarkSweepGC) G1 Garbage Collector (-XX:+UseG1GC)[^1]。每种GC都有各自的适用范围特点,例如CMS适用于低延迟需求较高的应用,而G1则更适合大容量堆环境下的综合表现化。 #### 三、具体操作流程 实际执行过程中,可以从多个角度入手来进行详细的诊断与改进工作: 1. 使用工具定位问题源码位置及其影响因素。比如利用 `jps -l`, `top -p <PID>`, 转换线程ID至十六进制形式并通过`printf "%x\n"`完成转换后再借助`jstack`命令获取具体的栈轨迹信息以便进一步排查异常行为所在之处[^3]; 2. 对于发现存在瓶颈或者耗时较长的操作部分,则需深入研究相关逻辑是否存在可化空间; 3. 同时也要关注外部依赖库版本兼容性情况以及其他潜在干扰项是否会对整体效能造成负面影响; 4. 配合使用专业的APM(Application Performance Management)平台持续跟踪线上实例状态变化趋势图谱从而及时作出相应整措施直至达到预期效果为止。 以下是用于打印当前虚拟机所有选项的一个简单脚本示例: ```bash #!/bin/bash if [ $# -ne 1 ]; then echo "Usage: $0 <pid>" exit 1 fi jinfo $1 | grep 'MaxHeapSize' ``` 此脚本接受一个进程号作为输入参数,并显示该进程中关于最大堆尺寸的信息。 #### 四、案例分享——解决堆溢出(OOM) 当遇到OutOfMemoryError错误提示时,意味着已经超出了所允许使用的内存限额界限。此时应当按照如下步骤处理: - 收集必要的数据资料,包括但不限于完整的gc日志文件、heap dump快照等辅助材料; - 应用专门设计用来解析上述提到的数据类型的软件产品如Eclipse MAT(Memory Analyzer Tool) 或 Visual VM 来识别主要占用者群体特征模式; - 结合业务背景知识共同探讨可行解决方案方向,最终决定增加可用硬件资源配置还是重构现有架构体系结构以减少不必要的对象创建活动频率等方式缓解压力状况。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值