• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

无信不立

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【jvisualvm】排查内存泄漏小案例

一、JVisualVM能做什么
      VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe),能够监控线程,内存情况,查看方法的CPU时间和内存中的对象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。
     在JDK_HOME/bin(默认是C:\Program Files\Java\jdk1.6.0_13\bin)目录下面,有一个jvisualvm.exe文件,双击打开,从UI上来看,这个软件是基于NetBeans开发的了。
 
具体的介绍参看:
http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/
二、准备模拟内存泄漏demo
      1、定义静态变量HashMap
      2、分段循环创建对象,并加入HashMap
      代码如下:
package com.spring.test.service.jvmtest;

import java.util.HashMap;
import java.util.Map;

/**
 *
 */
public class JvmMemoryTest {
    //声明缓存对象
    private static final Map map = new HashMap();

    public static void main(String args[]){
        try {
            Thread.sleep(100000);//给打开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");
    }

}





package com.spring.test.service.jvmtest;

/**
 * 
 */
public class TestMemory {
}
View Code
     3、配置jvm参数如下:
         -Xms512m
         -Xmx512m
         -XX:-UseGCOverheadLimit
         -XX:MaxPermSize=50m
     4、运行程序并打卡visualvm监控

 

三、使用jVisualvm分析内存泄漏
 1、查看Visual GC标签,内容如下,这是输出first的截图

这是输出forth的截图:

 老生代一直在gc,当程序继续运行可以发现老生代gc还在继续:增加到了10次,但是老生代的内存并没有减少。说明存在无法被回收的对象,可能是内存泄漏了。

如何分析是那个对象泄漏了呢?打开抽样器标签:点击后如下图:

按照程序输出进行堆dump,当输出first时,dump一次,当输出forth时dump一次。

 

进入最后dump出来的堆标签,点击类:点击右上角:“与另一个堆存储对比”。如图选择第一次导出的dump内容比较: 

比较的结果如下:(10:39:23和10:38:48)比较(上图的比较顺序反了,结果是类实例减少),发现TestMemory类的实例数,一直在增加,却没有被垃圾回收。 

可以看出在两次间隔时间内TestMemory对象实例一直在增加并且多了,说明该对象引用的方法可能存在内存泄漏。

如何查看,依赖关系。【点击类名,鼠标右键,选择在实例视图中显示】得到如下界面:

 

左侧是创建的实例总数,右侧上部为该实例的结构,下面为引用说明,从图中可以看出在类JvmMemoryTest里面被引用了,并且被HashMap引用。
如此可以确定泄漏的位置,进而根据实际情况进行分析解决。
五、扩展知识
线程死锁侦测
jvm优化建议
     本质上是减少GC的次数。
如果是频繁创建对象的应用,可以适当增加新生代大小。常量较多可以增加持久代大小。对于单例较多的对象可以增加老生代大小。比如spring应用中。
GC选择,在JDK5.0以后,JVM会根据当前系统配置进行判断。一般执行-Server命令便可以。gc包括三种策略:串行,并行,并发。
吞吐量大大应用,一般采用并行收集,开启多个线程,加快gc的是否。
响应速度高的应用,一般采用并发收集,比如应用服务器。
     年老代建议配置为并发收集器,由于并发收集器不会压缩和整理磁盘碎片,因此建议配置:
 
    -XX:+UseConcMarkSweepGC  #并发收集年老代
     -XX:CMSInitiatingOccupancyFraction=80 # 表示年老代空间到80%时就开始执行CMS
     -XX:+UseCMSCompactAtFullCollection # 打开对年老代的压缩。可能会影响性能,但是可以消除内存碎片。 
     -XX:CMSFullGCsBeforeCompaction=10 # 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此参数设置运行次

FullGC以后对内存空间进行压缩、整理。 
直接运行linux上的jvisualvm
     下载X-Manager,可以将试图展现在本地机器上。
不受此jvm支持
     保证jvisualvm所属jdk版本和linux上一致。

 

 

posted on 2019-05-15 10:24  无信不立  阅读(1909)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3