微信公众号【Java技术江湖】一位阿里 Java 工程师的技术小站。做者黄小斜,专一 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!(关注公众号后回复”Java“便可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送做者原创的Java学习指南、Java程序员面试指南等干货资源)
VisualVM是一款免费的JAVA虚拟机图形化监控分析工具。html
1. 拥有图形化的监控界面。
2. 提供本地、远程的JVM监控分析功能。
3. 是一款免费的JAVA工具。
4. VisualVM拥有丰富的插件支持。java
VisualVM官方网站:http://visualvm.java.net/程序员
VisualVM各版本下载页面: http://visualvm.java.net/releases.html面试
下载VisualVM时也应该注意,不一样的JDK版本对应不一样版本的VisualVM,具体根据安装的JDK版原本下载第一的VisualVM。数据库
下载版本参考:[](http://blog.csdn.net/chwshuan...Java虚拟机性能管理神器 - VisualVM(4) - JDK版本与VisualVM版本对应关系安全
备注:下列表中显示1.3.6版本只适合JDK7和JDK8,但是我用1.3.6版仍是能够监控JDK1.6_45的版本。性能优化
显示JAVA应用程序JVM参数,系统属性,JVM的信息和运行环境。服务器
能够链接到远程服务器上运行的JAVA应用程序,监控应用程序的运行状态。微信
能够监控到应用程序热点方法的执行单次时间、总耗时、耗时占比。网络
显示应用程序在运行时的编译时间、加载时间、垃圾回收时间、内存区域的回收状态等。
监控应用程序线程的运行、休眠、等待、锁定状态。
显示线程当前运行状态和关联类信息。
另外还提供更多更强大、方便的第三方插件。
Java虚拟机性能管理神器 - VisualVM(2) 监控远程主机上的JAVA应用程序
使用VisualVM监控远程主机上JAVA应用程序时,须要开启远程主机上的远程监控访问,或者在远程JAVA应用程序启动时,开启远程监控选项,两种方法,选择其中一种就能够开启远程监控功能,配置完成后就能够在本地对远程主机上的JAVA应用程序进行监控。
在JAVA_HOME/bin目录中,建立名称为jstatdAllPolicy文件(这个文件名称也能够顺便起,不过要与jstatd启动时指定名称相同),将如下内容拷贝到文件中。并保证文件的权限和用户都正确。
grant codebase"file:${java.home}/../lib/tools.jar"{ permission java.security.AllPermission; };
在JAVA_HOME/bin目录中,执行如下命令:
./jstatd -J-Djava.security.policy=jstatdAllPolicy-p 1099 -J-Djava.rmi.server.hostname=192.168.xxx.xxx
jstatd命令描述以及参数说明:
jstatd是一个基于RMI(Remove Method Invocation)的服务程序,它用于监控基于HotSpot的JVM中资源的建立及销毁,而且提供了一个远程接口容许远程的监控工具链接到本地的JVM执行命令。
-J-Djava.security.policy=jstatdAllPolicy 指定安全策略文件名称
-p 1099 指定启动端口
-J-Djava.rmi.server.hostname=192.168.xxx.xxx 指定本机IP地址,在hosts文件配置不正常时使用,最好加上。
在须要远程监控的JVM启动时,开启远程监控选项
-Dcom.sun.management.jmxremote.port=1099
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=192.168.xxx.xxx
在本地VisualVM的应用程序窗口,右键单击【远程】》【添加远程主机】》【主机名】中输入远程主机的IP地址,点击【高级设置】输入远程主机开启的监控端口,点击【肯定】完成配置。
若是一切正常,就能够看到远程主机上的JAVA应用程序了。
Java虚拟机性能管理神器 - VisualVM(3) 排查JAVA应用程序内存泄漏
线上应用部署完成后,运行1~2天左右就会出现假死,或者某天早上8~10点高峰期间忽然不处理数据了。因为在测试环境的压力测试没有作彻底,也没有遇到相关问题。状况出现后对客户的使用形成很大影响,领导要求赶忙排查出问题缘由!
排查缘由前,与运维沟通,了解线上服务器的运行状态,经过ganglila观察网络、CPU、内存、磁盘的运行历史状态,发现程序故障前,都有一波很高的负载,排查线上日志,负载来源在8~9点平台接入数据量成倍增长,经过与产品和市场人员分析,此时段是用户集中上班、接入平台的高峰时段,访问日志也显示,业务场景正常,无网络攻击和安全问题。属于产品业务正常的场景。
排除了网络安全因素后,就从程序的运行内部进行排查,首先想到的获取JVM的dmp文件。获取JVM的dmp文件有两中方式:
1. JVM启动时增长两个参数,出现 OOME 时生成堆 dump:
-XX:+HeapDumpOnOutOfMemoryError
生成堆文件地址:
-XX:HeapDumpPath=/home/test/jvmlogs/
2. 发现程序异常前经过执行指令,直接生成当前JVM的dmp文件,15434是指JVM的进程号
jmap -dump:format=b,file=serviceDump.dat 15434
因为第一种方式是一种过后方式,须要等待当前JVM出现问题后才能生成dmp文件,实时性不高,第二种方式在执行时,JVM是暂停服务的,因此对线上的运行会产生影响。因此建议第一种方式。
获取到dmp文件后,就开始进行分析。将服务器上的dmp文件拷贝到本地,而后启动本地的VisualVM,点击菜单栏【文件】选项,装入dmp文件
打开dmp文件后,查看类标签,就能看到占用内存的一个排行。
而后经过检查中查找最大的对象,排查到具体线程和对象。
上列中的com.ctfo.trackservice.handler.TrackHandleThread#4就是重点排查对象。
经过代码的比对,在此线程中,有调用DAO接口,负责将数据存储到数据库中。而存储到数据库中时,因为存储速度较慢,致使此线程中的数据队列满了,数据积压,没法回收致使了队列锁定,结果就是程序假死,不处理数据。
经过进一步分析,发现数据库存储时有瓶颈,虽然当前是批量提交,速度也不快。平均8000/秒的存储速度。而数据库有一个DG(备份)节点,采用的是同步备份方式,即主库事务要等DG的事务也完成后才能返回成功,这样就会由于网络因素、DG性能因素等缘由致使性能降低。经过与DBA、产品、沟通,将同步备份改成异步备份,实时同步改成异步(异步可能会致使主备有10分钟之内的数据延迟)。速度达到30000/秒。问题解决。
至此,经过VisualVM分析java程序内存泄漏到此结束。不过还有几个问题:1. 若是dmp文件较大,VisualVM分析时间可能好久;另外,VisualVM对堆的分析显示功能还不算全面。若是须要更全面的显示,就可使用另一个专业的dmp文件分析工具【Memory Analyzer (MAT)】,此工具能够做为eclipse的插件进行安装,也能够单独下载使用。若是有感兴趣的朋友,我我的建议仍是单独下载使用。下载地址:http://www.eclipse.org/mat/
Java虚拟机性能管理神器 - VisualVM(4) 查找JAVA应用程序耗时的方法函数
JAVA程序在开发前,根据设计文档的性能需求,是要对程序的性能指标进行测试的。好比接口每秒响应次数要求1000次/秒,就须要平均每次请求处理的时间在1ms之内,若是须要知足这个指标,就须要在开发阶段对接口执行函数进行监控,也能够经过打印日志进行监控,从而统计对应的性能指标,而后能够根据性能指标的要求进行相应优化。
根据具体业务的场景和需求,主要集中在IO通信、文件读写、数据库操做、业务逻辑处理上,这些都是制约性能的重要因素,因此须要重点关注。
在研发环境,大部分会使用syso的方式或者日志方式打印性能损耗,若是代码没有加在运行时才想起来,或者想关注忽然想起的函数,换作之前,是须要重启服务的,若是有VisualVM就能够直接查看耗时以及调用次数等状况。而不用打印、输出日志来查看性能损耗。
对于性能损耗的函数,根据业务逻辑能够进行相应的优化,例如字符串处理、文件读写方式、SQL语句优化、多线程处理等等方式。
因为性能优化涉及的内容不少,这里就不深刻了。主要是告诉你们经过VisualVM来排查问题的具体位置。
Java虚拟机性能管理神器 - VisualVM(5) 排查JAVA应用程序线程锁
JAVA线程锁的例子和缘由网上一大堆,我也不在这里深刻说明,这里主要是否讲如何使用VisualVM进行排查。至于例子能够看这里:http://blog.csdn.net/fengzhe0411/article/details/6953370
这个例子比较极端,通常状况下,出现锁竞争激烈是比较常见的。
启动 VisualVM,在应用程序窗口,选择对应的JAVA应用,在详情窗口》线程标签(勾选线程可视化),查看线程生命周期状态,主要留意线程生命周期中红色部分。
(1)绿色:表明运行状态。通常属于正常状况。若是是多线程环境,生产者消费者模式下,消费者一直处于运行状态,说明消费者处理性能低,跟不上生产者的节奏,须要优化对应的代码,若是不处理,就可能致使消费者队列阻塞的现象。对应线程的【RUNNABLE】状态。
(2)蓝色:表明线程休眠。线程中调用Thread.sleep()函数的线程状态时,就是蓝色。对应线程的【TIMED_WAITING】状态。
(3)黄色:表明线程等待。调用线程的wait()函数就会出现黄色状态。对应线程的【WAITING】状态。
(4)红色:代码线程锁定。对应线程的【BLOCKED】状态。
发生线程锁的缘由有不少,我所遇到比较多的状况是多线程同时访问同一资源,且此资源使用synchronized关键字,致使一个线程要等另一个线程使用完资源后才能运行。例如再没有链接池的状况下,同时访问数据库接口。这种状况会致使性能的极具降低,解决的方案是增长链接池,或者修改访问方式。或者将资源粒度细化,相似ConCurrentHashMap中的处理方式,将资源分为多个更小粒度的资源,在更小粒度资源上来处理锁,就能够解决资源竞争激烈的问题。]