详解JAVA程序调优

简介: 详解JAVA程序调优

1.概述

在实际工作中我们难免会遇见程序执行慢、线程死锁等一系列的问题,这时候就需要我们定位具体问题然后来解决问题了。所谓的程序调优其实也就是定位问题然后解决问题。怎么定位问题喃?在JAVA程序的调优中我们无非要关注以下内容:

  • JVM的情况
  • 程序的情况

JVM的情况无其实就是去看一下它的类加载情况、内存使用情况、GC的情况。

程序的情况无非就是其线程的运行情况。

JDK为我们提供了两套方案来查看这两方面的情况,一方面是命令,另一方面是工具。

2.命令

2.1.查看JAVA进程

当然查看上面说的两方面情况的前提是要知道程序的进程是哪个对吧。所以首先是第一个常用的命令:

  • jps,查看当前机器上的java进程

jps命令默认输出进程的ID号以及对应的主启动类:


jps常用的一些参数:

  • -q:简洁模式,只输出Java虚拟机(JVM)的进程ID(PID),不显示主类名称或者其他详细信息。
  • -m:输出运行主类(即包含main方法的类)时传递给main方法的参数。

2.2.查看虚拟机状态

jstat,查看虚拟机状态,该命令需要跟上参数来指定具体查看的项目。

jstat的主要常用参数如下:

  • 监控堆内存(Heap): jstat -gc <pid> [interval] [count]:显示GC(Garbage Collection)统计信息,包括各代堆内存的大小、已用空间、已分配对象数以及GC次数与时间等。这里要注意的是如果只是想看内存情况,jmp命令也可以办到。

类装载系统(Class Loader): jstat -class <pid>:显示类加载器相关的统计信息,比如已加载的类数量及其占用的空间。


线程信息(Thread Information): jstat -thread <pid>:显示有关Java线程的信息,如活动线程数、死锁检测等。

  • GC算法详细信息(Detailed GC Algorithm Info): jstat -gccapacity <pid>:显示各个分代区域的容量配置。 jstat -gcutil <pid>:提供更详细的GC使用率信息,包括堆区的使用率和永久代的使用情况。

jstat -gc的结果对应的字段含义如下:

年轻代相关指标: S0C: 第一个幸存区(Survivor Space 0)的当前容量(字节)。 S1C: 第二个幸存区(Survivor Space 1)的当前容量。 S0U: 第一个幸存区当前已使用的空间。 S1U: 第二个幸存区当前已使用的空间。


Eden区和老年代相关指标: EC: Eden区的当前容量。 EU: Eden区当前已使用的空间。 OC: 老年代(Old Generation)的当前容量。 OU: 老年代当前已使用的空间。


元空间(Metaspace)或永久代(Permanent Generation,在JDK 1.8之前的版本中): MC: 元空间的当前容量(在JDK 1.8及更高版本中)或永久代的当前容量(在JDK 1.8之前)。 MU: 元空间当前已使用的空间或永久代当前已使用的空间。


垃圾收集次数和时间: YGCT: 自JVM启动以来,年轻代垃圾回收(Young Generation Collection)所花费的总时间。 FGCT: 自JVM启动以来,全局或全堆垃圾回收(Full Garbage Collection,也可能是并发标记清除等类型的GC)所花费的总时间。 GCT: 总共的垃圾回收时间(YGCT + FGCT)。

jstat -gccapacity 结果对应的字段含义如下:

NGCMN - 新生代(Young Generation)的最小容量。


NGCMX - 新生代的最大容量。


NGC - 当前新生代的实际容量。


OGCMN - 老年代(Old Generation)的最小容量。


OGCMX - 老年代的最大容量。


OGC - 当前老年代的实际容量。


PC/MC - 元空间(MetaSpace)的最小容量(在JDK 1.8及更高版本中)。在JDK 1.8之前,这里是永久代(PermGen)的相关字段。


PC/MC - 元空间的当前容量。


CCSMN - 如果存在的话,表示压缩类空间(Compressed Class Space)的最小容量(在启用压缩类空间的情况下)。


CCSMX - 压缩类空间的最大容量。


CCSC - 当前压缩类空间的实际容量。


OC (Old Capacity): 表示当前老年代(Old Generation)的容量,即当前老年代能容纳多少内存空间。


MCMN (Metaspace Capacity Minimum): 在JDK 1.8及更高版本中,这个字段指的是元空间(Metaspace)的最小容量。元空间取代了永久代(PermGen),用于存储类元数据和静态变量等。


MCMX (Metaspace Capacity Maximum): 表示元空间的最大容量,即元空间允许增长到的最大内存大小。


MC (Metaspace Capacity): 表示当前元空间的实际容量,即当前元空间占用的内存大小。

jstat -gcutil 结果对应的字段含义如下:

S0:第一个幸存区(Survivor Space 0)的使用率。


S1:第二个幸存区(Survivor Space 1)的使用率。


E:伊甸园区(Eden Space)的使用率。


O:老年代(Old Generation)的使用率。


M:元空间(Metaspace)的使用率。在JDK 1.8及更高版本中,元空间取代了永久代(PermGen)来存储类的元数据和静态变量。


CCS(如果存在):压缩类空间(Compressed Class Space)的使用率(在启用压缩类空间的情况下)。


YGC:自JVM启动以来,年轻代(Young Generation)垃圾回收的次数。


YGCT:自JVM启动以来,年轻代垃圾回收所消耗的总时间。


FGC:自JVM启动以来,全局或全堆(Full GC)垃圾回收的次数。


FGCT:自JVM启动以来,全局或全堆垃圾回收所消耗的总时间。


GCT:自JVM启动以来,所有垃圾回收(包括年轻代和全局)所消耗的总时间。

2.3.查看线程的情况

查看线程的情况无非就是抓一下线程的执行记录,也就是线程快照,jstack,可以用来抓线程快照,从而展示JVM当前线程的一个总体情况。


如果Java进程启用了远程调试,jstack可以连接到远程主机上的调试端口来获取线程堆栈信息:


jstack [-l] <host>:<port>

其中,-l 参数会包含锁定信息,有助于发现死锁问题。


jstack命令输出的信息通常包括:


线程ID(Thread ID)和线程名称(Thread Name)


线程的状态(如RUNNABLE、BLOCKED、WAITING、TIMED_WAITING)


线程执行的Java方法堆栈(Method Stack Trace),按照调用顺序逐层显示,这有助于追踪线程执行流和识别阻塞点。


通过分析jstack命令的输出,可以快速找到哪些线程处于何种状态,以及可能导致性能瓶颈或问题的代码位置,从而指导进一步的问题排查和性能调优工作。


3.工具

3.1.jconsole

连接上我们想监控的程序就可以看到,这其实就是个集成了前文所有命令的功能的图形化界面:

这里需要注意的是jconsole可以用来监控MBean,也就是用JMX自行实现的监控指标,对JMX不了解但是有兴趣的读者可以移步:

3.2.jvisualVM

jvisualVM其实和jconsole在功能上大部分是重叠的也是那些命令的图形化工具:


但是其和jconsole不同的是:


当我们用到了JMX,有自定义的MBean的时候一般用jconsole来进行可视化监控。当我们需要导入线程快照的时候一般用jvisualVM。


为什么会导入jvisualVM喃?是因为在实际的生产环境中,为了防止被攻击,服务器不会开出去太多的端口,所以想通过JDK的监控工具直接远程到服务器上去其实是比较难的,所以一般是用jstack命令去抓一段线程快照,然后拉到本地来用jvisualVM来进行可视化分析从而定位问题。

4.实战场景

下面是一些使用上述JDK工具进行具体场景优化和问题排查的例子:

示例1:使用jps和jstat排查内存泄漏

场景:一个长期运行的Java服务突然出现内存占用过高,疑似存在内存泄漏。


操作: 首先,使用jps命令找到目标Java进程的ID。 接着,利用jstat -gcutil <pid>周期性地监控JVM的内存使用情况,包括年轻代、老年代和元空间的使用率,以及垃圾回收的次数和时间。 如果观察到老年代使用率不断攀升且垃圾回收频繁且无效,则可能存在内存泄漏。 进一步,通过jmap -dump:format=b,file=<filename>.hprof <pid>命令生成堆转储文件。 使用MAT(Memory Analyzer Tool)分析堆转储文件,查找是否存在大量未释放的对象及其引用路径。


示例2:使用jstack解决线程死锁问题


场景:一个Java应用在高负载下发生卡顿,初步判断可能是线程死锁。


操作: 使用jps找到目标进程ID。 执行jstack <pid>命令获取线程堆栈信息。 分析输出的线程堆栈,寻找那些状态为BLOCKED并且持有和等待资源相互关联的线程,这些往往是死锁的关键线索。 根据堆栈信息找到造成死锁的代码片段,修改代码以避免死锁发生。


示例3:使用jinfo动态调整JVM参数


场景:一个Java应用在运行时发现GC过于频繁,影响了系统性能。


操作: 使用jps找到目标进程ID。 使用jinfo -flag UseConcMarkSweepGC <pid>检查当前是否使用CMS垃圾收集器。 若确认为CMS,但是效果不佳,可以考虑调整GC参数,如增大初始堆大小 -Xms 或 最大堆大小 -Xmx,或者改变GC策略(如切换到G1垃圾收集器)。 使用jinfo -flag -Xms <new_size> <pid>或jinfo -flag -Xmx <new_size> <pid>动态调整堆大小(并非所有JVM版本都支持所有参数的动态调整)。


示例4:使用jconsole进行实时性能监控


场景:需要持续监控Java应用的CPU、内存、线程数等基础性能指标。


操作: 启动Java应用时添加 -Dcom.sun.management.jmxremote 等JMX远程监控相关参数。 运行jconsole,连接到目标Java应用。 在jconsole界面中,可以实时查看各项性能指标,并进行图表绘制和历史数据分析,快速发现潜在的性能瓶颈。 通过这些实际场景和操作步骤,可以看到JDK自带的各种工具在实际工作中是如何协同配合,帮助我们诊断和优化Java应用程序的。

目录
相关文章
|
7天前
|
XML 存储 JSON
Java程序部署
Java程序部署
|
2月前
|
监控 算法 Java
Java内存管理:垃圾收集器的工作原理与调优实践
在Java的世界里,内存管理是一块神秘的领域。它像是一位默默无闻的守护者,确保程序顺畅运行而不被无用对象所困扰。本文将带你一探究竟,了解垃圾收集器如何在后台无声地工作,以及如何通过调优来提升系统性能。让我们一起走进Java内存管理的迷宫,寻找提高应用性能的秘诀。
|
2月前
|
Kubernetes Cloud Native Java
云原生之旅:从容器到微服务的演进之路Java 内存管理:垃圾收集器与性能调优
【8月更文挑战第30天】在数字化时代的浪潮中,企业如何乘风破浪?云原生技术提供了一个强有力的桨。本文将带你从容器技术的基石出发,探索微服务架构的奥秘,最终实现在云端自由翱翔的梦想。我们将一起见证代码如何转化为业务的翅膀,让你的应用在云海中高飞。
|
21天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
175 37
|
8天前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
18 5
|
9天前
|
Java 编译器 数据库连接
探索Java中的异常处理:提升程序的鲁棒性
【9月更文挑战第25天】在Java的世界里,异常是那些不请自来、令人头疼的“客人”。它们悄无声息地潜入我们的代码,一旦出现,便可能导致程序崩溃或行为异常。但是,如果能够妥善管理这些异常,我们就能将潜在的灾难转变为增强程序鲁棒性和用户体验的机会。本文将通过深入浅出的方式,带领读者理解Java异常处理的重要性,并提供实用的策略来优雅地处理这些意外情况。让我们一起学习如何在Java中捕捉、处理和预防异常,确保我们的程序即使在面对不可预见的错误时也能保持稳健运行。
|
13天前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
15天前
|
监控 Java 数据库
Java程序如何进行不停机更新?
Java程序如何进行不停机更新?
17 1
|
26天前
|
监控 IDE Java
【Java性能调优新工具】JDK 22性能分析器:深度剖析,优化无死角!
【9月更文挑战第9天】JDK 22中的性能分析器为Java应用的性能调优提供了强大的支持。通过深度集成、全面监控、精细化分析和灵活报告生成等核心优势,性能分析器帮助开发者实现了对应用性能的全面掌控和深度优化。在未来的Java开发过程中,我们期待性能分析器能够继续发挥重要作用,为Java应用的性能提升贡献更多力量。
|
1月前
|
缓存 监控 安全
如何提高 Java 高并发程序的性能?
以下是提升Java高并发程序性能的方法:优化线程池设置,减少锁竞争,使用读写锁和无锁数据结构。利用缓存减少重复计算和数据库查询,并优化数据库操作,采用连接池和分库分表策略。应用异步处理,选择合适的数据结构如`ConcurrentHashMap`。复用对象和资源,使用工具监控性能并定期审查代码,遵循良好编程规范。
下一篇
无影云桌面