45-通过jmap来分析Java的内存泄露

初始代码

/**
 * 泛型栈
 * @author 焦焱
 *
 * @param <T>
 */
public class TStack<T> {
    /**
     * 泛型数组
     */
    private T[] data = null;
    /**
     * 栈顶
     */
    private int top;
    /**
     * 栈的长度
     */
    private int length;
    /**
     * 空构造函数
     */
    public TStack() {
        this(10);
    }
    /**
     * 有参构造函数
     * @param i 栈的长度
     */
    public TStack(int i)
    {
        //data = new T[i];  × 错误
        length = i;
        //通过将object数组强转为泛型数组的方式创造泛型数组
        data = (T[]) new Object[length];
        //栈顶初始化
        top = 0;
    }
    /**
     * 入栈
     * @param val
     */
    public void push(T val)
    {   
        if(top>=length)
        {
            return;
        }
        this.data[top++] = val;
    }
    /**
     * 出栈
     * @return 数据
     */
    public T pop()
    {   
        if(top==0)
        {
            return null;
        }
        T a = this.data[--top];
        //稍后在此插入代码
        return a;
    }



    /**
     * 获得栈顶元素
     * @return 数据
     */
    public T getTop()
    {   if(top==0)
        {
        return null;
        }
        return this.data[top-1];
    }
    public static void main(String[] args) {
        TStack<User> a = new TStack<User>(10);
        for (int i = 0; i < a.length; i++) {
            a.push(new User("1", "1", i));
        }

        System.out.println(a.pop());
        //手动调用gc
        System.gc();
        //断点打在此
        System.out.println();
    }

}

打断点后进行调试
然后打开cmd
输入

jps

可以看到
这里写图片描述
可以看到进程号
然后我们将此进程中的运行信息导入到一个txt文件中
执行

jmap -histo:live 72276 >log.txt

然后在你自己的用户路径下找文件
这里写图片描述
查找到以后打开
可以看到
这里写图片描述
这就是当时运行的时候jvm中所运行的java对象
在里面找到User对象。
这里写图片描述
然后在留的//稍后在此插入代码这一行插入

this.data[top] = null;

及释放对象,重复进行以上操作
打开log.txt
这里写图片描述
我们发现User对象变成了9

总结

所以我们第一次写的代码是有内存泄露的,因为当我们pop出去一个User之后,它并没有消失,而是还在被引用,所以会占用JVM的内存空间,这样的代码是不好的,所以我们看到的实例数为10。
当我们加入哪一行之后,pop出去以后,当前User的引用会被置为null,相应堆内的User也会等待回收,当我们手动gc后就会被回收掉,所以实例数会变为9。

猜你喜欢

转载自blog.csdn.net/qq_38345606/article/details/80542153