# 目录
  * [一、VisualVM是什么？](#一、visualvm是什么？)
  * [二、如何获取VisualVM？](#二、如何获取visualvm？)
  * [三、获取那个版本？](#三、获取那个版本？)
  * [四、VisualVM能做什么？](#四、visualvm能做什么？)
    * [监控远程主机上的JAVA应用程序](#监控远程主机上的java应用程序)
  * [排查JAVA应用程序内存泄漏](#排查java应用程序内存泄漏)
  * [查找JAVA应用程序耗时的方法函数](#查找java应用程序耗时的方法函数)
  * [排查JAVA应用程序线程锁](#排查java应用程序线程锁)
  * [参考文章](#参考文章)


本文转自互联网，侵删

本系列文章将整理到我在GitHub上的《Java面试指南》仓库，更多精彩内容请到我的仓库里查看
> https://github.com/h2pl/Java-Tutorial

喜欢的话麻烦点下Star哈

文章将同步到我的个人博客：
> www.how2playlife.com

本文是微信公众号【Java技术江湖】的《深入理解JVM虚拟机》其中一篇，本文部分内容来源于网络，为了把本文主题讲得清晰透彻，也整合了很多我认为不错的技术博客内容，引用其中了一些比较好的博客文章，如有侵权，请联系作者。

该系列博文会告诉你如何从入门到进阶，一步步地学习JVM基础知识，并上手进行JVM调优实战，JVM是每一个Java工程师必须要学习和理解的知识点，你必须要掌握其实现原理，才能更完整地了解整个Java技术体系，形成自己的知识框架。

为了更好地总结和检验你的学习成果，本系列文章也会提供每个知识点对应的面试题以及参考答案。

如果对本系列文章有什么建议，或者是有什么疑问的话，也可以关注公众号【Java技术江湖】联系作者，欢迎你参与本系列博文的创作和修订。

<!-- more -->

## 一、VisualVM是什么？
    VisualVM是一款免费的JAVA虚拟机图形化监控分析工具。
    1.  拥有图形化的监控界面。
    2. 提供本地、远程的JVM监控分析功能。
    3. 是一款免费的JAVA工具。
    4. VisualVM拥有丰富的插件支持。
## 二、如何获取VisualVM？
    VisualVM官方网站：http://visualvm.java.net/

    VisualVM各版本下载页面: http://visualvm.java.net/releases.html

     下载VisualVM时也应该注意，不同的JDK版本对应不同版本的VisualVM，具体根据安装的JDK版本来下载第一的VisualVM。
## 三、获取那个版本？

       下载版本参考：Java虚拟机性能管理神器 - VisualVM（4） - JDK版本与VisualVM版本对应关系

备注：下列表中显示1.3.6版本只适合JDK7和JDK8，可是我用1.3.6版还是可以监控JDK1.6_45的版本。

## 四、VisualVM能做什么？

1. 显示JAVA应用程序配置和运行时环境。
显示JAVA应用程序JVM参数，系统属性，JVM的信息和运行环境。

 
2. 显示本地和远程JAVA应用程序运行状态。
可以连接到远程服务器上运行的JAVA应用程序，监控应用程序的运行状态。

3. 监控应用程序的性能消耗。
可以监控到应用程序热点方法的执行单次时间、总耗时、耗时占比。

 
4. 显示应用程序内存分配，显示分析堆信息。
显示应用程序在运行时的编译时间、加载时间、垃圾回收时间、内存区域的回收状态等。

 
5. 监控应用程序线程状态和生命周期。
监控应用程序线程的运行、休眠、等待、锁定状态。

 
6. 显示、分析线程堆信息。
显示线程当前运行状态和关联类信息。

 
7. 支持第三方插件来分析JAVA应用程序。
另外还提供更多更强大、方便的第三方插件。

 
### 监控远程主机上的JAVA应用程序

    使用VisualVM监控远程主机上JAVA应用程序时，需要开启远程主机上的远程监控访问，或者在远程JAVA应用程序启动时，开启远程监控选项，两种方法，选择其中一种就可以开启远程监控功能，配置完成后就可以在本地对远程主机上的JAVA应用程序进行监控。

1.远程服务器、应用程序配置
        1.1配合jstatd工具提供监控数据  
        1.1.1创建安全访问文件
        在JAVA_HOME/bin目录中，创建名称为jstatdAllPolicy文件（这个文件名称也可以顺便起，不过要与jstatd启动时指定名称相同），将以下内容拷贝到文件中。并保证文件的权限和用户都正确。

        grant codebase"file:${java.home}/../lib/tools.jar"{ permission java.security.AllPermission; };

 

1.1.2启动jstatd服务
        在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文件配置不正常时使用，最好加上。


1.2JVM启动时配置远程监控选项
        在需要远程监控的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

 

2.本地VisualVM配置
        在本地VisualVM的应用程序窗口，右键单击【远程】》【添加远程主机】》【主机名】中输入远程主机的IP地址，点击【高级设置】输入远程主机开启的监控端口，点击【确定】完成配置。



        如果一切正常，就可以看到远程主机上的JAVA应用程序了。



 

## 排查JAVA应用程序内存泄漏

1. 发现问题
    线上应用部署完成后，运行1~2天左右就会出现假死，或者某天早上8~10点高峰期间突然不处理数据了。由于在测试环境的压力测试没有做完全，也没有遇到相关问题。情况出现后对客户的使用造成很大影响，领导要求赶紧排查出问题原因！

2. 排查原因
        排查原因前，与运维沟通，了解线上服务器的运行状态，通过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是暂停服务的，所以对线上的运行会产生影响。所以建议第一种方式。

3. 解决方案
        获取到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应用程序耗时的方法函数

1.为什么要监控？
        JAVA程序在开发前，根据设计文档的性能需求，是要对程序的性能指标进行测试的。比如接口每秒响应次数要求1000次/秒，就需要平均每次请求处理的时间在1ms以内，如果需要满足这个指标，就需要在开发阶段对接口执行函数进行监控，也可以通过打印日志进行监控，从而统计对应的性能指标，然后可以根据性能指标的要求进行相应优化。

2. 那些方法函数需要监控？
        根据具体业务的场景和需求，主要集中在IO通讯、文件读写、数据库操作、业务逻辑处理上，这些都是制约性能的重要因素，所以需要重点关注。

        

3. 如何排查
        在研发环境，大部分会使用syso的方式或者日志方式打印性能损耗，如果代码没有加在运行时才想起来，或者想关注突然想起的函数，换做以前，是需要重启服务的，如果有VisualVM就可以直接查看耗时以及调用次数等情况。而不用打印、输出日志来查看性能损耗。



4. 如何处理
        对于性能损耗的函数，根据业务逻辑可以进行相应的优化，例如字符串处理、文件读写方式、SQL语句优化、多线程处理等等方式。
 
       由于性能优化涉及的内容很多，这里就不深入了。主要是告诉大家通过VisualVM来排查问题的具体位置。
 

 

## 排查JAVA应用程序线程锁

 

1. JAVA应用程序线程锁原因
        JAVA线程锁的例子和原因网上一大堆，我也不在这里深入说明，这里主要是否讲如何使用VisualVM进行排查。至于例子可以看这里：http://blog.csdn.net/fengzhe0411/article/details/6953370 

这个例子比较极端，一般情况下，出现锁竞争激烈是比较常见的。

2. 排查JAVA应用程序线程锁
       启动 VisualVM，在应用程序窗口，选择对应的JAVA应用，在详情窗口》线程标签（勾选线程可视化），查看线程生命周期状态，主要留意线程生命周期中红色部分。



（1）绿色：代表运行状态。一般属于正常情况。如果是多线程环境，生产者消费者模式下，消费者一直处于运行状态，说明消费者处理性能低，跟不上生产者的节奏，需要优化对应的代码，如果不处理，就可能导致消费者队列阻塞的现象。对应线程的【RUNNABLE】状态。

（2）蓝色：代表线程休眠。线程中调用Thread.sleep()函数的线程状态时，就是蓝色。对应线程的【TIMED_WAITING】状态。

（3）黄色：代表线程等待。调用线程的wait()函数就会出现黄色状态。对应线程的【WAITING】状态。

（4）红色：代码线程锁定。对应线程的【BLOCKED】状态。



3. 分析解决JAVA应用程序线程锁
        发生线程锁的原因有很多，我所遇到比较多的情况是多线程同时访问同一资源，且此资源使用synchronized关键字，导致一个线程要等另外一个线程使用完资源后才能运行。例如再没有连接池的情况下，同时访问数据库接口。这种情况会导致性能的极具下降，解决的方案是增加连接池，或者修改访问方式。或者将资源粒度细化，类似ConCurrentHashMap中的处理方式，将资源分为多个更小粒度的资源，在更小粒度资源上来处理锁，就可以解决资源竞争激烈的问题。]

## 参考文章

<https://segmentfault.com/a/1190000009707894>

<https://www.cnblogs.com/hysum/p/7100874.html>

<http://c.biancheng.net/view/939.html>

<https://www.runoob.com/>

https://blog.csdn.net/android_hl/article/details/53228348


