• JVisualVM简介与内存泄漏实战分析


    一、JVisualVM能做什么
          VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe),能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。
         在JDK_HOME/bin(默认是C:Program FilesJavajdk1.6.0_13in)目录下面,有一个jvisualvm.exe文件,双击打开,从UI上来看,这个软件是基于NetBeans开发的了。
         可以进行远程和本地监控。远程监控需要打开jmx,下面内容会提到。
         其默认页面为:
    左侧分为本地和远程。双击本地中VisualVM线程,可以看到如下监控内容:
    具体的介绍参看:
    二、准备模拟内存泄漏demo
          1、定义静态变量HashMap
          2、分段循环创建对象,并加入HashMap
          代码如下:
    1. import java.util.HashMap;  
    2. import java.util.Map;  
    3. public class CyclicDependencies {  
    4.     //声明缓存对象  
    5.     private static final Map map = new HashMap();  
    6.     public static void main(String args[]){  
    7.         try {  
    8.             Thread.sleep(10000);//给打开visualvm时间  
    9.         } catch (InterruptedException e) {  
    10.             e.printStackTrace();  
    11.         }  
    12.         //循环添加对象到缓存  
    13.         for(int i=0; i<1000000;i++){  
    14.             TestMemory t = new TestMemory();  
    15.             map.put("key"+i,t);  
    16.         }  
    17.         System.out.println("first");  
    18.         //为dump出堆提供时间  
    19.         try {  
    20.             Thread.sleep(10000);  
    21.         } catch (InterruptedException e) {  
    22.             e.printStackTrace();  
    23.         }  
    24.         for(int i=0; i<1000000;i++){  
    25.             TestMemory t = new TestMemory();  
    26.             map.put("key"+i,t);  
    27.         }  
    28.         System.out.println("second");  
    29.         try {  
    30.             Thread.sleep(10000);  
    31.         } catch (InterruptedException e) {  
    32.             e.printStackTrace();  
    33.         }  
    34.         for(int i=0; i<3000000;i++){  
    35.             TestMemory t = new TestMemory();  
    36.             map.put("key"+i,t);  
    37.         }  
    38.         System.out.println("third");  
    39.         try {  
    40.             Thread.sleep(10000);  
    41.         } catch (InterruptedException e) {  
    42.             e.printStackTrace();  
    43.         }  
    44.         for(int i=0; i<4000000;i++){  
    45.             TestMemory t = new TestMemory();  
    46.             map.put("key"+i,t);  
    47.         }  
    48.         System.out.println("forth");  
    49.         try {  
    50.             Thread.sleep(Integer.MAX_VALUE);  
    51.         } catch (InterruptedException e) {  
    52.             e.printStackTrace();  
    53.         }  
    54.         System.out.println("qqqq");  
    55.     }  
    56. }  
    import java.util.HashMap;
    import java.util.Map;
    public class CyclicDependencies {
        //声明缓存对象
        private static final Map map = new HashMap();
        public static void main(String args[]){
            try {
                Thread.sleep(10000);//给打开visualvm时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //循环添加对象到缓存
            for(int i=0; i<1000000;i++){
                TestMemory t = new TestMemory();
                map.put("key"+i,t);
            }
            System.out.println("first");
            //为dump出堆提供时间
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i=0; i<1000000;i++){
                TestMemory t = new TestMemory();
                map.put("key"+i,t);
            }
            System.out.println("second");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i=0; i<3000000;i++){
                TestMemory t = new TestMemory();
                map.put("key"+i,t);
            }
            System.out.println("third");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i=0; i<4000000;i++){
                TestMemory t = new TestMemory();
                map.put("key"+i,t);
            }
            System.out.println("forth");
            try {
                Thread.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("qqqq");
        }
    }
         3、配置jvm参数如下:
     
    1. -Xms512m  
    2. -Xmx512m  
    3.  -XX:-UseGCOverheadLimit  
    4.  -XX:MaxPermSize=50m  
             -Xms512m
             -Xmx512m
              -XX:-UseGCOverheadLimit
              -XX:MaxPermSize=50m
         4、运行程序并打卡visualvm监控
    三、使用jVisualvm分析内存泄漏
          1、查看Visual GC标签,内容如下,这是输出first的截图
                
    这是输出forth的截图:
    通过2张图对比发现:
    老生代一直在gc,当程序继续运行可以发现老生代gc还在继续:
    增加到了7次,但是老生代的内存并没有减少。说明存在无法被回收的对象,可能是内存泄漏了。
    如何分析是那个对象泄漏了呢?打开抽样器标签:点击后如下图:
    按照程序输出进行堆dump,当输出second时,dump一次,当输出forth时dump一次。
    进入最后dump出来的堆标签,点击类:
    点击右上角:“与另一个堆存储对比”。如图选择第一次导出的dump内容比较:
    比较结果如下:
    可以看出在两次间隔时间内TestMemory对象实例一直在增加并且多了,说明该对象引用的方法可能存在内存泄漏。
    如何查看对象引用关系呢?
    右键选择类TestMemory,选择“在实例视图中显示”,如下所示:
    左侧是创建的实例总数,右侧上部为该实例的结构,下面为引用说明,从图中可以看出在类CyclicDependencies里面被引用了,并且被HashMap引用。
    如此可以确定泄漏的位置,进而根据实际情况进行分析解决。
    四、jVisualvm远程监控tomcat
         1、修改远程tomcat的catalina.sh配置文件,在其中增加:
             
    1. JAVA_OPTS="$JAVA_OPTS  
    2.           -Djava.rmi.server.hostname=192.168.122.128  
    3.           -Dcom.sun.management.jmxremote.port=18999  
    4. -Dcom.sun.management.jmxremote.ssl=false  
    5. -Dcom.sun.management.jmxremote.authenticate=false"  
    JAVA_OPTS="$JAVA_OPTS
              -Djava.rmi.server.hostname=192.168.122.128
              -Dcom.sun.management.jmxremote.port=18999
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false"
    这次配置先不走权限校验。只是打开jmx端口。
        2、打开jvisualvm,右键远程,选择添加远程主机:
              
       3、输入主机的名称,直接写ip,如下:
              
            右键新建的主机,选择添加JMX连接,输入在tomcat中配置的端口即可。
       4、双击打开。完毕!
    五、扩展知识
    线程死锁侦测
    jvm优化建议
         本质上是减少GC的次数。
    如果是频繁创建对象的应用,可以适当增加新生代大小。常量较多可以增加持久代大小。对于单例较多的对象可以增加老生代大小。比如spring应用中。
    GC选择,在JDK5.0以后,JVM会根据当前系统配置进行判断。一般执行-Server命令便可以。gc包括三种策略:串行,并行,并发。
    吞吐量大大应用,一般采用并行收集,开启多个线程,加快gc的是否。
    响应速度高的应用,一般采用并发收集,比如应用服务器。
         年老代建议配置为并发收集器,由于并发收集器不会压缩和整理磁盘碎片,因此建议配置:
     
    1. -XX:+UseConcMarkSweepGC  #并发收集年老代  
    2.  -XX:CMSInitiatingOccupancyFraction=80 # 表示年老代空间到80%时就开始执行CMS  
    3.  -XX:+UseCMSCompactAtFullCollection # 打开对年老代的压缩。可能会影响性能,但是可以消除内存碎片。   
    4.  -XX:CMSFullGCsBeforeCompaction=10 # 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此参数设置运行次  
        -XX:+UseConcMarkSweepGC  #并发收集年老代
         -XX:CMSInitiatingOccupancyFraction=80 # 表示年老代空间到80%时就开始执行CMS
         -XX:+UseCMSCompactAtFullCollection # 打开对年老代的压缩。可能会影响性能,但是可以消除内存碎片。 
         -XX:CMSFullGCsBeforeCompaction=10 # 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此参数设置运行次
    FullGC以后对内存空间进行压缩、整理。 
    直接运行linux上的jvisualvm
         下载X-Manager,可以将试图展现在本地机器上。
    不受此jvm支持
         保证jvisualvm所属jdk版本和linux上一致。
  • 相关阅读:
    494. Target Sum 添加标点符号求和
    636. Exclusive Time of Functions 进程的执行时间
    714. Best Time to Buy and Sell Stock with Transaction Fee有交易费的买卖股票
    377. Combination Sum IV 返回符合目标和的组数
    325. Maximum Size Subarray Sum Equals k 和等于k的最长子数组
    275. H-Index II 递增排序后的论文引用量
    274. H-Index论文引用量
    RabbitMQ学习之HelloWorld(1)
    java之struts2的数据处理
    java之struts2的action的创建方式
  • 原文地址:https://www.cnblogs.com/firstdream/p/7810094.html
Copyright © 2020-2023  润新知