Java web应用性能分析之【java进程问题分析概叙】-****博客
前面大概讲了java进程问题分析流程,这里再小结一下分析工具,后面也会小结一下java进程问题分析定位。
1.分析工具
1.1.linux命令工具
参考:Java web应用性能分析之【Linux服务器性能监控分析概叙】_web应用 cpu密集型-****博客
1.2.jdk自带分析工具
在JDK的bin目录下有很多命令行工具,各个工具的大小基本上都稳定在27kb左右,这个不是JDK开发团队刻意为之的,而是因为这些工具大多数是jdk\lib\tools.jar类库的一层薄包装而已,他们的主要功能代码是在tools类库中实现的。命令行工具的好处是:当应用程序部署到生产环境后,无论是直接接触物理服务器还是远程telnet到服务器上都会受到限制。而借助tools.jar类库里面的接口,我们可以直接在应用程序中实现功能强大的监控分析功能。
常用命令:
1、jps:JVM Process Status Tool,现实指定系统内所有的HotSpot虚拟机进程 查看本机java进程信息;jinfo:Configuration Info for Java,虚拟机配置信息, 查看jvm和系统参数信息。
2、jstack:Stack Track for java ,显示虚拟机线程快照,打印线程的栈信息,制作 线程dump文件
3、jmap: Memory map for java,生成虚拟机的内存转储快照,打印内存映射信息,制作 堆dump文件
4、jstat:JVM Statistics Monitoring Tool,用于收集Hotspot虚拟机各个方面的运行参数 ,gc性能监控工具。
5、jhat:内存分析工具,JVM heap Dunp Browser,用于分析heapdump文件,他会建立一个HTTP/HTML服务,让用户可通过浏览器查看 。
jhat是sun提供的dump分析工具,上面讲过分析dump的工具还有MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer等,一般这个命令不太用到,是因为分析dump是个既耗时又耗机器资源的过程,第二个原因是这个工具比较简陋,没有MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer这些专业和强大。
OpenJDK正在从JDK中废弃HPROF agent 并移除“jhat”工具。这是OpenJDK 9的Java SE平台借助Jigsaw实现模块化的结果,为了准备模块化,起草了多项提议(JEP),并且其中有很多都已经接近完成了。
6、jconsole:JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动。简易的JVM可视化工具,其功能被jvisualvm替代。
7、jvisualvm:功能更强大的JVM可视化工具,
jvisualVM所谓多合一虚拟机故障处理工具,有强大的插件扩展功能,通过安装插件扩展支持,jvisualVM可以做到:
a、显示虚拟机进程及进程的配置和环境信息(jps,jinfo);
b、监视应用程序CPU、GC、堆、方法区及线程的信息(jstat、jstack);
c、dump及分析堆转储快照(jmap、jhat);
d、方法级的程序性能分析,找出调用最多,运行时间最长的方法;
8、javap:查看字节码
9、jcmd:在JDK 1.7之后,新增了一个命令行工具jcmd。它是一个多功能工具,可以用来导出堆,查看java进程,导出线程信息,执行GC等。jcmd拥有jmap的大部分功能,Oracle官方建议使用jcmd代替jmap。
1.3.arthas
- 简介 | arthas
- https://github.com/alibaba/arthas/issues/1892
Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
Arthas
可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到 JVM 的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
- 怎样直接从 JVM 内查找某个类的实例?
Arthas
支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab
自动补全功能,进一步方便进行问题的定位和诊断。
1.4.jprofile
- YourKit是一个商业化的Java性能分析工具,提供了线程转储分析功能,可以快速定位线程问题,同时还提供了内存和CPU分析功能。
- Java Profiler Features - YourKit
JProfiler是一种Java性能分析器,可以帮助开发人员监视和优化Java应用程序的性能。它提供了一个功能强大的图形用户界面,使开发人员能够实时监视CPU使用情况、内存使用情况、线程和锁状态等信息。JProfiler还具有一些其他功能,例如分析内存泄漏、检测代码覆盖率、监视数据库连接等。
使用JProfiler进行性能分析的一般步骤:
- 下载和安装JProfiler:从JProfiler官方网站下载JProfiler安装程序,并按照提示进行安装。
- 启动JProfiler:在开始菜单或桌面图标中找到JProfiler应用程序,并启动它。
- 创建会话:在JProfiler主界面中,单击“会话”按钮,并选择“创建”会话。在“会话设置”对话框中,选择要监视的Java进程或远程主机,并设置其他选项。
- 配置监视选项:在会话设置中,您可以选择要监视的Java应用程序选项。例如,您可以监视Java应用程序的CPU使用情况、内存使用情况、线程和锁状态等信息。
- 开始监视:在会话设置中,单击“开始”按钮,以开始监视Java应用程序。JProfiler将显示实时数据,并在出现问题时发出警告。
- 分析性能数据:在监视期间,您可以使用JProfiler的图形用户界面查看性能数据。您可以使用各种图表和工具来分析Java应用程序的性能,例如CPU使用率图表、内存使用情况图表、线程和锁状态等。
- 解决问题:根据分析的结果,您可以使用JProfiler提供的各种工具来解决性能问题。例如,您可以修复内存泄漏、优化代码、调整JVM参数等。
- 关闭会话:在解决问题后,您可以关闭JProfiler会话。如果您需要继续监视Java应用程序的性能,可以创建一个新的会话并重复上述步骤。
总之,使用JProfiler可以帮助开发人员更好地了解Java应用程序的性能,并解决性能问题。但是,使用JProfiler需要一定的经验和技能,因此建议您先学习有关JProfiler的相关知识再使用它。
1.5.jar包反编译工具
下载地址:Java Decompiler
JD-GUI和JAD是两个用于反编译Java字节码的工具。它们通常在以下情况下使用:
- 代码调试:当你需要调试Java代码时,可以使用JD-GUI将Java类反编译为可读的源代码,以便进行调试和分析。
- 代码分析:如果你需要对Java类进行深入的分析,例如了解代码结构、方法调用关系等,可以使用JD-GUI来反编译Java类,并查看反编译后的源代码。
- 代码重构:如果你需要修改现有的Java类,可以使用JD-GUI将Java类反编译为可读的源代码,然后进行修改。注意,反编译后的源代码可能不完全与原始源代码相同,因此在进行修改时需要谨慎。
- 辅助学习:如果你是一名Java初学者,可以通过JD-GUI和JAD反编译现有的Java类,了解Java类的结构和实现方式,从而加深对Java语言的理解。
1.6.idea远程debug工具
线上环境最好不要远程debug,因为debug时,会阻塞当前java进程的全部请求。大多数是在测试环境、开发环境。
远程调试(对应IDEA功能:Remote JVM Debug)。远程调试使开发人员能够直接诊断服务器或其它线上进程上的问题,它提供了跟踪线上运行时错误并确定性能瓶颈和问题根源的方法,让你能够像在本地调试一样 Debug 远程服务器。
Ⅰ:Java的远程调试机制:
Java虚拟机提供了远程调试机制,只需要在服务端启动时通过添加JVM参数开启调试服务端口,并且在
客户端使用调试器连接到这个服务端口,就可以实现对远程JVM上程序的调试。
Ⅱ:IntelliJ IDEA远程调试的实现方式:
IDEA利用Java的远程调试机制,在目标JVM上启用调试服务端口,并在IDEA上创建远程调试配置,连接
到目标JVM上的调试服务端口,实现对远程程序的调试和控制。
Ⅲ:远程调试协议:
在远程调试时,客户端和服务端之间通过远程调试协议进行通信。客户端会发送调试命令和断点信息到
服务端口,服务端会执行调试命令并返回相应的结果,通过协议来实现客户端与服务端的交互。
Ⅳ:远程调试的工作原理:
远程调试流程包括以下步骤:
①:启动远程 JVM 并配置调试服务端口;
②:在客户端创建远程调试配置,并连接到目标 JVM 上的调试服务端口;
③:在客户端设置断点和调试命令,并通过远程调试协议发送到服务端口;
④:服务端接收到命令后执行,并返回结果;
⑤:客户端收到结果后,根据调试命令和状态更新调试器界面。
Ⅴ:远程调试的优劣势:
优势:可以直接在目标环境上调试代码,同时也可避免因操作系统等差异导致的问题;
劣势:需要在目标环境上(服务端)开启调试服务端口,同时还需要在客户端设置调试器进行连接,非
常繁琐。
1:服务端添加远程通道
要让远程服务器运行的代码支持远程调试,则服务端启动的时候必须加上特定的 JVM 参数,这些参数是:
不同的JDK版本需要设置不同的配置:
JDK 9 or later
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9999
JDK 5-8
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999
JDK 1.4.x
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999
JDK 1.3.x or earlier
-Xnoagent -Djava.compiler=NONE -Xdebug
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999
这里的9999则是服务端开放的端口,后期客户端IDEA需要连接当前端口进行远程交互和调试。
但是我们需要注意的是,这个9999端口在服务端一定要放开防火墙或者安全组;
具体端口看项目需求;运行服务端jar包程序则如下(JDK 5-8版本):
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999
./SwaggerDemo-0.0.1-SNAPSHOT.jar > app.log &
2:客户端连接远程通道
设置Edit Configurations来配置信息
备注:服务器代码和本地代码必须一致,否则debug断点无效。
1.7.springbootAdmin
在SpringCloud项目中,部署一个SpringBootAdmin即可通过web页面查看各应用的request、堆、栈等信息。具体实现是SpringCloud的actoator。
用于对 Spring Boot 应用的管理和监控。可以用来监控服务是否健康、是否在线、以及一些jvm数据等等。 Spring Boot Admin 分为服务端(spring-boot-admin-server)和客户端(spring-boot-admin-client),服务端和客户端之间采用 http 通讯方式实现数据交互;单体项目中需要整合 spring-boot-admin-client 才能让应用被监控。 在 SpringCloud 项目中,spring-boot-admin-server 是直接从注册中心抓取应用信息,不需要每个微服务应用整合 spring-boot-admin-client 就可以实现应用的管理和监控。
主要的功能点有:
- 显示应用程序的监控状态
- 应用程序上下线监控
- 查看 JVM,线程信息
- 可视化的查看日志以及下载日志文件
- 动态切换日志级别
- Http 请求信息跟踪
详细配置如下
<!--用于检查系统的监控情况-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--Spring Boot Admin Server监控服务端-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.3.1</version>
</dependency>
<!--增加安全防护,防止别人随便进-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
启动类开启admin@EnableAdminServer
登录配置
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 登录成功处理类
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(adminContextPath + "/");
http.authorizeRequests()
//静态文件允许访问
.antMatchers(adminContextPath + "/assets/**").permitAll()
//登录页面允许访问
.antMatchers(adminContextPath + "/login", "/css/**", "/js/**", "/image/*").permitAll()
//其他所有请求需要登录
.anyRequest().authenticated()
.and()
//登录页面配置,用于替换security默认页面
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
//登出页面配置,用于替换security默认页面
.logout().logoutUrl(adminContextPath + "/logout").and()
.httpBasic().and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers(
"/instances",
"/actuator/**"
);
}
}
yml配置
server:
port: 9111
spring:
boot:
admin:
ui:
title: HMB服务监控中心
client:
instance:
metadata:
tags:
environment: local
#要获取的client的端点信息
probed-endpoints: health,env,metrics,httptrace:trace,threaddump:dump,jolokia,info,logfile,refresh,flyway,liquibase,heapdump,loggers,auditevents
monitor: # 监控发送请求的超时时间
default-timeout: 20000
security: # 设置账号密码
user:
name: admin
password: admin
# 服务端点详细监控信息
management:
trace:
http:
enabled: true
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
启动项目
访问 http://ip:端口,
如我的http://localhost:9111,账号密码都是admin(上面的security配的)
多大
1.8.JOL(即Java Object Layout)
JOL(即Java Object Layout):OpenJDK提供的库,用于查看Java对象的内存布局,这个很有用,可以借助它来跟踪锁升级等过程。只需要引入Maven即可使用。
//引入依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
//代码
class TTTT {
public static void main(String[] args) {
System.err.println(ClassLayout.parseInstance(new Person()).toPrintable());
System.err.println(ClassLayout.parseClass(Person.class).toPrintable());
}
}
class Person {
private int age = 1;
private String name = "zhangsan";
}
//代码执行结果
com.marchon.learning.Person object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000005e4c804101 (hash: 0x5e4c8041; age: 0)
8 4 (object header: class) 0xf8010dd9
12 4 int Person.age 1
16 4 java.lang.String Person.name (object)
20 4 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
2.分析工具使用
2.1 jps和jinfo
常用命令: jps (输出java进程号pid)、 jps -l (输出java进程号和详细的jar信息) 、 jinfo pid(输出 java进程系统参数和jvm参数)、jinfo -flags pid (指输出jvm参数信息)
2.2 jstack
jstack 是 JDK 自带的一种堆栈跟踪工具,可用于生成 JVM 当前时刻的线程快照。线程快照是当前 JVM 内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过 jstack 来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果 java 程序崩溃生成 core 文件,jstack 工具可以用来获得 core 文件的 java stack 和 native stack 的信息,从而可以知道 java 程序是如何崩溃和在程序何处发生问题。另外,jstack 工具还可以 attach 到正在运行的 java 程序中,看到当时运行的 java 程序的 java stack 和 native stack的信息, 如果现在运行的 java 程序呈现 hung 的状态,jstack 是非常有用的。
简而言之,jstack 主要用来查看 Java 线程的调用堆栈,可以用来分析线程问题(如死锁、死循环、CPU 占用过高)。
- -F:当正常输出的请求不被响应时,强制输出线程堆栈
- -m:如果调用到本地方法的话,加上此参数可以显示本地方法的堆栈
- -l:最常用的一个参数,除堆栈外,显示关于锁的附加信息,在发生死锁时可以用 jstack -l pid 来观察锁持有情况
把 jstack 的输出重定向到文件中,就可以分析了。
// 比如
jstack -l <pid> >> thread.log
java中锁的实现是通过monitor来实现的,它可以看成是对象或者 Class 的锁。每一个对象都有,也仅有一个 monitor。下图描述了线程和 Monitor 之间的关系,以及线程的状态转换图:
- 进入区(Entry Set):表示线程通过 synchronized 要求获取对象的锁。如果对象未被锁住,则变为拥有者,否则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。
- 拥有者(The Owner):表示某一线程成功竞争到对象锁。
- 等待区(Wait Set):表示线程通过对象的 wait 方法,释放对象的锁,并在等待区等待被唤醒。
从图中可以看出,一个 Monitor 在某个时刻,只能被一个线程拥有,该线程就是 Active Thread,而其它线程都是 Waiting Thread,分别在两个队列 Entry Set 和 Wait Set 里面等候。在 Entry Set 中等待的线程状态是 Waiting for monitor entry,而在 Wait Set 中等待的线程状态是 “in Object.wait()”。
使用 jstack 分析死锁步骤非常简单:
- jps 获取 pid
- jstack -l pid 打印堆栈信息
- 分析堆栈信息,一般来说 Java-level 的死锁,jstack 能自动检测出来。
比如下面这段 jstack 打印的堆栈信息,就是 jstack 自动检测出了一个 Java-level 死锁:
- Thread-0 锁住了<0x00000007d6aa2c98>,尝试获取 <0x00000007d6aa2ca8> 的锁
- Thread-1 锁住了<0x00000007d6aa2ca8>,尝试获取 <0x00000007d6aa2c98> 的锁
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f0134003ae8 (object 0x00000007d6aa2c98, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f0134006168 (object 0x00000007d6aa2ca8, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at javaCommand.DeadLockclass.run(JStackDemo.java:40)
- waiting to lock <0x00000007d6aa2c98> (a java.lang.Object)
- locked <0x00000007d6aa2ca8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at javaCommand.DeadLockclass.run(JStackDemo.java:27)
- waiting to lock <0x00000007d6aa2ca8> (a java.lang.Object)
- locked <0x00000007d6aa2c98> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
需要注意的是,jstack 只能自动检测 Java 线程间的死锁,不能检测到其它类型的线程状态,比如本地线程或者操作系统线程的状态。
如果要分析本地线程或者操作系统线程是否出现了死锁,可以在使用 jstack 时加上 -m 参数,把本地线程堆栈一起打印起来进行分析。
2.2.1 jstack分析锁信息和死锁
使用 jstack 查看线程堆栈信息时可能会看到的线程的几种状态:
- New:创建后尚未启动的线程处于这种状态,不会出现在Dump中。
- RUNNABLE:包括Running和Ready。线程开启start()方法,会进入该状态,在虚拟机内执行的。
- Waiting:无限的等待另一个线程的特定操作。
- Timed Waiting:有时限的等待另一个线程的特定操作。
- Blocked:在程序等待进入同步区域的时候,线程将进入这种状态,在等待监视器锁。
- Terminated:已终止线程的线程状态,线程已经结束执行。
Dump 文件的线程状态一般其实就以下3种:
- RUNNABLE,线程处于执行中
- BLOCKED,线程被阻塞
- WAITING,线程正在等待
常见 dump 内容
- locked <地址> 目标:申请对象锁成功,监视器的拥有者。
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at com.jiuqi.dna.core.internal.db.datasource.PooledConnection.prepareStatement
- waiting to lock <地址> 目标:申请对象锁未成功,在 Entry Set 等待。
// 一个线程锁住某对象,大量其他线程在该对象上等待:
"blocker" runnable
java.lang.Thread.State: RUNNABLE
at com.jiuqi.hcl.javadump.Blocker$1.run(Blocker.java:23)
- locked <0x00000000eb8eff68> (a java.lang.Object)
"blockee-11" waiting for monitor entry
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)
- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)
"blockee-86" waiting for monitor entry
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)
- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)
- waiting on <地址> 目标:释放对象锁,在等待区等待被唤醒。
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo
- locked <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingThread.run
2.2.2jstack分析cpu占用
cpu飙高分析流程:稍后写个case复现一把。
1.top 查看占用 CPU 较高的进程,可以发现 pid 为 21340 的进程 CPU 占用较高。
2.top -Hp pid 可以查看该进程下各个线程的 CPU 使用情况,可以发现线程21350的 CPU 占用较高
3.jstack -l pid >> dump.log 将线程堆栈信息保存下来
4.分析堆栈信息,thread dump 中,每个线程对应一个十六进制的 nid(native thread id),将 21350 转成十六进制 5366 然后 grep 查看相关信息即可:查看线程信息附近线程状态,重点关注Blocked
2.3jstat
jstat 是 JDK 自带的一个命令行工具,全称为 Java Virtual Machine statistics monitoring tool,可以用来监视和分析 Java 应用程序的内存使用和性能情况。jstat 命令可以显示有关 Java 堆和非堆内存使用情况、类加载、垃圾回收、线程和编译器等方面的信息。
jstat [ generalOption | outputOptions vmid [ interval[s|ms] [count] ] ]
参数说明:
generalOption:一般选项,用于设置 jstat 命令的全局行为。可以是以下选项之一:
-class:显示类加载情况。
-compiler:显示JIT编译器统计信息。
-gc:显示垃圾回收统计信息。
-gccapacity:显示垃圾回收堆内存使用情况。
-gcmetacapacity:显示垃圾回收非堆内存使用情况。
-gcnew:显示新生代垃圾回收统计信息。
-gcnewcapacity:显示新生代垃圾回收堆内存使用情况。
-gcold:显示老年代垃圾回收统计信息。
-gcoldcapacity:显示老年代垃圾回收堆内存使用情况。
-gcutil:显示垃圾回收堆内存使用情况总览。
-printcompilation:显示 JIT 编译器编译情况。
outputOptions:输出选项,用于设置 jstat 命令的输出格式。可以是以下选项之一:
-t:输出时间戳。
-h:输出帮助信息。
-J:输出完整的 JVM 内部信息。
vmid:Java 虚拟机进程 ID 或进程名。
interval:指定采样间隔时间,默认单位是毫秒。可以使用 s 或 ms 后缀指定单位,例如 10s 或 500ms。
count:指定采样次数,默认是无限次。
常用命令
jstat -class pid 显示加载class的数量,及所占空间等信息:
显示列名 | 具体描述 |
---|---|
Loaded | 装载的类的数量 |
Bytes | 装载类所占用的字节数 |
Unloaded | 卸载类的数量 |
Bytes | 卸载类的字节数 |
Time | 装载和卸载类所花费的时间 |
jstat -compiler pid 显示VM实时编译的数量等信息:
显示列名 | 具体描述 |
---|---|
Compiled | 编译任务执行数量 |
Failed | 编译任务执行失败数量 |
Invalid | 编译任务执行失效数量 |
Time | 编译任务消耗时间 |
FailedType | 最后一个编译失败任务的类型 |
FailedMethod | 最后一个编译失败任务所在的类及方法 |
jstat -gc pid 可以显示gc的信息,查看gc的次数,及时间:
显示列名 | 具体描述 |
---|---|
S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
S0U | 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) |
S1U | 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) |
EC | 年轻代中 Eden(伊甸园)的容量 (字节) |
EU | 年轻代中 Eden(伊甸园)目前已使用空间 (字节) |
OC | 老年代的容量 (字节) |
OU | 老年代目前已使用空间 (字节) |
PC | Perm(持久代)的容量 (字节) |
PU | Perm(持久代)目前已使用空间 (字节) |
YGC | 从应用程序启动到采样时年轻代中 gc 次数 |
YGCT | 从应用程序启动到采样时年轻代中 gc 所用时间(s) |
FGC | 从应用程序启动到采样时老年代(full gc) gc 次数 |
FGCT | 从应用程序启动到采样时老年代(full gc) gc 所用时间(s) |
GCT | 从应用程序启动到采样时 gc 用的总时间(s) |
jstat -gccapacity pid 显示 VM 内存中三代(young, old, perm)对象的使用和占用大小:
显示列名 | 具体描述 |
---|---|
NGCMN | 年轻代(young)中初始化(最小)的大小(字节) |
NGCMX | 年轻代(young)的最大容量 (字节) |
NGC | 年轻代(young)中当前的容量 (字节) |
S0C | 年轻代中第一个 survivor(幸存区)的容量 (字节) |
S1C | 年轻代中第二个 survivor(幸存区)的容量 (字节) |
EC | 年轻代中 Eden(伊甸园)的容量 (字节) |
OGCMN | 老年代中初始化(最小)的大小 (字节) |
OGCMX | 老年代的最大容量(字节) |
OGC | 老年代当前新生成的容量 (字节) |
OC | 老年代的容量 (字节) |
PGCMN | perm 代中初始化(最小)的大小 (字节) |
PGCMX | perm 代的最大容量 (字节) |
PGC | perm 代当前新生成的容量 (字节) |
PC | perm(持久代)的容量 (字节) |
YGC | 从应用程序启动到采样时年轻代中 gc 次数 |
FGC | 从应用程序启动到采样时老年代(full gc) gc 次数 |
jstat -gcutil pid 统计 gc 信息:
显示列名 | 具体描述 |
---|---|
S0 | 年轻代中第一个 survivor(幸存区)已使用的占当前容量百分比 |
S1 | 年轻代中第二个 survivor(幸存区)已使用的占当前容量百分比 |
E | 年轻代中 Eden(伊甸园)已使用的占当前容量百分比 |
O | 老年代已使用的占当前容量百分比 |
P | perm 代已使用的占当前容量百分比 |
YGC | 从应用程序启动到采样时年轻代中 gc 次数 |
YGCT | 从应用程序启动到采样时年轻代中 gc 所用时间(s) |
FGC | 从应用程序启动到采样时老年代(full gc) gc 次数 |
FGCT | 从应用程序启动到采样时老年代(full gc) gc 所用时间(s) |
GCT | 从应用程序启动到采样时 gc 用的总时间(s) |
jstat -gccause pid 1000 10 同gcutil 多了一个gc (打印10次,每次间隔1000ms)
2.4jmap和jcmd
jmap 是 JDK 自带的一个命令行工具,可以用于生成 Java Heap Dump 文件,以及查看 Java 进程中的内存使用情况。
jmap [option] <pid>
jmap [option] <executable (to connect to a core file)
jmap [option] [server_id@] (to connect to remote debug server)
option:命令选项,常用选项如下:
-heap:打印 Java 堆概要信息,包括使用的 GC 算法、堆配置参数和各代中堆内存使用情况;
-histo[:live]: 打印 Java 堆中对象直方图,通过该图可以获取每个 class 的对象数目,占用内存大小和类全名信息,带上 :live,则只统计活着的对象;
-permstat 打印永久代统计信息;
-finalizerinfo 打印等待回收的对象信息
-dump: 以 hprof 二进制格式将 Java 堆信息输出到文件内,该文件可以用 JProfiler、VisualVM 或 jhat 等工具查看;
dump-options 选项:
live 只输出活着的对象,不指定则输出堆中所有对象
format=b 指定输出格式为二进制
file= 指定文件名及文件存储位置,例如:jmap -dump:live,format=b,file=D:\heap.bin
-F 与-dump: 或 -histo 一起使用,当没有响应时,强制执行;注意:不支持live子选项
pid:进程id
- 查看大对象:
jmap -histo <pid>|less
- 查看对象数最多的对象,并按降序排序输出:
jmap -histo <pid>|grep 关键字|sort -k 2 -g -r|less
- 查看占用内存最多的对象,并按降序排序输出:
jmap -histo <pid>|grep 关键字|sort -k 3 -g -r|less
查看大对象jmap -histo pid
jmap -histo:live pid 统计 heap 中所有生存的对象的情况, 这个命令会先触发 gc 再统计:
jmap -dump:live,format=b,file=a.log pid
执行这个命令,JVM 会将整个 heap 的信息 dump 到一个文件,heap 如果比较大的话会导致这个过程比较耗时,并且执行的过程中为了保证 dump 的信息是可靠的会暂停应用。
该命令通常用来分析内存泄漏 OOM,通常做法是:
- 首先配置 JVM 启动参数,让 JVM 在遇到 OutOfMemoryError 时自动生成 Dump 文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path
- 使用命令 Dump Heap 信息
jmap -dump:format=b,file=/path/heap.bin pid
- 使用 MAT 分析工具,如 jhat 命令分析 hprof 文件
2.5jhat
jhat 全称为 Java Virtual Machine Heap Analysis Tool,即虚拟机堆转储快照分析工具。jhat 用于分析 heapdump 文件,它会建立一个HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果。 jhat 一般与 jmap 搭配使用,用于分析 jmap 生成的堆转储快照。jhat 是一个命令行工具,使用起来比较简便,但功能也相对简陋。如果条件允许的话,建议使用 JProfiler 或者 IBM HeapAnalyzer 等功能更强大的工具来分析 heapdump 文件。
jhat [options] heap-dump-file
option 具体选项及作用如下:
-J< flag >:因为 jhat 命令实际上会启动一个 JVM 来执行,通过 -J 可以在启动 JVM 时传入一些启动参数。例如,-J-Xmx512m 指定运行 jhat 的 JVM 使用的最大堆内存为 512 MB。 如果需要使用多个 JVM 启动参数,则传入多个 -Jxxxxxx。
-stack false|true:关闭跟踪对象分配调用堆栈。如果分配位置信息在堆转储中不可用,则必须将此标志设置为 false。默认值为 true。
-refs false|true:关闭对象引用跟踪。默认情况下,返回的指针是指向其他特定对象的对象,如反向链接或输入引用(referrers or incoming references),,会统计/计算堆中的所有对象。
-port port-number:设置 jhat HTTP server 的端口号,默认值 7000。
-exclude exclude-file:指定对象查询时需要排除的数据成员列表文件。 例如,如果文件列出了 java.lang.String.value,那么当从某个特定对象 Object o 计算可达的对象列表时,引用路径涉及 java.lang.String.value 的都会被排除。
-baseline exclude-file:指定一个基准堆转储(baseline heap dump)。 在两个 heap dump 文件中有相同 object ID 的对象会被标记为不是新的(marked as not being new),其他对象被标记为新的(new)。在比较两个不同的堆转储时很有用。
-debug int:设置 debug 级别,0 表示不输出调试信息。 值越大则表示输出更详细的 debug 信息。
-version:启动后只显示版本信息就退出。
2.6 jvisualvm
最后说jvisualvm,因为jvisualvm是对前面jps、jinfo、jstack、jstat、jmap、jhat的汇总,集上面之大成,并提供了可视化的界面;还可以监控远程Java服务;支持监控JMX。安装完插件比JConsole的功能还要完善。JVisualVM比JConsole更强大:支持对CPU、内存运行进行采样、配置。推荐用JVisualVM。
jconsole都不能保存连接信息,每次都要输入
安装插件
插件下载地址:VisualVM: Plugins Centers
也可以单个插件下载,离线安装
安装成功后的效果
JAVA Dump:
JAVA Dump就是虚拟机运行时的快照,将虚拟机运行时的状态和信息保存到文件中,包括:
线程dump:包含所有线程的运行状态,纯文本格式
堆dump:包含所有堆对象的状态,二进制格式