内存溢出之后,分析原因往往有些困难,这里在启动jvm的时候可以增加一些参数,等内存溢出发生时jvm会帮我们记录当时的快照
这是我模拟内存溢出的启动参数:
-Xmx64m -Xms32m -Xmn16m -Xss8m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/liucheng/开发/vm/VMDemo.hprof -XX:OnOutOfMemoryError="/Users/liucheng/开发/vm/sh/clear.sh VMDemo"
设置jvm的内存大小相关参数我不说了,重点是后面几个参数
-XX:+HeapDumpOnOutOfMemoryError 当jvm发生内存溢出(om溢出)的时候进行快照记录;
-XX:HeapDumpPath 设置快照路径(可以是路径,也可以是具体到快照文件名),如果不设置,默认会在当前工作空间生成快照。
-XX:OnOutOfMemoryError 设置发生内存溢出后调用一个sh脚本(调用脚本更多的目的是将快照文件转移到其他服务器,因为快照文件非常大,经常发生om又没有及时清理快照文件,会很快让磁盘被塞满)
ok,开始正题,导致堆内存溢出的代码:
执行结果:
附上clear.sh脚本:
生成的快照文件:
如何分析快照文件,这里我推荐3种方式:
一、使用java自带的命令 jhat
执行 jhat -port 8888 VMDemo.hprof
不指定 -port 默认7000
访问 http://localhost:8888
这种方式会将快照信息以web的方式展现,但是展示信息有限。
浏览器访问 http://localhost:7000
这里面每个链接操作都有相应的说明,有兴趣的可以百度了解,这种方式我不太喜欢,没有过多了解
二、使用mat工具分析
在这里下载 https://www.eclipse.org/mat/
mat载入快照文件后:
这个把内存情况用饼形图表达的很清楚。
更多信息
通过上面这张图可以看出代码中的 list塞入了1407条类型为string的数据
三、使用visualVM
装载进快照文件后,选择线程项
以上是都是快照已经生成,事后来找问题的方案,针对正在运行中的jvm来说,可以用以下方式监控jvm各种参数起到调试作用。
一、使用visualVM
这种方式我就不多说了,除了本地也可以远程连接,这种都懂,下面我要介绍的是才是好用的
二、使用java自带的jconsole工具
运行 jconsole 稍等一下下出来一个界面
这次连接一个远程的jvm来示例
远程连接之前的准备工作:
在服务器上执行 hostname -i 得到的ip应该是-Djava.rmi.server.hostname的设置ip
然后用如下命令启动:
java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9990 -Dcom.sun.management.jmxremote.rmi.port=9990 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=al.cloud.net -jar eureka-0.0.1-SNAPSHOT.jar
这里公网ip就不对外公开了,我用本地hosts al.cloud.net映射了公网服务器ip,设置成你自己要连接的服务器上公网ip即可。
以下是参数解释
-Dcom.sun.management.jmxremote 布尔 是否支持远程JMX访问,默认true
-Dcom.sun.management.jmxremote.port 数值 监听端口号,方便远程访问
-Dcom.sun.management.jmxremote.rmi.port 数值 rmi端口,方便远程访问
-Dcom.sun.management.jmxremote.authenticate 布尔 是否需要开启用户认证,默认开启
-Dcom.sun.management.jmxremote.ssl 布尔 是否对连接开启SSL加密,默认开启
-Dcom.sun.management.jmxremote.access.file 路径 对访问用户的权限授权的文件的路径,默认路径JRE_HOME/lib/management/jmxremote.access
-Dcom.sun.management.jmxremote. password.file 路径 设置访问用户的用户名和密码,默认路径JRE_HOME/lib/management/ jmxremote.password
本地来测试一下服务器上的端口是否可以正常通信 telnet al.cloud.net 9990
开始连接
出现这个是因为ssl项设置为false了 不管它继续点击连接
后面的都知道操作了。
其实还有一些java自带的命令方式查看
比如:
当前线程所有堆栈信息 jstack pid
当前线程的相关信息输出到日志 kill -3 pid
查看gc信息
jstat -gc pid
gc信息 2000ms内输出一次 总共输出20次
jstat -gc pid 2000 20
同-gc,还会输出Java堆各区域使用到的最大、最小空间
jstat -gccapacity 进程id
同-gc,还会输出的是已使用空间占总空间的百分比
jstat -gcutil pid
垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
jstat -gccause pid
查看JIT编译过的方法数量耗时
jstat -compiler pid
查看类装载数量
jstat -class pid