JVM调优以及Arthas简单使用

    技术2025-02-11  19

    JVM调优以及Arthas简单使用

    1、JVM命令1、jps(查看本地运行进程)2、Jmap3、Jstack1、查找CPU占用过高进程的方法 4、jinfo5、Jstat1、堆内存垃圾回收统计2、堆内存统计3、年轻代垃圾回收统计4、年轻代内存统计5、老年代垃圾回收统计6、老年代内存统计7、元数据空间统计8、各单位区域比例数据统计 2、JVM优化思路3、Arthas1、Arthas简介2、Arthas安装3、Arthas简单使用1、Help (显示所有命令)2、dashboard (显示线程、堆,gc所有信息)3、thread 线程号 (打印某个线程的堆栈信息)4、thread (打印所有线程)5、thread -b (打印死锁)6、jad 文件名(.class文件)7、ognl (动态查看或修改线上代码) 10、辅助知识1、jvisualvm远程连接2、full gc比minor gc多的原因3、内存泄露是怎么回事4、JVM所有参数查看命令

    1、JVM命令

    1、jps(查看本地运行进程)

    进程号         应用

    2、Jmap

    Jmap命令可查看内存信息,实例个数以及占内存大小,当内存飙升的时候可以用Jmap来查看,接下来介绍几个具体命令: 1、Jmap -histo 进程号 一般情况会输出到文件里:Jmap -histo 进程号 > xxx.txt 输入在当前这一时刻这个程序进程对应的对象实例数以及占用内存大小

    参数参数说明num序号#instances实例数#bytes占用内存大小class name类名称[C is a char[],[S is a short[]…

    2、Jmap -heap 进程号 查看当前进程下的堆信息 如果要输出文件命令为:Jmap -heap 进程号 > xxx.txt

    参数含义Heap Usage堆的使用PS Young Generation年轻代Eden SpaceEden区域From SpaceSurvivor的s0区(也是from区)To SpaceSurvivor的s1区(也是to区)PS Old Generation老年代capacity区域总容量used已使用容量free空闲容量

    3、Jmap -dump:format=b,file=xxx 进程号 在当前目录下导出一个存储当前堆内存信息的dump文件(dump文件是进程的内存镜像),文件中存储的是当时那一时刻的堆的信息。

    这样就代表已经导出成功,例如用我们的jvisualvm工具就可以直接文件装入,选出我们刚刚导出的文件即可。 注意:在内存溢出之前,我们可以通过设置两个参数,导出内存溢出之前的堆信息。 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ (要导出文件的路径)

    3、Jstack

    当CPU使用率过高的话,可以使用Kstack命令来查看 Jstack 进程号 可查看线程的死锁,下面是我截取了输入执行命令之后的报错信息(截取了其中的一部分)。

    参数含义Thread-1线程名prio=5java优先级是5os_prio=0操作系统优先级是0tidjava线程的id,threadidnid内核里面的线程唯一id

    1、查找CPU占用过高进程的方法

    首先我们使用下面这段代码,并附上图可以看见行号。

    public class JvisualvmLRTest { public static void main(String[] args) { // 为了让程序一直运行,但不要OOM while (true) { } } }

    首先我们需要执行这段代码,在linux上通过java命令运行 接着我们通过top命令发现有一个进程占用CPU特别高,就是我们运行的这个类,如下图所示:

    1、输入命令top -p 进程号:显示java进程的内存情况,结果如下图(top -p 33417):

    2、查看进程的具体线程:输入大写“H”查看具体的线程信息,结果我们发现是33418这个线程占用CPU特别高,结果如下图所示:

    3、查看具体线程堆栈情况:我们首先得将线程id=33418转换为16进制,因为内核中nid,也就是上面介绍JStack得nid是16进制,0x828A = 33418,这里的十六进制的字母都得转换位小写,因为nid里面存的是小写。 输入命令:jstack 进程号|grep -A 10 线程id的十六进制 jstack 33417|grep -A 10 找到线程堆里面此线程信息的后10条信息,可分析出是哪行代码的原因导致了内存升高,如下图所示:

    结果:从图中我们可以看出是JvisualvmLRTest个类的第四行,分析正确。

    4、jinfo

    查看正在运行的Java应用程序的扩展参数,有以下几种用法。 1、jinfo 进程号:查看所有参数。 2、jinfo -flags 进程号:查看JVM参数。 3、jinfo -sysprops 进程号:查看java系统参数。

    5、Jstat

    Jstat命令可以查看堆内存各部分的使用量以及加载类的个数。 使用方法:jstat [命令] 进程号 间隔时间 次数 例如:jstat -gc 1254 1000 10 -gc 代表打印方式 1254 代表进程号 1000 代表1000ms打印一条,如下图所示 10 代表打印10次

    1、堆内存垃圾回收统计

    命令:jstat -gc 进程号 展示出当前进程整个堆的垃圾回收内存大小,如下图所示:

    参数含义S0CSurvivor的S0区域,单位kbS1CSurvivor的S1区域,单位kbS0US0区域目前已使用内存,单位kbS1US1区域目前已使用内存,单位kbECEden区域内存大小,单位kbEUEden区域目前已使用内存,单位kbOCOld区域内存大小,单位kbOUOld区域目前已使用内存,单位kbMC方法区区域内存大小,单位kbMU方法区区域目前已使用内存,单位kbCCSC压缩类空间内存大小,单位kbCCSU压缩类空间目前已使用内存,单位kbYGC从启动开始执行的Young gc次数YGCT从启动开始执行Young gc所耗总时间,单位sFGC从启动开始执行的Full gc次数FGCT从启动开始执行Full gc所耗总时间,单位sGCT从启动开始执行所有gc所耗总时间,单位s

    2、堆内存统计

    命令:jstat -gccapacity 进程号 展示出当前进程整个堆的各个区域内存分配大小,如下图所示:

    参数含义NGCMN年轻代最小容量,单位kbNGCMX年轻代最大容量,单位kbNGC年轻代当前容量,单位kbS0CSurvivor的S0区域,单位kbS1CSurvivor的S1区域,单位kbECEden区域内存大小,单位kbOGCMN老年代最小容量,单位kbOGCMX老年代最大容量,单位kbOGC老年代当前容量,单位kbOC老年代当前容量,单位kbMCMN元数据最小容量,单位kbMCMX元数据最大容量,单位kbMC元数据当前容量,单位kbCCSMN压缩类最小空间大小,单位kbCCSMX压缩类最大空间大小,单位kbCCSC压缩类当前空间大小,单位kbYGC从启动开始执行的Young gc次数FGC从启动开始执行的Full gc次数

    3、年轻代垃圾回收统计

    参数含义S0CSurvivor的S0区域,单位kbS1CSurvivor的S1区域,单位kbS0US0区域目前已使用内存,单位kbS1US1区域目前已使用内存,单位kbTT对象在年轻代中存活的次数MTT对象在年轻代中存活的最大次数DSS期望的Survivor区域大小,单位kbECEden区域内存大小,单位kbEUEden区域目前已使用内存,单位kbYGC从启动开始执行的Young gc次数YGCT从启动开始执行Young gc所耗总时间,单位s

    4、年轻代内存统计

    参数含义NGCMN年轻代最小容量,单位kbNGCMX年轻代最大容量,单位kbNGC年轻代当前容量,单位kbS0CMXSurvivor的S0区域最大容量,单位kbS0CSurvivor的S0区域当前容量,单位kbS1CMXSurvivor的S1区域最大容量,单位kbS1CSurvivor的S1区域当前容量,单位kbECMXEden区域的最大容量,单位kbECEden区域当前内存大小,单位kbYGC从启动开始执行的Young gc次数FGC从启动开始执行的Full gc次数

    5、老年代垃圾回收统计

    参数含义MC方法区目前容量,单位kbMU方法区已使用容量,单位kbCCSC压缩类当前空间大小,单位kbCCSU压缩类已使用空间大小,单位kbOCOld区域内存大小,单位kbOUOld区域目前已使用内存,单位kbYGC从启动开始执行的Young gc次数FGC从启动开始执行的Full gc次数FGCT从启动开始执行Full gc所耗总时间,单位sGCT从启动开始执行所有gc所耗总时间,单位s

    6、老年代内存统计

    参数含义OGCMN老年代最小容量,单位kbOGCMX老年代最大容量,单位kbOGC老年代当前容量,单位kbOC老年代当前容量,单位kbYGC从启动开始执行的Young gc次数FGC从启动开始执行的Full gc次数FGCT从启动开始执行Full gc所耗总时间,单位sGCT从启动开始执行所有gc所耗总时间,单位s

    7、元数据空间统计

    参数含义MCMN元数据最小容量,单位kbMCMX元数据最大容量,单位kbMC元数据当前容量,单位kbCCSMN压缩类最小空间大小,单位kbCCSMX压缩类最大空间大小,单位kbCCSC压缩类当前空间大小,单位kbYGC从启动开始执行的Young gc次数FGC从启动开始执行的Full gc次数FGCT从启动开始执行Full gc所耗总时间,单位sGCT从启动开始执行所有gc所耗总时间,单位s

    8、各单位区域比例数据统计

    参数含义S0Survivor的S0区域当前使用比例S1Survivor的S1区域当前使用比例EEden区域当前使用比例OOld区域当前使用比例M元数据区域当前使用比例CCS压缩区域当前使用比例YGC从启动开始执行的Young gc次数FGC从启动开始执行的Full gc次数FGCT从启动开始执行Full gc所耗总时间,单位sGCT从启动开始执行所有gc所耗总时间,单位s

    2、JVM优化思路

    优化重点:JVM优化主要优化的是Full gc,因为一次Full gc的STW时间比较长,容易造成卡顿,使用户的体验相当不好。 优化思路:jstat命令可以看到一些关键性信息,比如gc次数,gc时间,某一时刻堆各个区域的大小占比,如果我们使用jstat [命令] 进程号 间隔时间 次数命令就可以看出每个几秒甚至几分钟程序发生gc的情况,这样就可以去调整一些参数使尽可能少量的对象进入老年代,少发生full gc。 需求参数 1、年轻代对象增长的速率 算出类似于每秒给年轻代放入多少对象。 2、minor gc触发频率和每次耗时 年轻代几秒或者几分钟触发一次,每次执行minor gc的时间是多长。 3、每次minor gc之后有多少对象存活进入老年代 每次触发minor gc时,有多少对象挪动到老年代,如果过多,去通过对象挪到老年代规则判断一下是哪种方式比较多,详情参考这篇博客的判断进入老年代的方法。 4、full gc的触发频率和每次耗时 算出full gc的触发频率和耗时,去优化它。 举例: 例子1:如果大部分的对象都是朝生夕逝的话,就尽量别让这些对象进入老年代,主要看点就是让minor gc时,存活的对象总大小不要超过Survivor区域的S0或者S1区域的一半,如果超过一半,就会直接进入老年代,但是这些对象可能在几秒之后就变成了垃圾,所以,进入老年代只会占用过多空间,发生full gc而已。 例子:对象在没有达到设置长期存活年龄就进入老年代,一个有可能是大对象,还有可能就是某一块代码的问题导致对象突然增多,需要去校验一下代码,通过Jmap -histo 进程号命令查看一下是否有我们自己代码中定义的对象过多,如果查询到,肯定那块的CPU占用率就比较高,所以参考一下上面Jstack的查找CPU过高进程的方法,如果查找出来是代码的问题,我们就得优化自己的代码了。

    3、Arthas

    1、Arthas简介

    Arthas官方文档地址 https://alibaba.github.io/arthas 简述:Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。 Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

    2、Arthas安装

    1、下载安装包 使用git下载,输入命令: curl -O https://alibaba.github.io/arthas/arthas-boot.jar 2、安装安装包 将下载好的jar文件放在Linux环境下,输入命令: java -jar arthas-boot.jar 出现如下截图即可即为启动

    3、Arthas简单使用

    1、当程序运行时,启动Arthas。 当出现如下图所示时,即表示成功,我现在linux系统上只有一个进程,所以图中只显示了一个。

    2、当我按1并回车之后,Arthas将挂载在我们序号为1的那个进程上,我们就可以进一步分析,第一次进会有点慢,稍微等待一下,结果如下图所示:

    1、Help (显示所有命令)

    此命令可显示出Arthas为我们提供的所有命令以及命令的作用,详情如下图:

    2、dashboard (显示线程、堆,gc所有信息)

    中文意思“仪表盘” 输入命令之后可显示当前进程的所有线程信息,堆信息,gc信息以及运行程序的底层java版本等信息,这个结果还是动态的,每几秒刷新一下,详情如下图所示:

    3、thread 线程号 (打印某个线程的堆栈信息)

    在上图中我们发现有一个线程占用CPU特别高,我们可以通过这条命令看到这个线程的堆栈信息,以查找问题所在,通过图中红框中的信息,可以看到问题代码的所在,详情如下图所示:

    4、thread (打印所有线程)

    打印出当前进程的所有线程信息,详情如下图所示:

    5、thread -b (打印死锁)

    打印出当前进程的所有死锁信息,详情如下图所示:

    6、jad 文件名(.class文件)

    可以反编译线上的.class文件的字节码,使得我们查看代码是否发布成功,详情如下图所示:

    7、ognl (动态查看或修改线上代码)

    可以动态的去查阅或者修改线上代码变量的值 下面这是示例代码,通过这段代码操作

    import java.util.ArrayList; public class JvisualvmLRTest { private static ArrayList<String> arrayList = new ArrayList(); public static void main(String[] args) { for(int i = 0;i < 10;i++){ arrayList.add("我是第" + (i+1) + "个数据"); } // 为了让程序一直运行,但不要OOM while (true) { } } }

    1、查阅成员变量arrayList中的值 输入命令:ognl @JvisualvmLRTest@arrayList ognl @类名@变量名 详情如下图所示

    2、修改成员变量arrayList中的值 输入命令:ognl ‘@JvisualvmLRTest@arrayList.add(“I am Test Data.”)’ ognl ‘@类名@变量名.add(“I am Test Data.”)’ 注意:这里ognl后面的参数用单引号扩了起来(单引号为英文单引号) 详情如下图所示

    10、辅助知识

    1、jvisualvm远程连接

    拓展:用jvisualvm中的JMX远程连接服务器的项目,有两种项目。 1、普通的可直接运行的项目,比如一个java文件或者一个jar项目。 可以暂时先关闭掉服务器的防火墙 关闭命令:systemctl stop firewalld.service 下面这个是我之前写过的一段代码,为了让他一直运行,搞了个死循环。

    public class JvisualvmLRTest { public static void main(String[] args) { // 为了让程序一直运行,但不要OOM while (true) { } } }

    我将这个文件放到Linux系统下,然后输入如下命令:

    java -Dcom.sun.management.jmxremote.port=8888 -Djava.rmi.server.hostname=192.168.119.135 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false JvisualvmLRTest

    我的是centos7 上图中第一行是关闭防火墙 第二行是编辑java文件 第三行是配置远程连接参数,开启8888端口的同时运行这个java文件。 最后在Jvisualvm这边直接添加即可。

    2、如果是tomcat部署的项目。 在catalina.sh文件里的最后一个JAVA_OPTS的赋值语句下一行增加如下配置行 输入如下命令: 最后在Jvisualvm这边直接添加即可。

    JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=8889 -Djava.rmi.server.hostname=192.168.119.135 -Dcom.sun.management.j mxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

    2、full gc比minor gc多的原因

    元空间内存不够,一直要扩充元空间要触发full gc。显示调用System.gc()造成多余的full gc,线上程序可以通过XX:+DisableExplicitGC禁用代码中System.gc()代码,意思是就算代码中写了这行代码,也不起作用,就跟注释了一样。老年代空间过小或者放入老年代的对象过多,触发了老年代空间分配担保机制。

    3、内存泄露是怎么回事

    概念:例如一些缓存的对象已经过了很长时间不再会用到,但是还没有被清理,就相当于是垃圾对象无法被清除但是占用着内存空间,导致老年代剩余空间不足,频繁的发生full gc,这就叫内存泄漏。 解决办法:比如我们可以考虑采用一些成熟的JVM级缓存框架来解决,好比ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存,将长期不用的缓存对象淘汰掉。

    4、JVM所有参数查看命令

    java -XX:+PrintFlagsInitial:表示打印出所有JVM参数的默认值选项。 java -XX:+PrintFlagsInitial:表示打印出所有JVM参数在程序运行时生效的值。

    Processed: 0.010, SQL: 9