JVM 性能分析 & 监控 & 内存/CPU过高分析 红太狼 2022-10-07 10:49 430阅读 0赞 从这篇开始我们开始探讨一些jvm调优的问题。在jvm调优中一个离不开的重点是垃圾回收,当垃圾回收成为系统达到更高并发量的瓶颈时,我们就需要对jvm中如果进行“自动化”垃圾回收技术实施必要的监控和调节。 对于调优之前,我们必须要了解其运行原理,java 的垃圾收集Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。本文从:回收机制、监控、常见问题(cpu/内存高)定位及分析 三个放面进行分析: **一、回收机制** **谁要被回收?** java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同是数据区域,这些区域有各自各自的用途。主要包含以下几个部分组成: ![33e715000fc5226598c1b878aed6444b.png][] 1、程序计数器 程序计数器占用的内存空间我们可以忽略不计,它是每个线程所执行的字节码的行号指示器。 2、虚拟机栈 java的虚拟机栈是线程私有的,生命周期和线程相同。它描述的是方法执行的内存模型。同时用于存储局部变量、操作数栈、动态链接、方法出口等。 3、本地方法栈 本地方法栈,类似虚拟机栈,它调用的是是native方法。 4、堆 堆是jvm中管理内存中最大一块。它是被共享,存放对象实例。也被称为“gc堆”。垃圾回收的主要管理区域。 5、方法区 方法区也是共享的内存区域。它主要存储已被虚拟机加载的类信息、常量、静态变量、即时编译器(jit)编译后的代码数据。 以上就是jvm在运行时期主要的内存组成,我们看到常见的内存使用不但存在于堆中,还会存在于其他区域,虽然堆的管理对程序的管理至关重要,但我们不能只局限于这一个区域,特别是当出现内存泄露的时候,我们除了要排查堆内存的情况,还得考虑虚拟机栈的以及方法区域的情况。 知道了要对谁以及那些区域进行内存管理,我还需要知道什么时候对这些区域进行垃圾回收。 **什么时候回收?** 在垃圾回收之前,我们必须确定的一件事就是对象是否存活?这就牵扯到了判断对象是否存活的算法了。 1、应用计数算法 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器+1,当引用失效,计数器-1.任何时刻计数器为0的对象就是不可能再被使用的。 优点:实现简单,判定效率高效,被actionscript3和python中广泛应用。 缺点:无法解决对象之间的相互引用问题。java没有采纳 2、可达性分析算法 通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GCRoots没有任何引用链相连的时候,则证明此对象是不可用的。 比如如下,右侧的对象是到GCRoot时不可达的,可以判定为可回收对象。 ![ce6462eda0251a72e06a39c6c07eeb89.png][] 在java中,可以作为GCRoot的对象包括以下几种: \* 虚拟机栈中引用的对象。 \* 方法区中静态属性引用的对象。 \* 方法区中常量引用的对象。 \* 本地方法中JNI引用的对象。 基于以上,我们可以知道,当当前对象到GCRoot中不可达时候,即会满足被垃圾回收的可能。 那么是不是这些对象就非死不可,也不一定,此时只能宣判它们存在于一种“缓刑”的阶段,要真正的宣告一个对象死亡。至少要经历两次标记: 第一次:对象可达性分析之后,发现没有与GCRoots相连接,此时会被第一次标记并筛选。 第二次:对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,此时会被认定为没必要执行。 **如何回收?** 上述的两点讲解之后,我们大概明白了,哪些对象会被回收,以及回收的依据是什么,但回收的这个工作实现起来并不简单,首先它需要扫描所有的对象,鉴别谁能够被回收,其次在扫描期间需要 ”stop the world“ 对象能被冻结,不然你刚扫描,他的引用信息有变化,你就等于白做了。 分代回收 我们从一个object1来说明其在分代垃圾回收算法中的回收轨迹。 1、object1新建,出生于新生代的Eden区域。 ![52c9aa259246653330c96fdac6c00b88.png][] 2、minor GC,object1 还存活,移动到Fromsuvivor空间,此时还在新生代。 ![220bde1641e6d7525915a854091ede9c.png][] 3、minor GC,object1 仍然存活,此时会通过复制算法,将object1移动到ToSuv区域,此时object1的年龄age+1。 ![87f925715132a7321bc168703125f0ca.png][] 4、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象并没有达到survivor的一半,所以此时通过复制算法,将fromSuv和Tosuv 区域进行互换,存活的对象被移动到了Tosuv。 ![f357b23972ac280ac7e527aa0b009f93.png][] 5、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象已经达到survivor的一半以上(toSuv的区域已经满了),object1被移动到了老年代区域。 ![b6d84ee04f88040b5a10f68a58d9db14.png][] 6、object1存活一段时间后,发现此时object1不可达GcRoots,而且此时老年代空间比率已经超过了阈值,触发了majorGC(也可以认为是fullGC,但具体需要垃圾收集器来联系),此时object1被回收了。fullGC会触发 stop the world。 ![3a8a5d42ed62aee8ad9147e1acb8bc25.png][] 在以上的新生代中,我们有提到对象的age,对象存活于survivor状态下,不会立即晋升为老生代对象,以避免给老生代造成过大的影响,它们必须要满足以下条件才可以晋升: 1、minor gc 之后,存活于survivor 区域的对象的age会+1,当超过(默认)15的时候,转移到老年代。 2、动态对象,如果survivor空间中相同年龄所有的对象大小的综合和大于survivor空间的一半,年级大于或等于该年级的对象就可以直接进入老年代。 以上采用分代垃圾收集的思想,对一个对象从存活到死亡所经历的历程。期间,在新生代的时刻,会用到复制算法,在老年代时,有可能会用到标记-清楚算法(mark-sweep)算法或者标记-整理算法,这些都是垃圾回收算法基于不同区域的实现,我们看下这几种回收算法的实现原理。 **垃圾回收算法** 1、标记清除法(Mark-Sweep) 标记清除法是垃圾回收算法的思想基础。标记清除算法将垃圾分为两个阶段:标记阶段和清除阶段。 标记阶段,通过根节点,标记所有从根节点开始的可达对象,未标记过的对象就是未被引用的垃圾对象。 清除阶段,清除所有未被标记的对象。 ![621cf961e9ff9693e5e6db30e3c60236.png][] 2、复制算法(copying) 复制算法是,将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在适用的内存中存活对象复制到未使用的内存块,然后清除使用的内存块中所有的对象。 ![7f65a2be8753c10bb8a4bb505a2c8712.png][] 3、标记压缩算法(Mark-Compact) 标记压缩算法是一种老年代的回收算法。 标记阶段和标记清除算法一致,对可达对象做一次标记。 清理阶段,为了避免内存碎片产生,将所有的存活对象压缩到内存的一端。 ![a83290934ac2f88c2675ef6189573001.png][] 4、垃圾收集器 垃圾收集器是内存回收的具体实现,不同的厂商提供的垃圾收集器有很大的差别,一般的垃圾收集器都会作用于不同的分代,需要搭配使用。以下是各种垃圾收集器的组合方式: ![330175202ea3461583171f0d0a59fa74.png][] **二、监控** # 命令行工具 # 在你所下载的jdk包下面的bin目录下,有非常多的exe文件,接下来要看的工具就在其中。 ![85863b21a1490a52380db7249d0d4e95.png][] 不难发现大部分exe文件都是在17KB左右,其实这并不是巧合,而是这些工具程序都是调用的jsk/lib/tools.jar类库下的函数,仅仅是对这个类库进行简单的封装,所以大小几乎都稳定在17KB左右。其实其他第三方监控工具也都是使用这个类库进行改造的,如果细细读其中的代码,你也可以自己写一个监控工具。 接下来的演示均是在本地演示,如果需要监控远程JVM的话,需要在启动时开启JMX管理功能(参数是:-Dcom.sun.management.jmxremote),当然还需要设置监控端口、用户名、密码、身份认证开关、SSL验证开关等,具体参数读者可自行百度或谷歌哦~ > 回答一个很多人都有的一个疑惑:系统只安装了一个jdk或jre,那么是多个程序共用一个虚拟机还是一个程序一个虚拟机?答案是一个程序一个jvm实例,你可以把它们的关系想象成类和对象的关系,jre就像是一个类,每运行一个程序时会实例化出一个jvm实例,同时根据参数为它分配内存空间。 ## 1.JPS:JVM PROCESS STATUS TOOL ## 虚拟机进程状况工具,\*\*可以列出正在运行的虚拟机进程,并显示虚拟机执行主类名称,以及这些进程的本地虚拟机唯一ID(LVMID)。\*\*这个应用非常多,一般用于查看有哪些运行在虚拟机上的程序,比如后面要讲的JConsle.exe在选择监控程序时就是用的jps,当然追根揭底还是调用的tools.jar中的方法。 如果你配置了jdk/bin下的环境变量,可直接在cmd中输入jps命令进行使用,否则你需要进入这个目录才能使用该工具。 命令格式: > jps \[ options \] \[ hostid \] 说明:如果不指定hostid就默认为当前主机或服务器,参数可以联合使用,比如*jps -lv*。 ![76d64f5f89894b769077950cae71f2fb.png][] 1. `-q 输出虚拟机唯一ID,不输出类名、Jar名和传入main方法的参数` 2. `-m 输出传入main方法的参数` 3. `-l 输出main类或Jar的全名` 4. `-v 输出虚拟机进程启动时JVM的参数` ## 2.JSTAT:JVM STATISTICS MONITORING TOOL ## 虚拟机统计信息监视工具,\*\*用于监视虚拟机各种运行状态信息的命令行工具,可以显示本地或远程虚拟机进程的类装载、内存、垃圾收集、JIT编译等运行数据。\*\*它是运行期定位虚拟机性能问题的首选工具。 命令格式: > jstat \[ option vmid \[interval \[s | ms\] \[count\] \] \] > > jstat \[-命令选项\] \[vmid\] \[间隔时间/毫秒\] \[查询次数\] jstat(JVM Statistics Monitoring Tool)是用于监控虚拟机各种运行状态信息的命令行工具 jstat -class pid:显示加载class的数量,及所占空间等信息。jstat -compiler pid:显示VM实时编译的数量等信息。jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。jstat -gcnew pid:new对象的信息。jstat -gcnewcapacity pid:new对象的信息及其占用量。jstat -gcold pid:old对象的信息。jstat -gcoldcapacity pid:old对象的信息及其占用量。jstat -gcpermcapacity pid: perm对象的信息及其占用量。jstat -gcutil pid:统计gc信息统计。jstat -printcompilation pid:当前VM执行的信息。 除了以上一个参数外,还可以同时加上 两个数字,如:jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次,还可以加上-h3每三行显示一下标题。 使用示例: **2.1、先找到tomcat对应的pid** * ps -ef | grep tomcat **2.2、执行** jstat -gcutil 28932 1000 20 S0 — Heap上的 Survivor space 0 区已使用空间的百分比 S1 — Heap上的 Survivor space 1 区已使用空间的百分比 E — Heap上的 Eden space 区已使用空间的百分比 O — Heap上的 Old space 区已使用空间的百分比 P — Perm space 区已使用空间的百分比 YGC — 从应用程序启动到采样时发生 Young GC 的次数 YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒) FGC — 从应用程序启动到采样时发生 Full GC 的次数 FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒) GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)-gc 监视Java堆状况,包括Eden区、两个Survivor区、老年代等容量、已用空间、GC时间合计等信息 ![e4aaaafb78a0375386bd3d4297246fa8.png][]各个字段详细说明: ### 类加载 ### -class 监视类装载、卸载数量、总空间以及类装载所耗费的时间 ![f4fb8ed2079ebecf2020cbb2a91189aa.png][] 每个字段说明: 1. `Loaded: 加载class的数量` 2. `Bytes:所占用空间大小` 3. `Unloaded:未加载数量` 4. `Bytes: 未加载占用空间` 5. `Time:时间` ### 垃圾收集 ### -gc 监视Java堆状况,包括Eden区、两个Survivor区、老年代等容量、已用空间、GC时间合计等信息 ![55504a831cf490ef687b198a01a50d7f.png][] 每个字段说明: 1. `S0C:第一个幸存区的大小` 2. `S1C:第二个幸存区的大小` 3. `S0U:第一个幸存区的使用大小` 4. `S1U:第二个幸存区的使用大小` 5. `EC:伊甸园区的大小` 6. `EU:伊甸园区的使用大小` 7. `OC:老年代大小` 8. `OU:老年代使用大小` 9. `MC:方法区大小` 10. `MU:方法区使用大小` 11. `CCSC:压缩类空间大小` 12. `CCSU:压缩类空间使用大小` 13. `YGC:年轻代垃圾回收次数` 14. `YGCT:年轻代垃圾回收消耗时间` 15. `FGC:老年代垃圾回收次数` 16. `FGCT:老年代垃圾回收消耗时间` 17. `GCT:垃圾回收消耗总时间` ### 编译 ### -compiler 输出JIT编译器编译过的方法、耗时等信息 ![d0deb82ebb2f8cf02d37cb22d2539c51.png][] 每个字段说明: 1. `Compiled:编译数量。` 2. `Failed:失败数量` 3. `Invalid:不可用数量` 4. `Time:时间` 5. `FailedType:失败类型` 6. `FailedMethod:失败的方法` ### 堆内存 ### -gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间 ![6427dfb6fdd24b2156b11eefb0e1c944.png][] 每个字段说明: 1. `NGCMN:新生代最小容量` 2. `NGCMX:新生代最大容量` 3. `NGC:当前新生代容量` 4. `S0C:第一个幸存区大小` 5. `S1C:第二个幸存区的大小` 6. `EC:伊甸园区的大小` 7. `OGCMN:老年代最小容量` 8. `OGCMX:老年代最大容量` 9. `OGC:当前老年代大小` 10. `OC:当前老年代大小` 11. `MCMN:最小元数据容量` 12. `MCMX:最大元数据容量` 13. `MC:当前元数据空间大小` 14. `CCSMN:最小压缩类空间大小` 15. `CCSMX:最大压缩类空间大小` 16. `CCSC:当前压缩类空间大小` 17. `YGC:年轻代gc次数` 18. `FGC:老年代GC次数` ### 新生代垃圾回收 ### -gcnew 监视新生代GC状况 每个字段说明: 1. `S0C:第一个幸存区大小` 2. `S1C:第二个幸存区的大小` 3. `S0U:第一个幸存区的使用大小` 4. `S1U:第二个幸存区的使用大小` 5. `TT:对象在新生代存活的次数` 6. `MTT:对象在新生代存活的最大次数` 7. `DSS:期望的幸存区大小` 8. `EC:伊甸园区的大小` 9. `EU:伊甸园区的使用大小` 10. `YGC:年轻代垃圾回收次数` 11. `YGCT:年轻代垃圾回收消耗时间` ### 新生代内存 ### -gcnewcapacity 监视内容与-gcnew基本相同,主要输出使用到的最大、最小空间 ![523534c98dab380ae44f3644bd81c4d4.png][] 每个字段说明: 1. `NGCMN:新生代最小容量` 2. `NGCMX:新生代最大容量` 3. `NGC:当前新生代容量` 4. `S0CMX:最大幸存1区大小` 5. `S0C:当前幸存1区大小` 6. `S1CMX:最大幸存2区大小` 7. `S1C:当前幸存2区大小` 8. `ECMX:最大伊甸园区大小` 9. `EC:当前伊甸园区大小` 10. `YGC:年轻代垃圾回收次数` 11. `FGC:老年代回收次数` ### 老年代垃圾回收 ### -gcold 监视老年代GC状况 ![07fcbdff5315aef4165185d4817d01bb.png][] 每个字段说明: 1. `MC:方法区大小` 2. `MU:方法区使用大小` 3. `CCSC:压缩类空间大小` 4. `CCSU:压缩类空间使用大小` 5. `OC:老年代大小` 6. `OU:老年代使用大小` 7. `YGC:年轻代垃圾回收次数` 8. `FGC:老年代垃圾回收次数` 9. `FGCT:老年代垃圾回收消耗时间` 10. `GCT:垃圾回收消耗总时间` ### 老年代内存 ### -gcoldcapacity 监视内容与-gcold基本相同,主要输出使用到的最大、最小空间 ![95c6f487db28db8d69909c579d8e9cc4.png][] 每个字段说明: 1. `OGCMN:老年代最小容量` 2. `OGCMX:老年代最大容量` 3. `OGC:当前老年代大小` 4. `OC:老年代大小` 5. `YGC:年轻代垃圾回收次数` 6. `FGC:老年代垃圾回收次数` 7. `FGCT:老年代垃圾回收消耗时间` 8. `GCT:垃圾回收消耗总时间` ### 元数据空间 ### -gcmetacapacity 监视元数据空间内存情况(jdk1.8之后) 每个字段说明: 1. `MCMN:最小元数据容量` 2. `MCMX:最大元数据容量` 3. `MC:当前元数据空间大小` 4. `CCSMN:最小压缩类空间大小` 5. `CCSMX:最大压缩类空间大小` 6. `CCSC:当前压缩类空间大小` 7. `YGC:年轻代垃圾回收次数` 8. `FGC:老年代垃圾回收次数` 9. `FGCT:老年代垃圾回收消耗时间` 10. `GCT:垃圾回收消耗总时间` ### 垃圾回收统计 ### -gcutil 统计所有垃圾回收的数据 ![7d6a217704c097ce390d05fa1bd15ec1.png][] 每个字段说明: 1. `S0:幸存1区当前使用比例` 2. `S1:幸存2区当前使用比例` 3. `E:伊甸园区使用比例` 4. `O:老年代使用比例` 5. `M:元数据区使用比例` 6. `CCS:压缩使用比例` 7. `YGC:年轻代垃圾回收次数` 8. `FGC:老年代垃圾回收次数` 9. `FGCT:老年代垃圾回收消耗时间` 10. `GCT:垃圾回收消耗总时间` ### 编译方法 ### -printcompilation 输出已经被JIT编译的方法 ![3519323735c8142d5c7c105ae3c67224.png][] 每个字段说明: 1. `Compiled:最近编译方法的数量` 2. `Size:最近编译方法的字节码数量` 3. `Type:最近编译方法的编译类型。` 4. `Method:方法名标识。` ## 3.JINFO:CONFIGURATION INFO FOR JAVA ## Java配置信息工具,**能实时地查看和调整虚拟机各项参数。** 命令格式: > jinfo \[option\] pid 说明:如果不传入参数,则输出该进程的所有配置信息 ![64f3172076b13bd9494270ca8b09be16.png][] ## 4.JMAP:MEMORY MAP FOR JAVA ## Java内存映像工具,\*\*用于生成堆存储快照(一般称为heapdump或dump文件),还可以查询finalize执行队列、Java堆的详细信息,如空间使用率、当前用的是哪种收集器等。\*\*如果不适用jmap命令,可以使用-XX:+HeapDumpOnOutOfMemoryError参数,当虚拟机发生内存溢出的时候可以产生快照。或者使用kill -3 pid也可以产生 jmap命令格式: > jmap \[option\] vmid ![3506a848d20ce16f944d2d64250f0cdf.png][] 参数说明: 1. `-dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.` 2. `-finalizerinfo 打印正等候回收的对象的信息.` 3. `-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.` 4. `-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.` 5. `-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.` 6. `-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效.` 7. `-h | -help 打印辅助信息` 8. `-J 传递参数给jmap启动的jvm.` * jmap -histo:live 28932 >> tmp.txt ![212317e046414f1fd48cfaaef2cf1b0d.png][] 通常情况下,如果Map或List或者某个自定义的对象排在第一位,则说明程序存在问题。上图中,就存在ConcurrentHashMap占用内存偏高的问题。 ## 5.JHAT:JVM HEAP ANALYSIS TOOL ## 虚拟机堆转储快照分析工具,可与jmap配合使用,\*\*这个工具是用来分析jmap dump出来的文件。\*\*由于这个工具功能比较简陋,运行起来也比较耗时,所以这个工具不推荐使用,推荐使用MAT或Visual VM。 例如我要解析上面生成的dump文件可以用命令: jhat vote.dump ## 6.JSTACK:STACK TRACE FOR JAVA ## Java堆栈跟踪工具,\*\*用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。\*\*线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,比如线程间死锁、死循环、请求外部资源导致的长时间等待等都是其常见原因。 jstack命令格式: > jstack \[option\] vmid 参数含义: 1. `-F 当正常输出的请求不被响应时,强制输出线程堆栈` 2. `-l 除堆栈外,显示关于锁的附加信息` 3. `-m 如果调用到本地方法的话,可以显示C/C++的堆栈` 6.1、查出最高cpu占用进程id PID * top -c ![308a10a651df8210453e103adf53a24d.png][] 6.2、获取到这个进程下面所有线程,通过查看%CPU找到最耗费CPU的是线程PID(shift+p 按cpu排序,shift+m 按内存排序) * top -Hp 84253 ![6ccc6928b3b76a6b93eca68cc16216d0.png][] 6.3、线程号转换成对应的16进制PID * printf '%x\n' 84303 ![1b54a9c6bbec993718098959e98c41f8.png][] 6.4、使用jstack 获取对应的线程信息 * jstack 84253 | grep -A 10 1494f ![488577959d947403bd5c367ce0d15fe5.png][] # 可视化工具 # JDK除了提供上面所讲的几个强大命令行工具外,还提供了一些可视化工具,可以方便的实时监控正在运行的Java程序。主要是两个:JConsole和VisualVM ## 1.JCONSOLE ## 点击bin目录下的jconsole.exe即可运行工具,可选两种监控对象,一个是本地程序一个是远程程序,如果要监控远程程序需要开启JMX管理,详细步骤因为篇幅原因就不说了,可以自行百度或谷歌。该程序因为是可视化工具,内容比较简洁直观,而且功能和上面的工具基本相同,只是把数据编程图标展示了,所以这里就不赘述,放截图就好啦。 ![1c9c10b7486b54155f89b36ad81e15cc.png][] ![d36ac363fa5d2df3b499d4b62000cad2.png][] ![65fe9071c0cfc3379380c455b52af964.png][] ![d666a05892390eec4a5811b2f2b9a3cf.png][] ![f58da8774e89cb65031a64091e5041c9.png][] ![5ada23d05995cb35ba421e2b37178d4e.png][] ## 2.VISUAL VM ## 点击bin目录下的jvisualvm.exe即可运行工具,jvisualvm同jconsole都是一个基于图形化界面的、可以查看本地及远程的JAVA GUI监控工具,Jvisualvm同jconsole的使用方式一样,jvisualvm界面更美观一些,数据更实时。 ![89731d7e8e9ba496a294abd6a636d428.png][] ![694fb5e012da5be53383d6c1d9e18340.png][] ![c2c96cedef5ffb2fdbe6935a2e0a6b1c.png][] **线程状态:** NEW:未启动的。不会出现在Dump中。 RUNNABLE:在虚拟机内执行的。运行中状态,可能里面还能看到locked字样,表明它获得了某把锁。 BLOCKED:受阻塞并等待监视器锁。被某个锁(synchronizers)給block住了。 WATING:无限期等待另一个线程执行特定操作。等待某个condition或monitor发生,一般停留在park(), wait(), sleep(),join() 等语句里。 TIMED\_WATING:有时限的等待另一个线程的特定操作。和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)。 TERMINATED,已退出的。 **三、常见问题刨析:** 在日常对基础服务开发及维护过程中,一个业务场景往往会涉及到很多服务间的相互调用,对服务本身封装和设计的程度直接影响到服务的性能,决定了用户体验;那么该如何保障基础服务可用并提升服务性能呢?下面从实战中提炼两个典型的案例分享给大家。 >>>> **内存问题案例** **问题现象:** 内存使用率超过95%后告警 **问题分析:** **stage 1. ****通过分析堆栈初步定位问题** ![51fd558bf1dd2569a3c311dc4d0dd0f1.png][]堆栈中显示以 pic-upload-thread- 开头的线程CPU使用率交高,此类线程是图片上传异步线程池中的线程,那么是不是说在图片异步 传过程中new了大量大对象堆栈溢出导致的这个问题呢? **处理方式** 在没有dump全部堆栈信息的情况下,我对线程池和具体执行类进行了优化,线程池增大了线程数量,具体执行类中对对象的引用使用了单例,对物理内存进行了扩容,由原来的8G扩大到10G,jvm堆栈由4G扩大到了6G。 **处理效果** 经过一段时间的观察后发现,然并卵(然而并没有什么卵用)!我决定dump下全部堆栈信息,进行分析。 **stage 2. ****MAT分析堆栈信息,找出罪魁祸首** 经过分析得出,占用内存较大的对象均为CMC服务相关对象 ![56fff10b7e3286788fdadf66b0784e37.png][] **代码分析** ![62179dff9c81b54ea64abf68e555ae90.png][] 当我们获取 cityname 时会调用cmc服务 dispLocaService 中的GetDispLocalByID 接口,此接口会封装调用 cmcpCachessService,如下图: ![62179dff9c81b54ea64abf68e555ae90.png][] 其中,涉及到MAT中占用内容较大的几个对象,经过服务管理平台得知: ![d5c29346435d485ebaf677ff70ea7fed.png][] 房本WF服务以 10/s 的频率请求CMCPC服务,获取展现信息,并持久化到客户端本地: ![655c027b8aeb0dfc0bfca9049eea7ca8.png][] 调用cmc服务获取展现信息的 scheduler 由 Initializer.initCache() 触发,经询问得知,cmc提供两种方式获取展现信息,一种为带本地缓存方式,即目前项目使用的方式;另一种为不带缓存的方式获取。 * **处理方案** 经与cmc同事沟通后,我将获取展现信息的接口更改为不带缓存的服务,并周期性的采集了更换前与更换后的堆栈信息,更换前后对比: ![265bad6b8c862e1dbe2d34d1ceea14e4.png][] 在观察一段时间后,内存使用率仍有升高,去掉了cmc服务cache后内存使用率的确下降了10%,但是这个红框框让我觉得事情另有蹊跷。机智的我在发现内存有上升趋势的时候立刻再次登陆服务器dump了堆栈信息分析发现: ![44f852a9641130f978839ed43140e145.png][] 图中红框的对象为图片上传时传输对象,对象中存在图片Base64编码字符串;最终优化方案为增加新生代内存空间,由1G扩大到2G,优化代码,主动释放对象引用,目前内存使用率由优化前的**70%**左右下降到目前的**35%**左右。 ![d58600ed588819b57d091f70c4c11b84.png][] **总结:** 在服务出现内存问题时,服务一定要做好监控和预警,第一时间dump下堆栈信息,使用常用的分析工具,本文中使用MAT工具,可以直观的感知道服务的内存使用情况,在尚不确定问题原因时,最好做到周期性的采集堆栈信息进行对比,在不断的优化过程中观察效果。 >>>> **CPU使用率过高案例** **问题现象:** 服务CPU使用率过高告警 **问题分析:** **stage 1. 通过分析堆栈初步定位问题** ![f49dc366ef663b212db9c17639dbddfa.png][] 通过 show-busiest-java-threads.sh 堆栈分析出消耗CPU资源线程并降序输出, 结合top命令: ![829dc29c71a8be5dd32c477dff299f88.png][] 分析出该线程池中存在大量消耗CUP的http阻塞线程,结合代码分析及I/O监控: ![acebd91ffbbe6f143bb278ec52f1023c.png][] **得出结论:** 此场景为图片下载,文件存储客户端采用分片传输方式,将文件下载到本地,告警由于网络及传输数据所致,当网络带宽有线,传输数据较大时出现此情况。 * **处理方式** 对传输文件进行压缩处理,增加服务器网络带宽及硬件配置。 [33e715000fc5226598c1b878aed6444b.png]: /images/20221005/ee536e5525a549f0ac9c2381cbd2dd43.png [ce6462eda0251a72e06a39c6c07eeb89.png]: /images/20221005/0254b2990a3f434785498ecff9a720d9.png [52c9aa259246653330c96fdac6c00b88.png]: /images/20221005/c3234c5a601e4918a19c9284097319f7.png [220bde1641e6d7525915a854091ede9c.png]: /images/20221005/27f85f709b87446bbe5995594b56c225.png [87f925715132a7321bc168703125f0ca.png]: /images/20221005/4e414cf442fe4418af42f04def0f84b3.png [f357b23972ac280ac7e527aa0b009f93.png]: /images/20221005/f13efe6a5d16485e8a0d4dc6fb074550.png [b6d84ee04f88040b5a10f68a58d9db14.png]: /images/20221005/ef8a1ac105b34608ae2f5a20a75676de.png [3a8a5d42ed62aee8ad9147e1acb8bc25.png]: /images/20221005/4cc785e7a204442d9a6a303a50ddc4a8.png [621cf961e9ff9693e5e6db30e3c60236.png]: /images/20221005/5a19b371ba024392982d06e0066e345d.png [7f65a2be8753c10bb8a4bb505a2c8712.png]: /images/20221005/47c228cc10a34a64ae8b9db921e11898.png [a83290934ac2f88c2675ef6189573001.png]: /images/20221005/f5fb0e48d604477dbe69019ba23b2f27.png [330175202ea3461583171f0d0a59fa74.png]: /images/20221005/16a68e18f3fa4389a85fde743cfe9ee0.png [85863b21a1490a52380db7249d0d4e95.png]: /images/20221005/beacc3f8dc55486dabd612a2727571f0.png [76d64f5f89894b769077950cae71f2fb.png]: /images/20221005/c7e250ce5127412481bc486b8ed6a7e9.png [e4aaaafb78a0375386bd3d4297246fa8.png]: /images/20221005/77802b42ad35493e9ef231e9ff62a56d.png [f4fb8ed2079ebecf2020cbb2a91189aa.png]: /images/20221005/15baf192ddea4612b79914634dae0179.png [55504a831cf490ef687b198a01a50d7f.png]: /images/20221005/864a900c9b4c419c843a049647b9f85e.png [d0deb82ebb2f8cf02d37cb22d2539c51.png]: /images/20221005/d553e87842f94618bcbe4b4b6e13a391.png [6427dfb6fdd24b2156b11eefb0e1c944.png]: /images/20221005/ea90da71462f4243bcfd8ccfa81954fb.png [523534c98dab380ae44f3644bd81c4d4.png]: /images/20221005/142dd7b40f4e466c95be8babc714f358.png [07fcbdff5315aef4165185d4817d01bb.png]: /images/20221005/b1d339e0ef7a4d10a904c1886e0629dd.png [95c6f487db28db8d69909c579d8e9cc4.png]: /images/20221005/3eb30e100b944e32affbaf8e9351f88b.png [7d6a217704c097ce390d05fa1bd15ec1.png]: /images/20221005/99845adee5934339bce4d2a084f9d308.png [3519323735c8142d5c7c105ae3c67224.png]: /images/20221005/505273a97236434a8f1efe86c0c390b2.png [64f3172076b13bd9494270ca8b09be16.png]: /images/20221005/acafa51ea97d420ea0a922e34be0b0a3.png [3506a848d20ce16f944d2d64250f0cdf.png]: /images/20221005/ae7863c3b741427682eb1555133afb39.png [212317e046414f1fd48cfaaef2cf1b0d.png]: /images/20221005/ea75fbbd0571432b9665ca25fc3f3e1d.png [308a10a651df8210453e103adf53a24d.png]: /images/20221005/854321d9f5f14da48a69276b8f1d32e1.png [6ccc6928b3b76a6b93eca68cc16216d0.png]: /images/20221005/998f23c4bb4240878cb03a2822a09cfd.png [1b54a9c6bbec993718098959e98c41f8.png]: /images/20221005/da1614b1843546ef9688eea8bca941a6.png [488577959d947403bd5c367ce0d15fe5.png]: /images/20221005/250226ff56b34c1bbb57d3cee3b537a5.png [1c9c10b7486b54155f89b36ad81e15cc.png]: /images/20221005/3e785b57162b48629d84d4bed4e6ef53.png [d36ac363fa5d2df3b499d4b62000cad2.png]: /images/20221005/0ea94bc1f72d42a59c4c098bde1e66c5.png [65fe9071c0cfc3379380c455b52af964.png]: /images/20221005/f9b76260806145bfbb231b637a2780ed.png [d666a05892390eec4a5811b2f2b9a3cf.png]: /images/20221005/04e58ffe60214b5e997ad53faedc3c16.png [f58da8774e89cb65031a64091e5041c9.png]: /images/20221005/ff94765df91e4a0db992fef27a8a2415.png [5ada23d05995cb35ba421e2b37178d4e.png]: /images/20221005/ad1c97deedfc44249a8e25948741eda2.png [89731d7e8e9ba496a294abd6a636d428.png]: /images/20221005/5059e898f82b4f73a32f279d7958dbfd.png [694fb5e012da5be53383d6c1d9e18340.png]: /images/20221005/6bdd0c1a798a4fb5a2a87c115eaae53b.png [c2c96cedef5ffb2fdbe6935a2e0a6b1c.png]: /images/20221005/7af0f718c3c644ab90ee592939da2d7c.png [51fd558bf1dd2569a3c311dc4d0dd0f1.png]: /images/20221005/dadaefbf2d354d37925101f1a1666636.png [56fff10b7e3286788fdadf66b0784e37.png]: /images/20221005/a4f14cc03dd44f3888d0c4c3aec90ab7.png [62179dff9c81b54ea64abf68e555ae90.png]: /images/20221005/e07df1be036d43bfac502f349f1e24ce.png [d5c29346435d485ebaf677ff70ea7fed.png]: /images/20221005/9a2897853e59403a8072b3753879790a.png [655c027b8aeb0dfc0bfca9049eea7ca8.png]: /images/20221005/3796c2c17b9d4556960a37fb6aaaf839.png [265bad6b8c862e1dbe2d34d1ceea14e4.png]: /images/20221005/d36b93902bb440edb6351cbf2ac822f9.png [44f852a9641130f978839ed43140e145.png]: /images/20221005/741cfe3b0c4c4f92979e343f661e64c1.png [d58600ed588819b57d091f70c4c11b84.png]: /images/20221005/5c077702063b4cc2920d679128cfad72.png [f49dc366ef663b212db9c17639dbddfa.png]: /images/20221005/1b34cd8c9fb742d4b9d62afe6062921c.png [829dc29c71a8be5dd32c477dff299f88.png]: /images/20221005/48a12d29c8bf4346bbbcee8c94ac8618.png [acebd91ffbbe6f143bb278ec52f1023c.png]: /images/20221005/92e48a73a55a488abbfb4b920878fadb.png
相关 性能瓶颈案例分析:Java CPU使用率过高问题 在Java开发中,CPU使用率过高可能源于以下几个常见的瓶颈案例: 1. **并发控制不当**: 当一个Java应用处理大量并发请求时,如果无恰当的锁机制(如Reent 浅浅的花香味﹌/ 2024年09月10日 18:39/ 0 赞/ 20 阅读
相关 Java性能调优:监控JVM、分析内存、CPU等问题 Java性能调优主要包括以下几个方面: 1. **JVM监控**: - 使用`jps`命令查看当前JVM的详细信息,包括正在运行的服务。 - 使用`jstat`命 红太狼/ 2024年09月04日 05:09/ 0 赞/ 15 阅读
相关 基于Linux系统的Tomcat内存泄露分析、CPU内存过高排查 Java内存,CPU占用过高排查 1.ps -ef | grep tomcat名字 -->拿到Tomcat进程的pid 2.jstack -l 进程pid >> 不念不忘少年蓝@/ 2023年08月17日 16:38/ 0 赞/ 106 阅读
相关 分析线上cpu或内存过高问题(参考) 1、top命令查询linux所有进程,占用cpu或内存情况 top ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_ 朱雀/ 2023年02月23日 05:02/ 0 赞/ 77 阅读
相关 JVM 性能分析 & 监控 & 内存/CPU过高分析 从这篇开始我们开始探讨一些jvm调优的问题。在jvm调优中一个离不开的重点是垃圾回收,当垃圾回收成为系统达到更高并发量的瓶颈时,我们就需要对jvm中如果进行“自动化”垃圾回收技 红太狼/ 2022年10月07日 10:49/ 0 赞/ 431 阅读
相关 linux内存使用率过高分析 1.top ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L 约定不等于承诺〃/ 2021年09月21日 06:40/ 0 赞/ 286 阅读
相关 springboot内存占用过高问题排查 - jvm内存使用分析 排查springboot内存占用过高问题 所需命令: ps命令:Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态 本是古典 何须时尚/ 2021年09月18日 06:14/ 1 赞/ 6019 阅读
相关 linux 分析 进程cpu占用过高 重点是查看进程的线程中,哪个线程占用cpu过高,然后用gdb附加到进程,调试线程,看是否有死循环或者死锁等问题,步骤如下: 先用ps + grep找出该死的进程pid,比如 心已赠人/ 2021年09月07日 06:16/ 0 赞/ 544 阅读
相关 top+jstack分析cpu过高原因 1、用ps -ef | grep tomcat-v3 查出tomcat运行的进程id ![这里写图片描述][20160711175137284] 2、用top -Hp 「爱情、让人受尽委屈。」/ 2021年06月11日 15:12/ 0 赞/ 446 阅读
还没有评论,来说两句吧...