首页 » SEO优化 » oqlphp技巧_JVM机能调优4机能调优对象

oqlphp技巧_JVM机能调优4机能调优对象

访客 2024-11-17 0

扫一扫用手机浏览

文章目录 [+]

JVM性能调优(1) —— JVM内存模型和类加载运行机制

JVM性能调优(2) —— 垃圾回收器和回收策略

oqlphp技巧_JVM机能调优4机能调优对象

JVM性能调优(3) —— 内存分配和垃圾回收调优

oqlphp技巧_JVM机能调优4机能调优对象
(图片来自网络侵删)

回到顶部

一、JDK工具

先来看看有哪些常用的工具可以赞助我们进行性能调优和问题排查,后面再通过一个详细的示例结合工具来剖析调优。

1、JDK工具

JDK自带了很多性能监控工具,我们可以用这些工具来监测系统和排查内存性能问题。

2、利用 jps 找出进程

jps(Java Virtual Machine Process Status Tool)是JDK 1.5供应的一个显示当前所有java进程pid的命令,大略实用,非常适宜在linux/unix平台上大略察看当前java进程的一些大略情形。

1)查看Java进程PID

【jps -l】左边一列便是Java进程的PID。

2)输出通报给JVM的参数

【jps -vl】

3、利用 jstat 查看VM统计信息

利用 jstat 工具可以监测 Java 运用程序的实时运行情形,可以看到VM内的Eden、Survivor、老年代的内存利用情形,还有 YoungGC 和 FullGC 的实行次数以及耗时。
通过这些指标,我们可以轻松的剖析出当前系统的运行情形,判断当前系统的内存利用压力以及GC压力,还有内存分配是否合理。

1)查看 jstat 有哪些操作

【jstat -options】

-class:显示 ClassLoad 的干系信息;-compiler:显示 JIT 编译的干系信息;-gc:显示和 gc 干系的堆信息;-gccapacity:显示各个代的容量以及利用情形;-gcmetacapacity:显示 Metaspace 的大小;-gcnew:显示新生代信息;-gcnewcapacity:显示新生代大小和利用情形;-gcold:显示老年代和永久代的信息;-gcoldcapacity :显示老年代的大小;-gcutil:显示垃圾网络信息;-gccause:显示垃圾回收的干系信息(同 -gcutil),同时显示末了一次或当前正在发生的垃圾回收的诱因;-printcompilation:输出 JIT 编译的方法信息

个中 jstat -gc 是最完全、最常用、最实用的命令,基本足够剖析jvm的运行情形了。

2)显示 ClassLoad 的干系信息

【jstat -class <pid>】

3)查看内存利用和GC情形

【jstat -gc <pid> [<interval> [<count>]】

S0C:年轻代中 To Survivor 的容量(单位 KB);S1C:年轻代中 From Survivor 的容量(单位 KB);S0U:年轻代中 To Survivor 目前已利用空间(单位 KB);S1U:年轻代中 From Survivor 目前已利用空间(单位 KB);EC:年轻代中 Eden 的容量(单位 KB);EU:年轻代中 Eden 目前已利用空间(单位 KB);OC:老年代的容量(单位 KB);OU:老年代目前已利用空间(单位 KB);MC:Metaspace 的容量(单位 KB);MU:Metaspace 目前已利用空间(单位 KB);CCSC:压缩类空间大小CCSU:压缩类空间利用大小YGC:从运用程序启动到采样时年轻代中 gc 次数;YGCT:从运用程序启动到采样时年轻代中 gc 所用韶光 (s);FGC:从运用程序启动到采样时 old 代(全 gc)gc 次数;FGCT:从运用程序启动到采样时 old 代(全 gc)gc 所用韶光 (s);GCT:从运用程序启动到采样时 gc 用的总韶光 (s)

4)查看垃圾回收统计

【jstat -gcutil <pid> [<interval> [<count>]】

S0:Survivor0 区占用百分比S1:Survivor1 区占用百分比E:Eden 区占用百分比O:老年代占用百分比M:元数据区占用百分比YGC:年轻代回收次数YGCT:年轻代回收耗时FGC:老年代回收次数FGCT:老年代回收耗时GCT:GC总耗时4、利用 jmap 查看工具分布情形

利用 jmap 可查看堆内存初始化配置信息以及堆内存的利用情形,输出堆内存中的工具信息,包括产生了哪些工具,工具数量多少等。

1)查看堆内存情形

【jmap -heap <PID>】

这个命令会打印出堆内存干系的一些参数设置以及各个区域的情形,要查看这些信息一样平常利用 jstat 命令就足够了。

2)查看系统运行时工具分布

【jmap -histo[:live] <PID>】带上 live 则只统计活工具

这个命令会按照各种工具占用内存空间的大小降序排列,把占用内存最多的工具放在最上面。
通过这个命令可以大略的理解下当前jvm中的工具对内存占用的情形以及当前内存里到底是哪个工具占用了大量的内存空间。

3)天生堆内存转储快照

【jmap -dump:format=b,file=<path> <pid>】

【jmap -dump:live,format=b,file=<path> <pid>】

jmap -dump 是输出堆中所有工具;jmap -dump:live 是输出堆中所有活着的工具,而且 jmap -dump:live 会触发 FullGC,线上利用要把稳。
format=b 因此二进制格式输出;file 是文件路径,格式为 hrpof 后缀。

这个命令会在当前目录下天生一个 dump.hrpof 文件,这是个二进制的格式,无法直接打开,可以利用MAT等工具来剖析。
这个命令把这一时候VM堆内存里所有工具的快照放到文件里去了,供你后续去剖析。

5、利用 jstack 剖析线程栈

jstack 是一种线程堆栈剖析工具,最常用的功能便是利用 jstack pid 命令查看线程的堆栈信息,常日会结合 top -Hp pid 或 pidstat -p pid -t 一起查看详细线程的状态,也常常用来排查一些去世锁的非常、CPU占用高的线程等。

1)jstack参数

-l:长列表. 打印关于锁的附加信息,例如属于 java.util.concurrent 的 ownable synchronizers 列表。
-F:当 jstack [-l] pid 没有相应的时候逼迫打印栈信息-m:打印 java 和 native c/c++ 框架的所有栈信息.-h | -help:打印帮助信息

2)查看线程堆栈信息

【jstack <pid> > stack.log】

这个命令可以把程序的线程堆栈dump下来。
每个线程堆栈的信息中,都可以查看到线程 ID、线程状态(wait、sleep、running 等状态)以及是否持有锁等。

pool-11-thread-6:线程名称#1920:线程编号prio=5:线程的优先级别os_prio=0:系统级别的线程优先级tid=0x00007f87e028c000:线程IDnid=0x6724:native线程的id,通过 printf "%x\n" <pid> 命令转换线程IDwaiting on condition [0x00007f87b97d2000]:线程当前的状态

回到顶部

二、Linux 命令行工具1、top 命令

top 命令是我们在 Linux 下最常用的命令之一,它可以实时显斧正在实行进程的 CPU 利用率、内存利用率以及系统负载等信息。
个中上半部分显示的是系统的统计信息,下半部分显示的是进程的利用率统计信息。

看第一行:紧张展示了CPU的负载情形

23:22:23:指的是当前韶光up 12 days, 12::18:指的是机器已经运行了多永劫光1 user:当前机器有一个用户在利用load average: 0.19, 0.27, 0.30:指 CPU 在1分钟、5分钟、15分钟内的负载情形。

最主要的便是看 load average,比如机器是4核CPU,那么 0.19、0.27、0.30,解释4核中连一个核都没用满,4核CPU基本很空闲。
如果CPU负载是1,解释有1个核被利用的比较繁忙了。
如果负载是4,解释4核CPU都跑满了;如果超过4,解释4核CPU被繁忙的利用还不足处理当前的任务,很多进程可能一贯在等待CPU去实行自己的任务。

② 查看详细线程利用系统资源情形

2、vmstat 命令

vmstat 是 Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存、进程、CPU活动进行监控。

命令格式:【vmstat [ 选项 ] [ <韶光间隔> ] [ <次数> ]】

字段解释:

Procs(进程):r:等待运行的进程数b:处于非中断就寝状态的进程数Memory(内存,单位Kb):swpd:虚拟内存利用情形free:空闲的内存buff:用来作为缓冲的内存数cache:用作缓存的内存大小Swap(交流区):si:从磁盘交流到内存的交流页数量so:从内存交流到磁盘的交流页数量IO:(现在的Linux版本块的大小为1024bytes)bi:发送到块设备的块数bo:从块设备吸收到的块数System(系统):in:每秒中断数,包括时钟中断。
【interrupt】cs:每秒高下文切换数。
【count/second】CPU(以百分比表示):us:用户 CPU 利用韶光(user time)sy:内核 CPU 系统利用韶光 (system time)id:空闲韶光(包括IO等待韶光),中心处理器的空闲韶光 。
以百分比表示。
wa:等待IO韶光

判断指标:

如果 r 常常大于4,id 常常少于40,表示cpu的负荷很重。
如果 bi,bo 长期不即是0,表示内存不敷。
如果 disk 常常不即是0,且在 b 中的行列步队大于3,表示io性能不好。
通过 cs 不雅观察 Java 程序运行过程中系统的高下文切换频率。
过高解释程序创建了过多的线程导致频繁的高下文切换。
3、pidstat 命令

如果是监视某个运用的高下文切换,可以利用 pidstat 命令监控指定进程的高下文切换。

pidstat 是 Sysstat 中的一个组件,也是一款功能强大的性能监测工具,我们可以通过命令:yum install sysstat 安装该监控组件。
top 和 vmstat 两个命令都是监测进程的内存、CPU 以及 I/O 利用情形,而 pidstat 命令则是深入到线程级别。

命令格式:【pidstat [ 选项 ] [ <韶光间隔> ] [ <次数> ]】

1)常用的选项:

-u:默认的参数,显示各个进程的 cpu 利用情形-r:显示各个进程的内存利用情形-d:显示各个进程的 I/O 利用情形-p:指定进程号-w:显示每个进程的高下文切换情形-t:显示进程中线程的统计信息-T { TASK | CHILD | ALL }TASK表示报告独立的task,CHILD关键字表示报告进程下所有线程统计信息。
ALL表示报告独立的task和task下面的所有线程。
把稳:task和子线程的全局的统计信息和pidstat选项无关。
这些统计信息不会对应到当前的统计间隔,这些统计信息只有在子线程kill或者完成的时候才会被网络。
-V:版本号-h:在一行上显示了所有活动,这样其他程序可以随意马虎解析。
-I:在SMP环境,表示任务的CPU利用率/内核数量-l:显示命令名和所有参数

2)查看所有进程的 CPU 利用情形

【pidstat】、【pidstat -u -p ALL】

PID:进程ID%usr:进程在用户空间占用cpu的百分比%system:进程在内核空间占用cpu的百分比%guest:进程在虚拟机占用cpu的百分比%CPU:进程占用cpu的百分比CPU:处理进程的cpu编号Command:当提高程对应的命令

3)显示每个进程的高下文切换情形

【pidstat -w -p <PID> <韶光间隔> <次数>】

PID:进程idCswch/s:每秒主动任务高下文切换数量Nvcswch/s:每秒被动任务高下文切换数量Command:命令名

4)显示进程中线程的统计信息

【pidstat -p <PID> -t】

回到顶部

三、可视化工具

下面大略先容几款常用的可视化剖析工具,一样平常我们须要将GC日志文件、堆转储文件dump下来,然后就可以通过这些工具来剖析。
如果是线上剖析一样平常直策应用上面的那些JDK命令行工具就足够了,这些可视化工具可以做一些赞助性的剖析。

1、jvisualvm — JVM监控

jvisualvm 是 jdk 供应的监控工具,位于 %JAVA_HOME%/bin/jvisualvm.exe,双击运行即可。

VisualVM 供应了一个可视界面,用于查看JVM上运行的基于 Java 技能的运用程序的详细信息。
VisualVM 能够监控线程,内存情形,方法的CPU韶光和内存中的工具,已被GC的工具,反向查看分配的堆栈(如100个String工具分别由哪几个工具分配出来的)等。

1)插件安装

VisualVM 基于NetBeans平台开拓工具,它具备通过插件扩展功能的能力,有了插件扩展支持,VisualVM可以做到:

显示虚拟机进程以及进程的配置、环境信息(jps、jinfo)监视运用程序的处理器、垃圾网络、堆、方法区以及线程的信息(jstat、jstack)dump以及剖析堆转储快照(jmap、jhat)方法级的程序运行性能剖析,找出被调用最多、运行韶光最长的方法离线程序快照:网络程序的运行时配置、线程dump、内存dump等信息建立一个快照,可以将快照发送开拓者处进行Bug反馈其他插件带来的无限可能性

可以从工具选项中打开插件面板安装所需的插件:

2)监视面板

在左边选择须要监控的程序,右边就可以可查看CPU、堆、线程等颠簸情形,也可以直接在这里进行手动 GC 和堆 Dump 操作。

3)线程面板

可看到所有的线程,以及线程的运行状态。
点击面板的线程 Dump 按钮,可以查看线程瞬时的线程栈。
(通过 jstack 也可以抓取线程栈)

4)GC面板

可以很方便的看到GC趋势图。
(也可利用 jstat 工具监控)

5)剖析堆转储快照

通过左上角装入快照按钮打开 dump 的堆转储文件,就可以剖析堆转储快照了。

3、GCViewer — 离线剖析GC日志

4、GCeasy — 在线剖析GC日志

GCeasy 是一款在线版的非常直不雅观的 GC 日志剖析工具,我们可以将日志文件压缩之后,上传到 GCeasy 官网即可看到非常清楚的 GC 日志剖析结果。

5、FastThread — 剖析线程栈

线程栈利用 jstack 命令 dump 下来后,可以利用 FastThread 在线工具剖析线程栈信息,可以直不雅观的看到有多少线程、线程池、线程的状态、是否有去世锁等信息。

6、MAT — 剖析堆转储文件

我们利用 jmap 命令 dump 下来的堆转储文件可以利用 MAT 来剖析,可以剖析创建了哪些工具,然后剖析工具的引用链,找出问题所在。
MAT 下载地址:http://www.eclipse.org/mat/downloads.php

MAT 紧张功能:

找出内存泄露的缘故原由找出重复引用的类和jar剖析凑集的利用剖析类加载器

7、性能调优工具

调优工具选择

调优的工具很多,一样平常对付线上系统,利用 jstat 工具足以剖析出JVM的运行情形,如果有GC日志,也可以利用GCeasy快速剖析JVM的运行情形。

碰着CPU负载过高,可以利用 top + pidstat 找出负载高的线程,或者直策应用 jstat 不雅观察是不是在频繁FullGC。

碰着去世锁、OOM等问题,可以用 jstack 把线程栈dump下来剖析,还可以结合 FastThread 在线工具,剖析线程栈、哪些线程壅塞等待等。

碰着OOM问题,利用 jmap 把堆转储快照dump下来,用MAT来剖析创建了哪些大量的工具。

8、JVM监控平台

系统上线后,如果不支配可视化的监控平台,我们一样平常就通过上面的这些工具来剖析JVM的内存运转模型、GC情形等,可以在机器上运行jstat,让其把监控信息写入一个文件,每天定时检讨—下。

监控平台则可以通过可视化的界面看到JVM各个区域的内存变革,GC次数和GC耗时等信息,以及涌现性能问题时能及时报警。

一样平常可以支配 Zabbix、Ganglia、Open-Falcon、Prometheus 之类的可视化监控平台,把线上系统接入到这些平台,就可以直接图形化看到JVM的表现。

回到顶部

四、利用工具剖析JVM运行情形

要想合理地分配内存、优化GC,通过前一篇的性能调优过程可以创造,我们至少须要知道如下的一些信息:新生代工具增长的速率,YoungGC的触发频率,YoungGC的耗时,每次YoungGC后存活工具大小,每次YoungGC过后有多少工具进入了老年代,老年代工具增长的速率,FullGC的触发频率,FullGC的耗时等。
前面我们是通过剖析GC日志或者粗略估算的办法来调优的,现在就利用 jstat 工具来剖析下。

1、运行示例程序

1)如下示例代码

这段代码仿照每秒钟在新生代创建20M工具,1秒之后就变为垃圾工具了。

1 public class GCMain { 2 static final int _1M = 1024 1024; 3 4 public static void main(String[] args) { 5 sleep(20); 6 7 for (int i = 0; i < 100; i++) { 8 loadData(i); 9 }10 }11 12 // loadData 每次要求产生20M工具,每次要求耗时1秒13 public static void loadData(int index) {14 System.out.println("load data: " + index);15 byte[] data1 = new byte[_1M 10];16 byte[] data2 = new byte[_1M 10];17 18 sleep(1);19 }20 21 public static void sleep(long seconds) {22 try {23 Thread.sleep(seconds 1000);24 } catch (InterruptedException e) {25 e.printStackTrace();26 }27 }28 }

2)设置JVM参数

运行示例程序前设置如下的JVM参数:新生代、老年代各100M,Eden区80M,Survivor区10M,大工具阀值20M。

-Xms200M-Xmx200M-Xmn100M-XX:SurvivorRatio=8-XX:MaxTenuringThreshold=5-XX:PretenureSizeThreshold=20M-XX:+UseParNewGC-XX:+UseConcMarkSweepGC-XX:CMSInitiatingOccupancyFraction=92-XX:+UseCMSInitiatingOccupancyOnly-XX:+UseCMSCompactAtFullCollection-XX:CMSFullGCsBeforeCompaction=0-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:./gc.log

3)估算内存运转模型

我们先根据这段业务代码以及JVM参数配置估算下JVM运行情形:

这段代码每秒将在Eden区产生20M工具,大概3~4秒钟会占满Eden区触发YoungGC。
YoungGC 后存活的工具可能超过10M,由于可能在创建 data2 时,Eden区不足了,而 data1 还是存活的;也有可能为0,在创建 data1 的时候 Eden 区就不足了。
由于 Survivor 区不敷以放下YoungGC后存活的工具,那么每次大概会有10M的工具进入老年代;考虑到有可能YoungGC后没有存活工具,就估算为2次YoungGC会有10M进入老年代吧。
YoungGC 3~4 秒触发一次,那么大概经由18次旁边YoungGC,便是60秒旁边,老年代就快满了,然后存活工具无法放入老年代触发FullGC。
由于CMS后台回收线程在老年代超过92%时会触发OldGC,以是60秒旁边也有可能由于老年代超过92%这个阀值触发GC。

4)利用 jps 命令找出程序的 PID

将程序运行起来,首先通过 jps -l 命令找到这个程序的PID。

5)利用 jstat 命令查看GC情形

如下是 jstat 输出的情形:

View Code

首先从前面几行可以看出内存各个区域的大小,Survivor0/Survivor1 10M,Eden区80M,老年代100M 等信息。

2、新生代工具增长的速率

从 EU 这一行可以看出,新生代基本是按照每秒20M旁边的工具在增长。

3、YoungGC的触发频率和耗时

从 Eden 区的内存变革可以看出,基本是每隔3秒或4秒就会触发一次 YoungGC,比如第一次Eden区增长到66424.2时,经由一次 YoungGC后只剩下11878.4。
从 YGC 这列也大致可以看出YoungGC的频率。

从 YGCT 这列可以看出,每次YoungGC耗时1~5毫秒的样子,也便是说每隔3~4秒,触发一次YoungGC,一次YoungGC系统卡顿1~5毫秒。
这也可以看出 YoungGC 实在是很快的,就算新生代800M也才10几毫秒,对系统险些没什么影响。

4、YoungGC后存活工具大小以及有多少工具进入了老年代

从 S0U、S1U 这两列的变革可以看出,每次YoungGC后有800K旁边的工具进入 Survivor 区。

从 OU 这列的变革可以看出,每次进入老年代的工具在10M旁边,以是一次YoungGC后可能有10M的存活工具进入老年代。

5、FullGC的触发频率和耗时

从 OU 这列的变革可以看出,在老年代达到 82573.2 时,触发了 FullGC,回收后老年代大小为 10892.1。
从整体的韶光线上看,刚好60秒就触发了一次FullGC。

从 FGCT 可以看出,一次FullGC 耗时2毫秒,

为什么在老年代 82573.2 时就触发了FullGC呢,我们从GC日志中来看:

可以看出这一秒内,实际上这次YoungGC导致有10M的工具进入老年代,老年代实际有92815K工具,因而该当是CMS超过了 92% 的阀值之后触发了老年代GC。

6、利用GCeasy查看GC日志

至此,实在已经基本上剖析出全体JVM的运转情形了。
这里总结下:

新生代、老年代 100M,Eden区80M,Survivor区10M;Eden区每秒产生20M旁边工具,每隔3~4秒触发一次YoungGC;YoungGC后存活工具在0~10M旁边,由于无法放入Survivor区会进入老年代,每次进入老年代工具10M旁边;在经由16次旁边YoungGC后,也便是60秒旁边老年代会靠近占满,超过设置的阀值,触发一次 FullGC。

从上面的剖析可以看出,jstat 监控的输出结果基本是符合前面估算的结果的。
但是粗略估算须要熟习系统核心的业务,而且其它未知成分也比较多,粗略估算一样平常用于系统刚上线阶段来设置JVM参数。
而通过 jstat 来监控一样平常就可以比较准确的摸清JVM的运行情形,然后进行性能调优。

接下来再通过GC日志来看下是否符合剖析的情形,GC日志就不再一行一行剖析了,我们直接通过在线工具 GCeasy 来看看内存变革和GC的情形。
将输出的GC日志直接拷贝到 GCeasy 上,就可以看到剖析的结果。

1)GC总体情形

从这张图可以得到如下信息:

垃圾回收器的吞吐率为 99.937%;均匀GC停顿韶光:2毫秒最长GC停顿韶光:10毫秒80% 的GC耗时 0~1毫秒20% 的GC耗时 9~10毫秒

2)YoungGC频率

从 Young Gen 这个统计图可以看出,YoungGC的频率在3~4秒的样子。

3)老年代GC频率

从这张统计图可以看出,老年代是每两次YoungGC增长一次,每次增长10M旁边,在60秒旁边触发一次OldGC。

4)CMS回收情形

这张图展示了CMS各个阶段的统计情形

7、性能优化

从上面的剖析可以看出,这个JVM最大的问题在于 Survivor 区没起浸染,Survivor 区只有10M,而YoungGC后存活工具大于10M,导致无法放进Survivor区而直接进入老年代了,进而触发FullGC。
因此我们可以增大新生代大小或者调度Eden区和Survivor区比例,来让工具进入Survivor区。

比如改为如下配置:新生代给150M,比例调度为6,这样Eden区90M,Survivor区各30M,这样Survivor区足以放下YoungGC后存活的工具,也基本上能避免动态年事判断导致工具进入老年代。

-Xms200M-Xmx200M-Xmn150M-XX:SurvivorRatio=6

再看这时的GC情形,首先从 S0U、S1U 的变革可以看出,Survivor 区在起浸染了,每次YoungGC后存活工具都进入Survivor区了。

然后从 OU、YGC的变革可以看出,有部分长期存活的工具在YoungGC次数超过设置的GC年事阀值(设置的5岁)后,进入了老年代。

从 FGC 这列可以看出,Survivor 区合理设置后,再没有发生过 FullGC 了。

回到顶部

五、利用 MAT 剖析OOM问题

对付排查 OOM 问题、剖析程序堆内存利用情形,最好的办法便是剖析堆转储,堆转储,包含了堆现场全貌和线程栈信息。
这节就来看看如何利用MAT剖析OOM问题。

1、运行示例程序

准备如下两个测试类:

1 package com.lyyzoo.test.jvm; 2 3 public class OomService { 4 5 private List<String> data = new ArrayList<>(); 6 7 public void addData() { 8 //往同一个ArrayList中不断加入大小为10KB的字符串 9 data.add(IntStream.rangeClosed(1, 10_000)10 .mapToObj(__ -> "A")11 .collect(Collectors.joining("")));12 }13 }

1 package com.lyyzoo.test.jvm; 2 3 public class OomMain { 4 public static void main(String[] args) { 5 OomService service = new OomService(); 6 7 while (true) { 8 service.addData(); 9 }10 }11 }

设置如下JVM参数:

-Xms200M-Xmx200M-Xmn100M-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=./dump.hprof-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:./gc.log

运行程序后报OOM非常:

2、MAT 剖析OOM问题的思路

对付线上运行的程序,如果我们不能通过日志快速定位出OOM的根源,一样平常就可以利用MAT来剖析OOM的问题。

利用 MAT 剖析 OOM 问题,一样平常可以按照以下思路进行:

通过支配树功能或直方图功能查看花费内存最大的类型,来剖析内存透露的大概缘故原由;查看那些花费内存最大的类型、详细的工具明细列表,以及它们的引用链,来定位内存透露的详细点;合营查看工具属性的功能,可以分开源码看到工具的各种属性的值和依赖关系,帮助我们理清程序逻辑和参数;赞助利用查看线程栈来看 OOM 问题是否和过多线程有关,乃至可以在线程栈看到 OOM 末了一刻涌现非常的线程。

如果dump出来的内存快照很大,比如有几个G,务必在启动MAT之前,先在配置文件(MemoryAnalyzer.ini)里给MAT本身设置—下堆内存大小(默认为1024m),比如设置为4个G,或者8个G。

3、总览图 — 快速剖析OOM问题

利用MAT打开堆转储文件 dump.hprof,打开后前辈入的是概览信息界面:

从饼图可以看出,明显有工具占用了大量内存,然后再看 Problem Suspect1,已经解释了 main 线程通过局部变量霸占了 99.42% 内存的工具,而且是 java.lang.Object[] 数组霸占了大量内存。

点击 Details 进去查看详细的解释,从 “Accumulated Objects in Dominator Tree” 支配树可以看出,main 线程引用了 OomService 工具,OomService 引用了一个 ArrayList 工具,然后 ArrayList 存储了大量 String 工具。
这里基本上就能剖析出OOM的根源了。

再点击 See stacktrace 看看线程栈基本就能定位到问题代码了。

4、直方图 — 定位根源

工具栏的第二个按钮可以打开直方图,直方图按照类型进行分组,列出了每个类有多少个实例,以及占用的内存。

可以看到,char[] 字节数组占用内存最多,工具数量大概多,第二位的 String 工具数量也非常多,有 9791 个,从这大概可以猜出该当是创建了大量的 String 工具。

在 char[] 上点击右键,选择 List objects -> with incoming references,就可以列出所有的 char[] 实例,以及每个 char[] 的全体引用关系链:

随机展开一个 char[],如下图所示:

右侧框中可以看到全体引用链,左侧的框可以查看每一个实例的内部属性。

通过这个引用链可以创造是 String 工具引用了 char[] 数组(String 的内部构培养是一个 char[] 数组),解释创建了大量的 String 工具;然后 String 工具又被 ArrayList 的 Object[] 数组引用着,解释是大量 String 工具放入了 ArrayList 中,然后这个 ArrayList 又被 OomService 的 data 变量引用着。
到这里就定位出了引发OOM的类了。

Retained Heap(深堆)代表工具本身和工具关联的工具占用的内存,Shallow Heap(浅堆)代表工具本身占用的内存。
比如,OomService 中的 data 这个 ArrayList 工具本身只有 16 字节,但是其所有关联的工具占用了 130+MB 内存。

如果希望看到完全内容的话,可以右键选择 Copy->Value,把值复制到剪贴板或保存到文件中:

5、支配树 — 定位根源

实在,利用直方图定位 OomService,已经走了些弯路。
可以点击工具栏中第三个按钮进入支配树界面。
这个界面会按照工具保留的 Retained Heap 倒序直接列出占用内存最大的工具。

可以看到,第一位便是 OomService,全体路径是 OomSerice -> ArrayList -> Object[] -> String -> char[] 。

6、线程栈 — 剖析代码

可以点击工具栏的第五个按钮,打开线程视图来剖析 OomService 实行什么逻辑。
可以看到 OomService 是 OomMain 的一个本地变量,然后 OomMain 调用了 OomService 的 addData 方法,然后 addData 方法里该当是通过 Stream 天生一个字符串放入 data 中的。

7、OQL—查询数据

点击工具栏的第四个按钮,来到 OQL 界面。
在这个界面,我们可以利用类似 SQL 的语法,在 dump 中搜索数据。
可以看到只创建了一个 OomService 实例,解释只有一个地方调用了 OomService 的方法。

然后可通过 List objects 功能搜索引用OomService 的工具:

可以看到其被 main 线程引用着:

回到顶部

六、利用 Arthas 剖析高 CPU 问题

Arthas 是阿里开源的 Java 诊断工具,比较 JDK 内置的诊断工具,要更人性化,并且功能强大,可以实现许多问题的一键定位,而且可以一键反编译类查看源码,乃至是直接进行生产代码热修复,实现在一个工具内快速定位和修复问题的一站式做事。

Arthas 官方文档:https://alibaba.github.io/arthas/

1、运行示例程序

准备如下导致CPU负载高的代码:代码中创建了2个线程的线程池,提交的任务通过 BCryptPasswordEncoder 对一个长字符串加密,这个是非常花费CPU的。

1 package com.lyyzoo.test.jvm; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.Future; 6 import java.util.stream.Collectors; 7 import java.util.stream.IntStream; 8 9 import org.apache.commons.lang3.RandomUtils;10 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;11 12 public class CpuService {13 private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();14 private ExecutorService executor = Executors.newFixedThreadPool(2);15 16 public void doTask() throws Exception {17 while (true) {18 randomEncode(RandomUtils.nextInt(0, 10000));19 }20 }21 22 private void randomEncode(Integer size) throws Exception {23 String payload = IntStream.rangeClosed(1, size).mapToObj(__ -> "A").collect(Collectors.joining());24 Future<String> f1 = executor.submit(() -> {25 return encoder.encode(payload);26 });27 Future<String> f2 = executor.submit(() -> {28 return encoder.encode(payload);29 });30 31 f1.get();32 f2.get();33 }34 }

1 public class CpuMain {2 private static CpuService service = new CpuService();3 4 public static void main(String[] args) throws Exception {5 service.doTask();6 }7 }2、启动 Arthas

启动后,直接找到我们要排查的 JVM 进程,然后可以看到 Arthas 附加进程成功:

输入 help 命令,可以看到所有支持的命令列表。
这里紧张会用到 dashboard、thread、jad、watch 等命令,来定位高CPU的问题。

3、dashboard — 展示整体情形

dashboard 命令整体展示了进程所有线程、内存、GC 等情形,可以明显看到两个CPU占用很高的线程,从线程名字来看该当是线程池的线程。

4、thread — 查看高CPU的线程

接下来,查看最繁忙的线程在实行的线程栈,可以利用 thread -n 命令。
这里,我们查看下最忙的 2 个线程:从线程栈可以看出,该当便是 CpuService 的 randomEncode 方法调用 BCryptPasswordEncoder 的 encode 方法导致CPU负载高的。

5、watch — 监控参数

如果想要不雅观察方法的入参和出参,可以用 watch 命令来不雅观察:

6、jad — 反编译

前面已经剖析出CPU负载高的位置是 CpuService 的 randomEncode 了,那么通过 jad 反编译来看看源码长什么样子,方便我们进一步定位问题。

7、redefine — 重载类

如果我们想做线上调试,又不想在本地改代码,打印日志,再提交到做事器,再重启做事测试,那我们可以结合 arthas 的 jad、mc、redefine 来动态重定义类。

① 首先用 jad 把源文件下载下来

然后修正下源码:添加了一行输出日志

② 利用 mc 命令反编译源文件

反编译后会天生对应的 class 文件:

③ 利用 redefine 重载类

就可以看到掌握台已经在输出我们打印的日志了:

须要额外解释的是,由于 monitor、trace、watch 等命令是通过字节码增强技能来实现的,会在指定类的方法中插入一些切面来实现数据统计和不雅观测,因此诊断结束要实行 shutdown 来还原类或方法字节码,然退却撤退出 Arthas。

学习更多JAVA知识与技巧,关注与私信博主(学习)

标签:

相关文章

我国土地利用分类代码的构建与应用

土地利用分类代码是我国土地管理的重要组成部分,是土地资源调查、规划、利用和保护的依据。土地利用分类代码的构建与应用显得尤为重要。本...

SEO优化 2025-02-18 阅读1 评论0

微信跳转微信支付便捷支付体验的秘密武器

移动支付已成为人们日常生活中不可或缺的一部分。作为我国领先的社交平台,微信支付凭借其便捷、安全的支付方式,深受广大用户的喜爱。而微...

SEO优化 2025-02-18 阅读0 评论0

探寻会计科目代码背后的奥秘分类与

会计科目代码是会计信息系统中不可或缺的组成部分,它将企业的经济活动进行分类和归纳,为会计核算、财务分析和决策提供重要依据。本文将从...

SEO优化 2025-02-18 阅读1 评论0