JVM 性能分析 & 监控 & 内存/CPU过高分析

从这篇开始我们开始探讨一些jvm调优的问题。在jvm调优中一个离不开的重点是垃圾回收,当垃圾回收成为系统达到更高并发量的瓶颈时,我们就需要对jvm中如果进行“自动化”垃圾回收技术实施必要的监控和调节。

 

对于调优之前,我们必须要了解其运行原理,java 的垃圾收集Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。本文从:回收机制、监控、常见问题(cpu/内存高)定位及分析 三个放面进行分析:

 

一、回收机制

 

谁要被回收?

java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同是数据区域,这些区域有各自各自的用途。主要包含以下几个部分组成:

1、程序计数器

程序计数器占用的内存空间我们可以忽略不计,它是每个线程所执行的字节码的行号指示器。

 

2、虚拟机栈

java的虚拟机栈是线程私有的,生命周期和线程相同。它描述的是方法执行的内存模型。同时用于存储局部变量、操作数栈、动态链接、方法出口等。

 

3、本地方法栈

本地方法栈,类似虚拟机栈,它调用的是是native方法。

 

4、堆

堆是jvm中管理内存中最大一块。它是被共享,存放对象实例。也被称为“gc堆”。垃圾回收的主要管理区域。

 

5、方法区

方法区也是共享的内存区域。它主要存储已被虚拟机加载的类信息、常量、静态变量、即时编译器(jit)编译后的代码数据。

 

以上就是jvm在运行时期主要的内存组成,我们看到常见的内存使用不但存在于堆中,还会存在于其他区域,虽然堆的管理对程序的管理至关重要,但我们不能只局限于这一个区域,特别是当出现内存泄露的时候,我们除了要排查堆内存的情况,还得考虑虚拟机栈的以及方法区域的情况。

 

知道了要对谁以及那些区域进行内存管理,我还需要知道什么时候对这些区域进行垃圾回收。

 

什么时候回收?

在垃圾回收之前,我们必须确定的一件事就是对象是否存活?这就牵扯到了判断对象是否存活的算法了。

1、应用计数算法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器+1,当引用失效,计数器-1.任何时刻计数器为0的对象就是不可能再被使用的。

 

优点:实现简单,判定效率高效,被actionscript3和python中广泛应用。

 

缺点:无法解决对象之间的相互引用问题。java没有采纳

 

2、可达性分析算法

通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GCRoots没有任何引用链相连的时候,则证明此对象是不可用的。

 

比如如下,右侧的对象是到GCRoot时不可达的,可以判定为可回收对象。

 

 

在java中,可以作为GCRoot的对象包括以下几种:

* 虚拟机栈中引用的对象。

* 方法区中静态属性引用的对象。

* 方法区中常量引用的对象。

* 本地方法中JNI引用的对象。

 

基于以上,我们可以知道,当当前对象到GCRoot中不可达时候,即会满足被垃圾回收的可能。

 

那么是不是这些对象就非死不可,也不一定,此时只能宣判它们存在于一种“缓刑”的阶段,要真正的宣告一个对象死亡。至少要经历两次标记:

第一次:对象可达性分析之后,发现没有与GCRoots相连接,此时会被第一次标记并筛选。

 

第二次:对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,此时会被认定为没必要执行。

 

如何回收?

 

上述的两点讲解之后,我们大概明白了,哪些对象会被回收,以及回收的依据是什么,但回收的这个工作实现起来并不简单,首先它需要扫描所有的对象,鉴别谁能够被回收,其次在扫描期间需要 ”stop the world“ 对象能被冻结,不然你刚扫描,他的引用信息有变化,你就等于白做了。

 

分代回收

 

我们从一个object1来说明其在分代垃圾回收算法中的回收轨迹。

 

1、object1新建,出生于新生代的Eden区域。

2、minor GC,object1 还存活,移动到Fromsuvivor空间,此时还在新生代。

3、minor GC,object1 仍然存活,此时会通过复制算法,将object1移动到ToSuv区域,此时object1的年龄age+1。

4、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象并没有达到survivor的一半,所以此时通过复制算法,将fromSuv和Tosuv 区域进行互换,存活的对象被移动到了Tosuv。

5、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象已经达到survivor的一半以上(toSuv的区域已经满了),object1被移动到了老年代区域。

6、object1存活一段时间后,发现此时object1不可达GcRoots,而且此时老年代空间比率已经超过了阈值,触发了majorGC(也可以认为是fullGC,但具体需要垃圾收集器来联系),此时object1被回收了。fullGC会触发 stop the world。

 

在以上的新生代中,我们有提到对象的age,对象存活于survivor状态下,不会立即晋升为老生代对象,以避免给老生代造成过大的影响,它们必须要满足以下条件才可以晋升:

1、minor gc 之后,存活于survivor 区域的对象的age会+1,当超过(默认)15的时候,转移到老年代。

 

2、动态对象,如果survivor空间中相同年龄所有的对象大小的综合和大于survivor空间的一半,年级大于或等于该年级的对象就可以直接进入老年代。

 

以上采用分代垃圾收集的思想,对一个对象从存活到死亡所经历的历程。期间,在新生代的时刻,会用到复制算法,在老年代时,有可能会用到标记-清楚算法(mark-sweep)算法或者标记-整理算法,这些都是垃圾回收算法基于不同区域的实现,我们看下这几种回收算法的实现原理。

 

垃圾回收算法

 

1、标记清除法(Mark-Sweep)

 

标记清除法是垃圾回收算法的思想基础。标记清除算法将垃圾分为两个阶段:标记阶段和清除阶段。

 

标记阶段,通过根节点,标记所有从根节点开始的可达对象,未标记过的对象就是未被引用的垃圾对象。

 

清除阶段,清除所有未被标记的对象。

  

 

2、复制算法(copying)

 

复制算法是,将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在适用的内存中存活对象复制到未使用的内存块,然后清除使用的内存块中所有的对象。

 

3、标记压缩算法(Mark-Compact)

 

标记压缩算法是一种老年代的回收算法。

标记阶段和标记清除算法一致,对可达对象做一次标记。

清理阶段,为了避免内存碎片产生,将所有的存活对象压缩到内存的一端。

 

4、垃圾收集器

 

垃圾收集器是内存回收的具体实现,不同的厂商提供的垃圾收集器有很大的差别,一般的垃圾收集器都会作用于不同的分代,需要搭配使用。以下是各种垃圾收集器的组合方式:

二、监控

命令行工具

在你所下载的jdk包下面的bin目录下,有非常多的exe文件,接下来要看的工具就在其中。

不难发现大部分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

 
  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时间合计等信息

各个字段详细说明:

类加载

-class  监视类装载、卸载数量、总空间以及类装载所耗费的时间

每个字段说明:

 
  1. Loaded: 加载class的数量

  2. Bytes:所占用空间大小

  3. Unloaded:未加载数量

  4. Bytes: 未加载占用空间

  5. Time:时间

垃圾收集

-gc  监视Java堆状况,包括Eden区、两个Survivor区、老年代等容量、已用空间、GC时间合计等信息

每个字段说明:

 
  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编译器编译过的方法、耗时等信息

每个字段说明:

 
  1. Compiled:编译数量。

  2. Failed:失败数量

  3. Invalid:不可用数量

  4. Time:时间

  5. FailedType:失败类型

  6. FailedMethod:失败的方法

堆内存

-gccapacity  监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间

每个字段说明:

 
  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基本相同,主要输出使用到的最大、最小空间

每个字段说明:

 
  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状况

每个字段说明:

 
  1. MC:方法区大小

  2. MU:方法区使用大小

  3. CCSC:压缩类空间大小

  4. CCSU:压缩类空间使用大小

  5. OC:老年代大小

  6. OU:老年代使用大小

  7. YGC:年轻代垃圾回收次数

  8. FGC:老年代垃圾回收次数

  9. FGCT:老年代垃圾回收消耗时间

  10. GCT:垃圾回收消耗总时间

老年代内存

-gcoldcapacity 监视内容与-gcold基本相同,主要输出使用到的最大、最小空间

每个字段说明:

 
  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 统计所有垃圾回收的数据

每个字段说明:

 
  1. S0:幸存1区当前使用比例

  2. S1:幸存2区当前使用比例

  3. E:伊甸园区使用比例

  4. O:老年代使用比例

  5. M:元数据区使用比例

  6. CCS:压缩使用比例

  7. YGC:年轻代垃圾回收次数

  8. FGC:老年代垃圾回收次数

  9. FGCT:老年代垃圾回收消耗时间

  10. GCT:垃圾回收消耗总时间

编译方法

-printcompilation  输出已经被JIT编译的方法

每个字段说明:

 
  1. Compiled:最近编译方法的数量

  2. Size:最近编译方法的字节码数量

  3. Type:最近编译方法的编译类型。

  4. Method:方法名标识。

3.JINFO:CONFIGURATION INFO FOR JAVA

Java配置信息工具,能实时地查看和调整虚拟机各项参数。

命令格式:

jinfo [option] pid

说明:如果不传入参数,则输出该进程的所有配置信息

4.JMAP:MEMORY MAP FOR JAVA

Java内存映像工具,**用于生成堆存储快照(一般称为heapdump或dump文件),还可以查询finalize执行队列、Java堆的详细信息,如空间使用率、当前用的是哪种收集器等。**如果不适用jmap命令,可以使用-XX:+HeapDumpOnOutOfMemoryError参数,当虚拟机发生内存溢出的时候可以产生快照。或者使用kill -3 pid也可以产生

jmap命令格式:

jmap [option] vmid

参数说明:

 
  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

 

通常情况下,如果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

 

6.2、获取到这个进程下面所有线程,通过查看%CPU找到最耗费CPU的是线程PID(shift+p 按cpu排序,shift+m 按内存排序)

  •  
top -Hp 84253

6.3、线程号转换成对应的16进制PID

  •  
printf '%x\n' 84303

 

6.4、使用jstack 获取对应的线程信息

  •  
jstack 84253 | grep -A 10 1494f

可视化工具

JDK除了提供上面所讲的几个强大命令行工具外,还提供了一些可视化工具,可以方便的实时监控正在运行的Java程序。主要是两个:JConsole和VisualVM

1.JCONSOLE

点击bin目录下的jconsole.exe即可运行工具,可选两种监控对象,一个是本地程序一个是远程程序,如果要监控远程程序需要开启JMX管理,详细步骤因为篇幅原因就不说了,可以自行百度或谷歌。该程序因为是可视化工具,内容比较简洁直观,而且功能和上面的工具基本相同,只是把数据编程图标展示了,所以这里就不赘述,放截图就好啦。

2.VISUAL VM

点击bin目录下的jvisualvm.exe即可运行工具,jvisualvm同jconsole都是一个基于图形化界面的、可以查看本地及远程的JAVA GUI监控工具,Jvisualvm同jconsole的使用方式一样,jvisualvm界面更美观一些,数据更实时。

线程状态:

NEW:未启动的。不会出现在Dump中。

RUNNABLE:在虚拟机内执行的。运行中状态,可能里面还能看到locked字样,表明它获得了某把锁。

BLOCKED:受阻塞并等待监视器锁。被某个锁(synchronizers)給block住了。

WATING:无限期等待另一个线程执行特定操作。等待某个condition或monitor发生,一般停留在park(), wait(), sleep(),join() 等语句里。

TIMED_WATING:有时限的等待另一个线程的特定操作。和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)。 TERMINATED,已退出的。

三、常见问题刨析:

在日常对基础服务开发及维护过程中,一个业务场景往往会涉及到很多服务间的相互调用,对服务本身封装和设计的程度直接影响到服务的性能,决定了用户体验;那么该如何保障基础服务可用并提升服务性能呢?下面从实战中提炼两个典型的案例分享给大家。

>>>>

内存问题案例

 

问题现象:

内存使用率超过95%后告警

问题分析:

stage 1. 通过分析堆栈初步定位问题

堆栈中显示以 pic-upload-thread- 开头的线程CPU使用率交高,此类线程是图片上传异步线程池中的线程,那么是不是说在图片异步

传过程中new了大量大对象堆栈溢出导致的这个问题呢?

处理方式

在没有dump全部堆栈信息的情况下,我对线程池和具体执行类进行了优化,线程池增大了线程数量,具体执行类中对对象的引用使用了单例,对物理内存进行了扩容,由原来的8G扩大到10G,jvm堆栈由4G扩大到了6G。

处理效果

经过一段时间的观察后发现,然并卵(然而并没有什么卵用)!我决定dump下全部堆栈信息,进行分析。

stage 2. MAT分析堆栈信息,找出罪魁祸首

经过分析得出,占用内存较大的对象均为CMC服务相关对象

 

代码分析

当我们获取 cityname 时会调用cmc服务 dispLocaService 中的GetDispLocalByID 接口,此接口会封装调用 cmcpCachessService,如下图:

其中,涉及到MAT中占用内容较大的几个对象,经过服务管理平台得知:

房本WF服务以 10/s 的频率请求CMCPC服务,获取展现信息,并持久化到客户端本地:

调用cmc服务获取展现信息的 scheduler 由 Initializer.initCache() 触发,经询问得知,cmc提供两种方式获取展现信息,一种为带本地缓存方式,即目前项目使用的方式;另一种为不带缓存的方式获取。

  • 处理方案

经与cmc同事沟通后,我将获取展现信息的接口更改为不带缓存的服务,并周期性的采集了更换前与更换后的堆栈信息,更换前后对比:

在观察一段时间后,内存使用率仍有升高,去掉了cmc服务cache后内存使用率的确下降了10%,但是这个红框框让我觉得事情另有蹊跷。机智的我在发现内存有上升趋势的时候立刻再次登陆服务器dump了堆栈信息分析发现:

图中红框的对象为图片上传时传输对象,对象中存在图片Base64编码字符串;最终优化方案为增加新生代内存空间,由1G扩大到2G,优化代码,主动释放对象引用,目前内存使用率由优化前的70%左右下降到目前的35%左右。

总结:

在服务出现内存问题时,服务一定要做好监控和预警,第一时间dump下堆栈信息,使用常用的分析工具,本文中使用MAT工具,可以直观的感知道服务的内存使用情况,在尚不确定问题原因时,最好做到周期性的采集堆栈信息进行对比,在不断的优化过程中观察效果。

 

>>>>

CPU使用率过高案例

 

问题现象:

服务CPU使用率过高告警

问题分析:

stage 1. 通过分析堆栈初步定位问题

通过 show-busiest-java-threads.sh 堆栈分析出消耗CPU资源线程并降序输出, 结合top命令:

分析出该线程池中存在大量消耗CUP的http阻塞线程,结合代码分析及I/O监控:

得出结论:

此场景为图片下载,文件存储客户端采用分片传输方式,将文件下载到本地,告警由于网络及传输数据所致,当网络带宽有线,传输数据较大时出现此情况。

  • 处理方式

对传输文件进行压缩处理,增加服务器网络带宽及硬件配置。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值