JVM 监控

昨天天发现Tomcat占用2G物理内存,11G虚拟内存,感觉就起了一个web项目,而且是在内网,没几个人用呀。内存占用怎么这么高。

Tomcat使用的大多是默认配置。就改了个conf/context.xml server.xml

遇到问题了,先想想怎么解决,暂时能想到的是用分析工具分析一下了。我用的是jdk1.8.0_111, ${JAVA_HOME}/bin目录里有两个工具jmc.exe jvisualvm.exe
这两个工具就是今天的主角了。用来监控JVM的。

用这两个工具监控本地的java程序特别简单,打开找到运行的程序就能用。但是监控远程的java程序就得没那么简单了。先看看前辈们的作法 使用本地JConsole监控远程JVM(最权威的总结) jvisualvm远程监控Tomcat
他们虽然写的挺好的,但是太啰嗦,对于想要快速解决问题的同志们,想要通篇看完文章有点难度。

JDK里自带的jmc和jvisualvm

JMC

JMC新建连接
JMC-MBean
JMC飞行记录器

VisualVM

VisualVM新建连接
VisualVM监控信息

连接远程的配置

上硬货。

1
2
3
4
5
6
7
# 修改配置文件 ${TOMCAT_HOME}/bin/catalina.sh   103行左右,添加以下内容
# OS specific support. $var _must_ be set to either true or false.
if [ "$1" = "start" ];then
JAVA_OPTS="-Xms256m -Xmx2048m -XX:PermSize=128M -XX:MaxPermSize=256m -Dcom.sun.management.jmxremote.port=6688 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management .jmxremote.authenticate=false -XX:+UnlockCommercialFeatures -XX:+FlightRecorder"
else
echo "shutdown";
fi;

1
2
3
4
5
6
7
8
9
10
11
// 参数解释
-Xms256m #JVM初始分配的堆内存
-Xmx2048m #JVM最大允许分配的堆内存,按需分配
-XX:PermSize=128M #JVM初始分配的非堆内存
-XX:MaxPermSize=256M #JVM最大允许分配的非堆内存,按需分配
-Dcom.sun.management.jmxremote.port #这个是配置远程 connection 的端口号的,要确定这个端口没有被占用
-Dcom.sun.management.jmxremote.ssl=false #是否启用ssl
-Dcom.sun.management.jmxremote.authenticate=false #指定了JMX 是否启用鉴权(需要用户名,密码鉴权)
-Djava.rmi.server.hostname #这个是配置server的IP的,可以不配置
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder #不用于商业用途(使用JMC bean时要配置该项)
-Dcom.sun.management.jmxremote.pwd.file #密码文件路径(例如: /root/soft/jdk8/jre/lib/management/jmxremote.password) 不使用密码可以不配置该项

还有加if else的原因:

1
2
Error: Exception thrown by the agent : java.rmi.server.ExportException: Port already in use: 6688; nested exception is: 
java.net.BindException: Address already in use (Bind failed)

如果不加if else,在关闭tomcat的时候会报错,提示端口已经占用。

1
2
3
4
5
6
7
8
9
10
11
12
# // 也可以使用下面这种配置,和上面效果一样
JAVA_OPTS="$JAVA_OPTS -Xms256m -Xmx1024m -XX:PermSize=128M -XX:MaxPermSize=256m"
if [ "$1" = "start" ];then
echo "set console";
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=6688 -Djava.rmi.server.hostname=192.168.1.6";
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false";
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false";
# JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.pwd.file=/root/soft/jdk8/jre/lib/management/jmxremote.password"
JAVA_OPTS="$JAVA_OPTS -XX:+UnlockCommercialFeatures -XX:+FlightRecorder
else
echo "shutdown";
fi;
  1. 虚拟内存大可能是程序mmap了什么大文件过着tomcat映射了一部分用户空间的内存地址到物理内存,方便后续直接操作而避免内存拷贝(请搜索zero copy概念),比如nio的很多方法都用mmap实现的。
  2. 虚拟内存越大,说明有更多的物理空间被映射到用户空间的地址,避免你访问物理内存的时候内存拷贝,是好事。
  3. top的cpu显示的是瞬时值,只有参考意义,实际意义不大,应该更关注平均值。另外很多人觉得cpu高很可怕,其实正好相反,高了说明你的程序iowait少啊,是好事啊,除非是程序的计算量预期很低但是实际很高。

References

[1] 使用本地JConsole监控远程JVM(最权威的总结)
[2] jvisualvm远程监控Tomcat
[3] 配置远程JConsole
[4] Tomcat虚拟内存占用很高?
[5] security-windows
[6] agent