JVM运维相关

1. jvm运维相关小工具(线上慎用)

Java提供很多工具用于查看jvm的实时运行状态。

使用jps命令显示java进程:

jps # 显示机器上java进程,类似于ps aux|grep java
jps -mlvV # 显示java进程启动参数,可以用jps -help会显示出该参数

1.1 查看jvm内存信息

使用jstat显示gc详情:

jstat -gc <pid>
# 显示的是jvm的内存使用情况和gc次数时间等数据。内存大小单位是KB
# S0C S1C S0U S1U : 两个survivor区的当前大小和分配大小
# EC EU : Eden区的当前大小和分配大小
# OC OU : Old区当前大小和分配大小
# MC MU CCSC CCSU : 方法区和压缩类
# YGC YGCT : young区的gc次数和时间 单位秒
# FGC FGCT : fullGC的次数和时间 单位秒
# GCT  : GC总时间 单位秒

gc统计信息实用技巧

  1. 可以看出eden区、survivor区、old区的大小和比例,以及当前的使用情况。正常情况下,这个数值不太能代表什么,即便快满了,可能只是刚好还没GC而已。

  2. YGC次数和FGC次数比例、YGCT和FGCT时间比例,应该趋于一个比较稳定的值,这个值项目不同而不同,一般YGCT是FGCT的10倍以上。

  3. GCT总GC时间,占ps aux显示的整个进程的CPU时间,应该不超过10%,一般只有1%这样。


使用jstat查看其它信息:

jstat -class <pid> # 查看加载的类
#Loaded  Bytes  Unloaded  Bytes     Time
#  7545 13542.6       49    70.6      18.35
# Loaded 加载的class数,Bytes占用的空间大小

使用jmap查看当前java映射的本地文件,加载class等信息:

jmap <pid> # 映射的文件
jmap -histo <pid> # jvm对象实例个数和内存占用,可以grep一下项目自己的包名,观察有没有可能内存泄漏
jmap -heap <pid> # jvm内存的使用情况
jmap -dump:format=b,file=temp_heapdump.hprof <pid> # 手工dump出heap文件

手动触发GC

jcmd <pid> GC.run

显示jvm当前的线程

jstack -l <pid> # -l是打印lock锁信息

TODO 这块的状态待细化

拿jvm当前所有线程的状态和堆栈,这个代码用code也能获取到线程的状态包括:
1) Runnable 程序可运行或正在运行
2) wait on condition 该状态出现在线程等待某个条件的发生。一般是在等网络或磁盘io,或者等待Thread
3) Waiting for monitor entry 和 in Object.wait()

显示jvm的详情信息

jinfo <pid> # 包括环境变量、VM Flags、命令行等

jmx远程debug

开启jmx使用jconsole可视化工具查看jvm运行情况,用于本地开发,打开jmx的jvm选项:

-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.port=1616
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

一些开源的小工具:jvmtop

2. 常用配置

简单的设置java内存和MetaspaceSize/Permsize内存大小:

-Xms128M -Xmx512M -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=128M # 直接紧跟在java命令后面,适合java8

-Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M #直接紧跟在java命令后面,适合java6

调整栈内存大小

在内存空间不足情况下,可以省些内存:

java -Xss512k 其它参数...

设置GC日志

-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:gc.log #直接跟在java命令后面

gclog默认打在标准输出中,加上-Xloggc:可以指定其打在指定文件中。这篇博客快速解读GC日志。关键词:

词汇 说明
DefNew 一个清理Young区的单线程、mark-copy stop-the-world内存回收器,young区包括eden、survivor1和suvivor2
Tenured 老年区,old区
Metaspace 元数据区,也是以前java1.6的perm区

GC日志揭示了一些重要信息:1. GC频率 2. GC时长 3. 每个分代内存使用数量和使用率,可以看出堆总大小。4. 每次GC回收前和回收后的内存数。

如果java程序启动稳定后,GC出现的频率很频繁(几秒一次)、GC的时间很久(正常情况下minor GCs只要几毫秒,fullGC大概一秒),那内存的使用就有问题。

3. 本地可视化工具

JProfiler本地分析(功能强大,适合开发环境)

JProfiler是个很全面好用的分析工具,但是它只适合于本地使用,线上环境是不建议用的。
一般我认为,只要Java可以提供的分析工具,都可以在JProfiler中找到。
一般JVM分析问题,会从内存和CPU(线程)分析入手。JProfiler可以做到:

**查看jvm内存所有对象个数和占用内存大小** 优化功能:1.动态每秒刷新一次,支持filter选出想看的类2.记录某一时间段的创建的对象数
**jvm堆内存变化图**:Eden、s0s1、old、perm区 画成图,看得出各个区的内存变化
查看线程、当前状态、当前线程堆栈、线程数 优化功能:现场动态图显示不足:没有统计此刻cpu时间占比和历史cpu总时间和占比
**JEE组件**:jdbc链接socket链接文件操作等 

Java VisualVM(jdk自带,简易,产品感更好)

在oracle jdk的bin目录下,找到jvisualvm.exe,以管理员身份运行即可。

**jvm内存** dump堆内存,查看内存中所有数据也可以分析heap dump文件

可以从heap dump文件中拿到:

1) 基本概要信息:类数、实例数、字节数、系统属性、当前jstack线程栈,线程状态分析参考这篇博客

2)所有加载的类和实例数,每个实例的具体数据和依赖关系

JVisualVM还自带插件,有:

1) Visual GC 可以看到java堆内存的占用图和时间图表

Eclipse Memory Analyzer (MAT)(有eclipse插件)

使用方式:切换到Memory Analysis视图,然后菜单栏File下可以打开堆栈文件。

**jvm内存** 可以分析一个实例所关联的所有对象的保留内存大小根据我的finance那个项目的测试,数据要比visualvm准确同样可以筛选但是数据下钻的能力不如jvisualvm,但不关键,只是体验不太好;但是MAT有显示没有map的占内存百分比,这点很舒服

**检测可能的内存泄漏** 找到最大的几个对象,按大小排序最大对象的分析path to GC root 路径分析。可以按照线程栈方式,查看栈引用的对象的个数,然后再根据线程堆栈,找到执行对应的方法。

**thread线程列表** 这一点也做得比jvisualvm好,但是居然没有线程的状态每个线程有个threadStatus的字段:这个字段好像没有枚举,和不同的操作系统有关

**对象引用列表,双向**

3. 常用操作

分析哪个java线程最耗CPU

首先,linux下是没有真正的线程,线程是靠进程来实现的,所以,通过:

top -H    # -H 显示出线程进程来

找到java最耗CPU的线程,记下这个线程id,转成16进制。例如,28938的16进制是0x710A.
然后执行:

jstack <pid>  # grep 一下上面那个二进制值,可以找到nid=对应的那个线程,这个就是耗CPU的线程

其它参考:https://blog.csdn.net/guixunlong/article/details/8450897

分析哪个java线程的栈引用对象内存大小最大

通过上面提到的MAT工具,分析dump出来的heap文件,可以从线程的角度查内存泄漏问题。点击MAT工具栏中”黄色齿轮状”的图标,即可以线程为单位看引用的堆大小,从而知道代码执行到哪里出现的问题。

还有另外一种方式,可以看到线程对应的引用堆,每个对象的数量及内存大小的统计。首先,点击MAT工具栏的“Histogram”查看内存堆中,最耗内存的对象,然后在该对象上,右键选择“Merge Shortest Path to GC Roots - with all references”,就可以看到了。

文档更新时间: 2020-08-26 13:39   作者:nick