启动项目
测试接口
下面是接口方法,模拟了一个CPU密集型的任务。
192.168.101.101:8080/cpu/false
执行了CPU密集型的任务后,CPU瞬间拉满了。
访问接口,停止任务。
192.168.101.101:8080/cpu/true
任务停止后,CPU恢复。
监控JVM
在CPU爆满期间,我们监控一下JVM的状态。
使用top命令查看
top
命令可以显示当前系统中运行的进程的实时信息。top
命令是一个性能监视工具,用于显示系统的实时进程统计信息,包括CPU使用率、内存使用情况、运行时间等。
下面是对每一列的解释:
- PID:进程ID,这是系统中每个进程的唯一标识符。
- USER:运行该进程的用户。
- PR:进程的优先级(Nice值),数值越低,优先级越高。
- NI:Nice值,用于调整进程的优先级。
- VIRT:进程使用的虚拟内存大小,单位是KB。
- RES:进程使用的常驻内存大小,即进程占用的物理内存,单位是KB。
- SHR:进程使用的共享内存大小,单位是KB。
- S:进程的状态,其中"S"表示睡眠状态,"R"表示运行状态,"Z"表示僵死状态等。
- %CPU:进程占用的CPU百分比。
- %MEM:进程占用的内存百分比。
- TIME+:进程运行的时间,单位是分钟和秒。
- COMMAND:启动进程的命令。
通过top命令,可以看到一个Java进程(PID 3456)目前占用了256.8%的CPU和19.7%的内存,并且已经运行了6分钟16秒。
jps查看java进程
jps
(Java Process Status)是一个Java自带的命令行工具,用于列出正在运行的Java进程及其详细信息。它通常与JDK(Java Development Kit)一起安装,用于Java应用程序的管理和监控。
基本用法
jps
的基本用法非常简单,只需要在命令行中输入 jps
并按回车键即可。默认情况下,它会列出所有的Java进程,包括Java虚拟机(JVM)进程和运行中的Java应用程序。
可以看到当前系统中有两个Java进程正在运行:
- 第一个进程的进程ID是3456,它对应的应用是
jvm-better.jar
。 - 第二个进程的进程ID是62596,它对应的应用是
Jps
。这实际上是jps
命令自身的进程。当我们运行jps
命令时,它会在当前环境中启动一个Java进程来收集和显示其他Java进程的信息,因此会在jps
的输出中看到它自己的进程。
jinfo查看JVM参数配置
jinfo
是一个命令行工具,它用于查看和修改运行中的Java虚拟机(JVM)的配置参数。这个工具是jinfo
命令的缩写,全称是"Java Information",它是jvm-tools
的一部分,jvm-tools
是一套用于监控和管理Java虚拟机的工具集。
jinfo 3456
通过jinfo
得到了一下信息:
Java环境基本信息
- Java版本:
java.specification.version
: 17java.version
: 17java.version.date
: 2021-09-14java.runtime.version
: 17+35-2724
- JVM供应商:
java.vm.vendor
: Oracle Corporationjava.vendor
: Oracle Corporation
- JVM名称:
java.vm.name
: OpenJDK 64-Bit Server VM
- 操作系统:
os.name
: Linuxos.version
: 3.10.0-957.el7.x86_64os.arch
: amd64
- 用户信息:
user.name
: rootuser.home
: /rootuser.language
: zhuser.country
: CNuser.timezone
: Asia/Shanghai
Java运行时配置
- Java类路径:
java.class.path
: jvm-better.jar
- Java库路径:
java.library.path
: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib
- Java安装目录:
java.home
: /usr/local/jdk-17
- 启动器类型:
sun.java.launcher
: SUN_STANDARD
- 文件编码:
file.encoding
: UTF-8sun.jnu.encoding
: UTF-8native.encoding
: UTF-8
- 临时文件目录:
java.io.tmpdir
: /tmp
- 日志相关:
LOG_PATH
: /LOG_FILE
: JVM-better.logCONSOLE_LOG_CHARSET
: UTF-8FILE_LOG_CHARSET
: UTF-8LOGGED_APPLICATION_NAME
: [JVM-better]
JVM特定配置
- JVM参数:
sun.boot.library.path
: /usr/local/jdk-17/libsun.java.command
: jvm-better.jarjdk.debug
: releasesun.cpu.endian
: littlejava.vm.compressedOopsMode
: 32-bitjava.awt.headless
: truejava.protocol.handler.pkgs
: org.springframework.boot.loader.net.protocolsun.management.compiler
: HotSpot 64-Bit Tiered Compilerscatalina.useNaming
: falsecatalina.home
: /tmp/tomcat.8080.11096328371303728266catalina.base
: /tmp/tomcat.8080.11096328371303728266PID
: 3456java.vm.info
: mixed mode, sharingsun.io.unicode.encoding
: UnicodeLittlejava.class.version
: 61.0
JVM性能和内存管理相关
- VM Flags:
-XX:CICompilerCount=3
-XX:ConcGCThreads=1
-XX:G1ConcRefinementThreads=4
-XX:G1EagerReclaimRemSetThreshold=8
-XX:G1HeapRegionSize=1048576
-XX:GCDrainStackTargetSize=64
-XX:InitialHeapSize=62914560
-XX:MarkStackSize=4194304
-XX:MaxHeapSize=989855744
-XX:MaxNewSize=593494016
-XX:MinHeapDeltaBytes=1048576
-XX:MinHeapSize=8388608
-XX:NonNMethodCodeHeapSize=5832780
-XX:NonProfiledCodeHeapSize=122912730
-XX:ProfiledCodeHeapSize=122912730
-XX:ReservedCodeCacheSize=251658240
-XX:+SegmentedCodeCache
-XX:SoftMaxHeapSize=989855744
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseG1GC
JVM性能和内存管理相关
- 垃圾回收器设置:
-XX:+UseG1GC
: 使用G1垃圾回收器,这是一种面向服务器的垃圾回收器,适用于具有大内存需求的应用程序。-XX:G1HeapRegionSize=1048576
: 设置G1垃圾回收器的堆区域大小为1MB。-XX:InitialHeapSize=62914560
: 初始化堆大小设置为60MB。-XX:MaxHeapSize=989855744
: 最大堆大小设置为940MB。-XX:MaxNewSize=593494016
: 新生代的最大大小设置为560MB。-XX:ConcGCThreads=1
: 并发垃圾收集的线程数设置为1。
Java应用程序启动参数
- VM Arguments:
java_command
: jvm-better.jar,这是启动Java应用程序的命令。java_class_path (initial)
: jvm-better.jar,这是应用程序的初始类路径。
Tomcat相关配置
- Tomcat主目录:
catalina.home
: /tmp/tomcat.8080.11096328371303728266,这是Tomcat实例的主目录。catalina.base
: /tmp/tomcat.8080.11096328371303728266,这是Tomcat实例的基目录。
其他重要信息
- 编码和国际化:
sun.jnu.encoding
: UTF-8,这影响JVM处理文件时的编码方式。file.encoding
: UTF-8,这是文件的默认编码。user.language
和user.country
: zh-CN,这表示用户的语言和地区设置。user.timezone
: Asia/Shanghai,这是用户的时区设置。
- JVM特性:
java.vm.info
: mixed mode, sharing,这表示JVM以混合模式运行,并且支持代码共享。
- 调试和性能监控:
sun.management.compiler
: HotSpot 64-Bit Tiered Compilers,这是JVM使用的编译器。jdk.debug
: release,这表示JVM以发行版模式运行,而不是调试模式。
jstat 查看java进程内存分配
jstat
(Java Virtual Machine Statistics Monitoring Tool)是一个命令行工具,用于监控运行中的Java虚拟机(JVM)的各种统计信息。这个工具是jvm-tools
的一部分,可以用来监控JVM的垃圾回收(GC)、类加载、编译以及内存使用情况。 jstat
命令的基本格式是:
jstat [options] <pid> [interval] [count]
其中:
options
:表示要监控的统计信息类型,例如gc
、class
、compiler
等。pid
:Java进程的进程ID,通常是应用程序启动时生成的。interval
:监控间隔,表示在两次监控之间的时间间隔(毫秒)。count
:监控次数,表示执行多少次监控操作。
常用选项:
- -gc:监控垃圾回收统计信息。
- -class:监控类加载和卸载统计信息。
- -compiler:监控JIT编译统计信息。
- -printcompilation:监控编译操作统计信息。
- -gccause:监控垃圾回收原因统计信息。
jstat -gc 3456
下面是jstat -gc 3456
得到的结果分析:
S0C
(Survivor 0 Capacity): Survivor 0区的总容量,这里是0.0KB,可能表示Survivor 0区当前为空。S1C
(Survivor 1 Capacity): Survivor 1区的总容量,这里是1024.0KB。S0U
(Survivor 0 Used): Survivor 0区的已使用容量,这里是0.0KB。S1U
(Survivor 1 Used): Survivor 1区的已使用容量,这里是4.0KB。EC
(Eden Capacity): Eden区的总容量,这里是579584.0KB。EU
(Eden Used): Eden区的已使用容量,这里是0.0KB,表示Eden区当前没有正在使用的内存。OC
(Old Capacity): Old区的总容量,这里是342016.0KB。OU
(Old Used): Old区的已使用容量,这里是171003.2KB。MC
(Metaspace Capacity): 元数据空间的总容量,这里是31744.0KB。MU
(Metaspace Used): 元数据空间的已使用容量,这里是31253.9KB。CCSC
(Compressed Class Space Capacity): 压缩类空间的总容量,这里是4160.0KB。CCSU
(Compressed Class Space Used): 压缩类空间的已使用容量,这里是3945.4KB。YGC
(Young Generation GC Count): 新生代垃圾回收次数,这里是4274次。YGCT
(Young Generation GC Time): 新生代垃圾回收消耗的时间,这里是7.677秒。FGC
(Full GC Count): 完整垃圾回收(包括老年代和永久代或元空间)次数,这里是0次。FGCT
(Full GC Time): 完整垃圾回收消耗的时间,这里是0.000秒。CGC
(Concurrent GC Count): 并发垃圾回收次数,这里是2次。CGCT
(Concurrent GC Time): 并发垃圾回收消耗的时间,这里是0.004秒。GCT
(GC Total Time): 垃圾回收总时间,这里是7.681秒。
从这个输出中,我们可以看到Old区使用了大约50%的内存,而Eden区当前没有使用内存。同时,新生代垃圾回收发生了4274次,消耗了7.677秒,但没有发生完整垃圾回收。元数据空间使用了大约98%的容量。
jstack 打印线程的调用栈
jstack
(Java Stack Trace for Process ID)是一个命令行工具,用于生成给定Java进程ID或核心文件的Java线程堆栈跟踪。这个工具是jvm-tools
的一部分,可以用来查看Java虚拟机(JVM)中线程的调用栈,这对于调试多线程应用程序非常有用。
jstack
命令的基本格式是:
jstack [options] <pid> [threadId]
其中:
options
:表示要执行的操作,例如-F
强制打印堆栈信息,即使Java进程没有响应。pid
:Java进程的进程ID,通常是应用程序启动时生成的。threadId
:Java线程ID,可选参数,如果不指定,则打印所有线程的堆栈信息。
常用选项:
- -F:强制打印堆栈信息,即使Java进程没有响应。
- -h:设置线程堆栈的格式。
- -help:显示帮助信息。
jstack 3456
这两段线程转储信息展示了两个不同的线程在Java应用程序中的状态。这些线程是作为Tomcat的HTTP请求处理线程池的一部分。
线程 "http-nio-8080-exec-3" (ID 23)
- 状态:WAITING (parking)
- 等待条件:线程正在等待一个条件,这个条件是由
AbstractQueuedSynchronizer$ConditionObject
表示的。 - 线程栈:
- 线程在
Unsafe.park
方法中等待,这是一个 native 方法,用于挂起当前线程。 LockSupport.park
方法是线程等待的起点。AbstractQueuedSynchronizer$ConditionObject.await
方法表明线程正在等待一个条件变量。LinkedBlockingQueue.take
方法说明线程正在尝试从一个阻塞队列中获取一个元素,但没有可用的元素,因此线程进入等待状态。ThreadPoolExecutor.getTask
方法表明线程是Tomcat线程池中的一个工作线程,正在等待任务。ThreadPoolExecutor.runWorker
和ThreadPoolExecutor$Worker.run
方法是线程池执行任务的起点。
- 线程在
线程 "http-nio-8080-exec-4" (ID 24)
- 状态:WAITING (parking)
- 等待条件:与线程 "http-nio-8080-exec-3" 相同,线程正在等待一个条件。
- 线程栈: 线程栈信息与线程 "http-nio-8080-exec-3" 基本相同,表明这个线程也是Tomcat线程池中的工作线程,并且处于等待任务的状态。
分析总结
这两个线程都是Tomcat的HTTP请求处理线程池中的工作线程。它们当前处于等待状态,因为它们试图从一个空的阻塞队列中获取任务,但没有可用的任务。这通常发生在系统负载较低时,没有新的HTTP请求需要处理。当新的请求到达时,这些线程将被唤醒并开始处理请求。
这种状态是正常的,表明线程池中的线程在空闲等待工作。如果系统负载增加,这些线程将被用来处理新的请求。如果线程池中的线程长时间处于等待状态,可能需要调整线程池的大小以优化资源使用。
这段线程转储信息展示了Java应用程序中的一个线程"pool-2-thread-6"(ID 39)的当前状态。下面是对这个线程状态的分析:
线程 "pool-2-thread-6" (ID 39)
- 状态:RUNNABLE
- CPU时间:线程已经消耗了大约507秒的CPU时间。
- 线程栈:
- 线程当前在
com.cengxuyuan.jvmbetter.controller.PlayController
类的lambda$burnCPU$3
方法中执行,这是一个Lambda表达式,用于执行一个CPU密集型的任务。 Executors$RunnableAdapter.call
方法表明这个任务被包装成一个Runnable
适配器,这通常发生在使用ExecutorService
执行Callable
任务时。FutureTask.run
方法是执行任务的主体,FutureTask
是一个可取消的异步计算任务。ThreadPoolExecutor.runWorker
和ThreadPoolExecutor$Worker.run
方法是线程池执行任务的起点。Thread.run
方法是所有线程执行的最终入口点。
- 线程当前在
分析总结
这个线程是应用程序中一个线程池的一部分,目前正在执行一个CPU密集型的任务。由于线程状态为RUNNABLE,这意味着线程当前正在使用CPU,并且没有等待任何资源或条件。线程已经消耗了大量的CPU时间,这表明它可能在进行一些计算密集型的工作,比如循环计算或者其他需要大量CPU资源的操作。
top -H打印线程的CPU占用情况
top -p 3456 -H
top
:表示启动top
命令。-p
:指定要监控的进程ID,3456
是要监控的进程ID。-H
:表示以线程视图显示,这样可以查看每个进程中的线程及其资源使用情况。