JDK提供命令工具
jstat
是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据,在没有 GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
语法:
jstat -option pid
其中option参数有如下:
-class (类加载器)
-compiler (JIT)
-gc (GC 堆状态)
-gccapacity (各区大小)
-gccause (最近一次 GC 统计和原因)
-gcnew (新区统计)
-gcnewcapacity (新区大小)
-gcold (老区统计)
-gcoldcapacity (老区大小)
-gcpermcapacity (永久区大小)
-gcutil (GC 统计汇总)
-printcompilation (HotSpot 编译统计)
查看GC情况命令:
jstat -gc pid 250 10
最后两个参数代表250毫秒查询一次,一共查询10次。
返回值列含义:
S0C:第一个幸存区(From 区)的大小
S1C:第二个幸存区(To 区)的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:(Eden)区的大小
EU:(Eden)区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
jinfo
查看和修改虚拟机的参数。
查看生产环境程序是否打印GC日志命令如下:
jinfo -flag PrintGC java进程Id
输出-XX:-PrintGC 是关闭状态,输出-XX:+PrintGC是开启状态。
动态开启GC日志命令:
jinfo -flag +PrintGC java进程Id
动态关闭GC日志命令:
jinfo -flag -PrintGC java进程Id
jmap
用于生成堆转储快照(一般称为 heapdump 或 dump 文件)。jmap 的作用并不仅仅是为了获取 dump 文件,它还可以查询 finalize 执行队列、Java 堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。和 jinfo 命令一样,jmap 有不少功能在 Windows 平台下都是受限的,除了生成 dump 文件的-dump 选项和用于查看每个类的实例、空间占用统计的-histo 选项在所有操作系统都提供之外,其余选项都只能在 Linux/Solaris 下使用。
打印heap概要信息:
jmap –heap <pid>
返回值含义:
Heap Configuration: ##堆配置情况,也就是 JVM 参数配置的结果[平常说的 tomcat 配置 JVM 参数,就是在配置这些]
MinHeapFreeRatio = 40 ##最小堆使用比例
MaxHeapFreeRatio = 70 ##最大堆可用比例
MaxHeapSize = 2147483648 (2048.0MB) ##最大堆空间大小
NewSize = 268435456 (256.0MB) ##新生代分配大小
MaxNewSize = 268435456 (256.0MB) ##最大可新生代分配大小
OldSize = 5439488 (5.1875MB) ##老年代大小
NewRatio = 2 ##新生代比例
SurvivorRatio = 8 ##新生代与 suvivor 的比例
PermSize = 134217728 (128.0MB) ##perm 区 永久代大小
MaxPermSize = 134217728 (128.0MB) ##最大可分配 perm 区 也就是永久代大小
Heap Usage: ##堆使用情况【堆内存实际的使用情况】
New Generation (Eden + 1 Survivor Space): ##新生代(伊甸区 Eden 区 + 幸存区 survior(1+2)空间)
capacity = 241631232 (230.4375MB) ##伊甸区容量
used = 77776272 (74.17323303222656MB) ##已经使用大小
free = 163854960 (156.26426696777344MB) ##剩余容量
32.188004570534986% used ##使用比例
Eden Space: ##伊甸区
capacity = 214827008 (204.875MB) ##伊甸区容量
used = 74442288 (70.99369812011719MB) ##伊甸区使用
free = 140384720 (133.8813018798828MB) ##伊甸区当前剩余容量
34.65220164496263% used ##伊甸区使用情况
From Space: ##survior1 区
capacity = 26804224 (25.5625MB) ##survior1 区容量
used = 3333984 (3.179534912109375MB) ##surviror1 区已使用情况
free = 23470240 (22.382965087890625MB) ##surviror1 区剩余容量
12.43827838477995% used ##survior1 区使用比例
To Space: ##survior2 区
capacity = 26804224 (25.5625MB) ##survior2 区容量
used = 0 (0.0MB) ##survior2 区已使用情况
free = 26804224 (25.5625MB) ##survior2 区剩余容量
0.0% used ## survior2 区使用比例
PS Old Generation: ##老年代使用情况
capacity = 1879048192 (1792.0MB) ##老年代容量
used = 30847928 (29.41887664794922MB) ##老年代已使用容量
free = 1848200264 (1762.5811233520508MB) ##老年代剩余容量
1.6416783843721663% used ##老年代使用比例
打印每个 class 的实例数目,内存占用,类全名信息.命令如下:
jmap –histo <pid>
jmap –histo:live <pid> #如果 live 子参数加上后,只统计活的对象数量.
jmap –histo pid | head -20 #只显示前20
生成堆的dump文件命令:
jmap -dump:live,format=b,file=heap.bin <pid>
jhat
加载dump文件命令:
jhat dump 文件名
后屏幕显示“Server is ready.”的提示后,用户在浏览器中键入http://localhost:7000/就可以访问详情
使用 jhat 可以在服务器上生成堆转储文件分析(一般不推荐,毕竟占用服务器的资源,比如一个文件就有 1 个 G 的话就需要大约吃一个 1G 的内存资源)。
dump是内存快照,可以通过dump文件查看.
jstack
一般来说 jstack 主要是用来排查是否有死锁的情况.
JVM调优方案
开启相关参数
-XX:-HeapDumpOnOutOfMemoryError 默认关闭,建议开启,在java.lang.OutOfMemoryError 异常出现时,输出一个 dump 文件,记录当时的堆内存快照。
-XX:HeapDumpPath=./java_pid.hprof 用来设置堆内存快照的存储文件路径,默认是 java 进程启动位置。
Arthas
Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断.
这里只简单介绍其有哪些功能,具体使用需在实战中体会。
dashboard命令,面板功能如下:
thread命令:
这个命令和 jstack 很相似,但是功能更加强大,主要是查看当前 JVM 的线程堆栈信息。同时可以结合使用 thread –b 来进行死锁的排查死锁。
参数解释:
-n 指定最忙的前 n 个线程并打印堆栈
-b 找出阻塞当前线程的线程
-i 指定 cpu 占比统计的采样间隔,单位为毫秒。
jad命令:
反编译指定已加载类的源码,需写明类全路径名,可以反编译出class源码,在生产环境可排查问题。
trace命令:
使用 trace 命令可以跟踪统计方法耗时。
命令汇总:
选择合适垃圾回收器
默认垃圾回收器STW时间较长,对于高性能系统,可以使用CMS或G1垃圾回收器,减少STW时间,提高性能。
GC调优策略
- 降低 Minor GC 频率
由于新生代空间较小,Eden 区很快被填满,就会导致频繁 Minor GC,因此我们可以通过增大新生代空间来降低 Minor GC 的频率。单次 Minor GC 时间是由两部分组成:T1(扫描新生代)和 T2(复制存活对象)。
在 JVM 中,复制对象的成本要远高于扫描成本。如果在堆内存中存在较多的长期存活的对象,此时增加年轻代空间,反而会增加 Minor GC 的时间。如果堆中的短期对象很多,那么扩容新生代,单次 Minor GC 时间不会显著增加。因此,单次 Minor GC 时间更多取决于 GC 后存活对象的数量,而非 Eden区的大小。
- 降低 Full GC 的频率
由于堆内存空间不足或老年代对象太多,会触发 Full GC,频繁的 Full GC 会带来上下文切换,增加系统的性能开销。通过减少创建大对象和增大堆内存空间来减少Full GC频率。
内存溢出与内存泄漏的区别
内存溢出: 单纯就是内存不够用了,通常报错Out of Memory。
内存泄漏: 没有用的对象在内存中无法通过gc清除,一直占用内存空间,称之为内存泄漏。内存泄漏不一定会内存溢出,但是一直泄露,最终一定会内存溢出。
CPU过高问题排查
- 使用top命令,查出CPU高的java进程pid。
- 使用top -p pid命令,单独查看步骤1找到的进程。
- 在步骤2监控界面输入H,获取当前进程下所有线程信息。
- 找到消耗CPU特别高的线程编号
- 执行jstack pid命令,对当前进程做dump,输出所有线程信息。
- 将步骤4得到的线程编号转为16进制。
- 根据第6步得到的线程编号在第5步线程信息里找对应线程内容
- 解读线程信息,定位具体代码位置。
总结:
如果 CPU 的 100%,要从两个角度出发,一个有可能是业务线程疯狂运行,比如说想很多死循环。还有一种可能性,就是 GC 线程在疯狂的回收,因为 JVM 中垃圾回收器主流也是多线程的,所以很容易导致 CPU 的 100%
Out of Memory问题排查
内存溢出一般都是某个对象过多导致,可使用如下命令查询哪个对象过多:
jmap –histo pid | head -20
然后分析对应类为什么会这么多,为什么不会被回收(可达性分析等),然后进一步分析原因。