进程号 应用
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 Space | Eden区域 |
From Space | Survivor的s0区(也是from区) |
To Space | Survivor的s1区(也是to区) |
PS Old Generation | 老年代 |
capacity | 区域总容量 |
used | 已使用容量 |
free | 空闲容量 |
3、Jmap -dump:format=b,file=xxx 进程号
在当前目录下导出一个存储当前堆内存信息的dump文件(dump文件是进程的内存镜像),文件中存储的是当时那一时刻的堆的信息。
这样就代表已经导出成功,例如用我们的jvisualvm工具就可以直接文件装入,选出我们刚刚导出的文件即可。
注意:在内存溢出之前,我们可以通过设置两个参数,导出内存溢出之前的堆信息。
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./ (要导出文件的路径)
当CPU使用率过高的话,可以使用Kstack命令来查看
Jstack 进程号
可查看线程的死锁,下面是我截取了输入执行命令之后的报错信息(截取了其中的一部分)。
参数 | 含义 |
---|---|
Thread-1 | 线程名 |
prio=5 | java优先级是5 |
os_prio=0 | 操作系统优先级是0 |
tid | java线程的id,threadid |
nid | 内核里面的线程唯一id |
首先我们使用下面这段代码,并附上图可以看见行号。
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个类的第四行,分析正确。
查看正在运行的Java应用程序的扩展参数,有以下几种用法。
1、jinfo 进程号:查看所有参数。
2、jinfo -flags 进程号:查看JVM参数。
3、jinfo -sysprops 进程号:查看java系统参数。
Jstat命令可以查看堆内存各部分的使用量以及加载类的个数。
使用方法:jstat [命令] 进程号 间隔时间 次数
例如:jstat -gc 1254 1000 10
-gc 代表打印方式
1254 代表进程号
1000 代表1000ms打印一条,如下图所示
10 代表打印10次
命令:jstat -gc 进程号
展示出当前进程整个堆的垃圾回收内存大小,如下图所示:
参数 | 含义 |
---|---|
S0C | Survivor的S0区域,单位kb |
S1C | Survivor的S1区域,单位kb |
S0U | S0区域目前已使用内存,单位kb |
S1U | S1区域目前已使用内存,单位kb |
EC | Eden区域内存大小,单位kb |
EU | Eden区域目前已使用内存,单位kb |
OC | Old区域内存大小,单位kb |
OU | Old区域目前已使用内存,单位kb |
MC | 方法区区域内存大小,单位kb |
MU | 方法区区域目前已使用内存,单位kb |
CCSC | 压缩类空间内存大小,单位kb |
CCSU | 压缩类空间目前已使用内存,单位kb |
YGC | 从启动开始执行的Young gc次数 |
YGCT | 从启动开始执行Young gc所耗总时间,单位s |
FGC | 从启动开始执行的Full gc次数 |
FGCT | 从启动开始执行Full gc所耗总时间,单位s |
GCT | 从启动开始执行所有gc所耗总时间,单位s |
命令:jstat -gccapacity 进程号
展示出当前进程整个堆的各个区域内存分配大小,如下图所示:
参数 | 含义 |
---|---|
NGCMN | 年轻代最小容量,单位kb |
NGCMX | 年轻代最大容量,单位kb |
NGC | 年轻代当前容量,单位kb |
S0C | Survivor的S0区域,单位kb |
S1C | Survivor的S1区域,单位kb |
EC | Eden区域内存大小,单位kb |
OGCMN | 老年代最小容量,单位kb |
OGCMX | 老年代最大容量,单位kb |
OGC | 老年代当前容量,单位kb |
OC | 老年代当前容量,单位kb |
MCMN | 元数据最小容量,单位kb |
MCMX | 元数据最大容量,单位kb |
MC | 元数据当前容量,单位kb |
CCSMN | 压缩类最小空间大小,单位kb |
CCSMX | 压缩类最大空间大小,单位kb |
CCSC | 压缩类当前空间大小,单位kb |
YGC | 从启动开始执行的Young gc次数 |
FGC | 从启动开始执行的Full gc次数 |
参数 | 含义 |
---|---|
S0C | Survivor的S0区域,单位kb |
S1C | Survivor的S1区域,单位kb |
S0U | S0区域目前已使用内存,单位kb |
S1U | S1区域目前已使用内存,单位kb |
TT | 对象在年轻代中存活的次数 |
MTT | 对象在年轻代中存活的最大次数 |
DSS | 期望的Survivor区域大小,单位kb |
EC | Eden区域内存大小,单位kb |
EU | Eden区域目前已使用内存,单位kb |
YGC | 从启动开始执行的Young gc次数 |
YGCT | 从启动开始执行Young gc所耗总时间,单位s |
参数 | 含义 |
---|---|
NGCMN | 年轻代最小容量,单位kb |
NGCMX | 年轻代最大容量,单位kb |
NGC | 年轻代当前容量,单位kb |
S0CMX | Survivor的S0区域最大容量,单位kb |
S0C | Survivor的S0区域当前容量,单位kb |
S1CMX | Survivor的S1区域最大容量,单位kb |
S1C | Survivor的S1区域当前容量,单位kb |
ECMX | Eden区域的最大容量,单位kb |
EC | Eden区域当前内存大小,单位kb |
YGC | 从启动开始执行的Young gc次数 |
FGC | 从启动开始执行的Full gc次数 |
参数 | 含义 |
---|---|
MC | 方法区目前容量,单位kb |
MU | 方法区已使用容量,单位kb |
CCSC | 压缩类当前空间大小,单位kb |
CCSU | 压缩类已使用空间大小,单位kb |
OC | Old区域内存大小,单位kb |
OU | Old区域目前已使用内存,单位kb |
YGC | 从启动开始执行的Young gc次数 |
FGC | 从启动开始执行的Full gc次数 |
FGCT | 从启动开始执行Full gc所耗总时间,单位s |
GCT | 从启动开始执行所有gc所耗总时间,单位s |
参数 | 含义 |
---|---|
OGCMN | 老年代最小容量,单位kb |
OGCMX | 老年代最大容量,单位kb |
OGC | 老年代当前容量,单位kb |
OC | 老年代当前容量,单位kb |
YGC | 从启动开始执行的Young gc次数 |
FGC | 从启动开始执行的Full gc次数 |
FGCT | 从启动开始执行Full gc所耗总时间,单位s |
GCT | 从启动开始执行所有gc所耗总时间,单位s |
参数 | 含义 |
---|---|
MCMN | 元数据最小容量,单位kb |
MCMX | 元数据最大容量,单位kb |
MC | 元数据当前容量,单位kb |
CCSMN | 压缩类最小空间大小,单位kb |
CCSMX | 压缩类最大空间大小,单位kb |
CCSC | 压缩类当前空间大小,单位kb |
YGC | 从启动开始执行的Young gc次数 |
FGC | 从启动开始执行的Full gc次数 |
FGCT | 从启动开始执行Full gc所耗总时间,单位s |
GCT | 从启动开始执行所有gc所耗总时间,单位s |
参数 | 含义 |
---|---|
S0 | Survivor的S0区域当前使用比例 |
S1 | Survivor的S1区域当前使用比例 |
E | Eden区域当前使用比例 |
O | Old区域当前使用比例 |
M | 元数据区域当前使用比例 |
CCS | 压缩区域当前使用比例 |
YGC | 从启动开始执行的Young gc次数 |
FGC | 从启动开始执行的Full gc次数 |
FGCT | 从启动开始执行Full gc所耗总时间,单位s |
GCT | 从启动开始执行所有gc所耗总时间,单位s |
优化重点: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过高进程的方法,如果查找出来是代码的问题,我们就得优化自己的代码了。
Arthas官方文档地址
https://alibaba.github.io/arthas
简述:Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。
Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
1、下载安装包
使用git下载,输入命令:
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
2、安装安装包
将下载好的jar文件放在Linux环境下,输入命令:
java -jar arthas-boot.jar
出现如下截图即可即为启动
1、当程序运行时,启动Arthas。
当出现如下图所示时,即表示成功,我现在linux系统上只有一个进程,所以图中只显示了一个。
2、当我按1并回车之后,Arthas将挂载在我们序号为1的那个进程上,我们就可以进一步分析,第一次进会有点慢,稍微等待一下,结果如下图所示:
此命令可显示出Arthas为我们提供的所有命令以及命令的作用,详情如下图:
中文意思“仪表盘”
输入命令之后可显示当前进程的所有线程信息,堆信息,gc信息以及运行程序的底层java版本等信息,这个结果还是动态的,每几秒刷新一下,详情如下图所示:
在上图中我们发现有一个线程占用CPU特别高,我们可以通过这条命令看到这个线程的堆栈信息,以查找问题所在,通过图中红框中的信息,可以看到问题代码的所在,详情如下图所示:
打印出当前进程的所有线程信息,详情如下图所示:
打印出当前进程的所有死锁信息,详情如下图所示:
可以反编译线上的.class文件的字节码,使得我们查看代码是否发布成功,详情如下图所示:
可以动态的去查阅或者修改线上代码变量的值
下面这是示例代码,通过这段代码操作
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 ‘@[email protected](“I am Test Data.”)’
ognl ‘@类名@变量名.add(“I am Test Data.”)’
注意:这里ognl后面的参数用单引号扩了起来(单引号为英文单引号)
详情如下图所示
拓展:用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"
概念:例如一些缓存的对象已经过了很长时间不再会用到,但是还没有被清理,就相当于是垃圾对象无法被清除但是占用着内存空间,导致老年代剩余空间不足,频繁的发生full gc,这就叫内存泄漏。
解决办法:比如我们可以考虑采用一些成熟的JVM级缓存框架来解决,好比ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存,将长期不用的缓存对象淘汰掉。
java -XX:+PrintFlagsInitial:表示打印出所有JVM参数的默认值选项。
java -XX:+PrintFlagsInitial:表示打印出所有JVM参数在程序运行时生效的值。