JVM之内存泄露

Posted 爱上口袋的天空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM之内存泄露相关的知识,希望对你有一定的参考价值。

一、内存泄露的理解与分析


二、Java中内存泄露的8种情况

1、静态集合类

2、单例模式

3、内部类持有外部类

4、各种连接,如数据库连接、网络连接和IO连接等

 5、变量不合理的作用域

6、改变哈希值

 测试代码如下:

 例1:
/**
 * 演示内存泄漏
 *

 * @create 14:43
 */
public class ChangeHashCode 
    public static void main(String[] args) 
        HashSet set = new HashSet();
        Person p1 = new Person(1001, "AA");
        Person p2 = new Person(1002, "BB");

        set.add(p1);
        set.add(p2);

        p1.name = "CC";//导致了内存的泄漏
        set.remove(p1); //删除失败

        System.out.println(set);

        set.add(new Person(1001, "CC"));
        System.out.println(set);

        set.add(new Person(1001, "AA"));
        System.out.println(set);

    


class Person 
    int id;
    String name;

    public Person(int id, String name) 
        this.id = id;
        this.name = name;
    

    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (id != person.id) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    

    @Override
    public int hashCode() 
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    

    @Override
    public String toString() 
        return "Person" +
                "id=" + id +
                ", name='" + name + '\\'' +
                '';
    

例2:
/**
 * 演示内存泄漏

 * @create 14:47
 */
public class ChangeHashCode1 
    public static void main(String[] args) 
        HashSet<Point> hs = new HashSet<Point>();
        Point cc = new Point();
        cc.setX(10);//hashCode = 41
        hs.add(cc);

        cc.setX(20);//hashCode = 51  此行为导致了内存的泄漏

        System.out.println("hs.remove = " + hs.remove(cc));//false
        hs.add(cc);
        System.out.println("hs.size = " + hs.size());//size = 2

        System.out.println(hs);
    



class Point 
    int x;

    public int getX() 
        return x;
    

    public void setX(int x) 
        this.x = x;
    

    @Override
    public int hashCode() 
        final int prime = 31;
        int result = 1;
        result = prime * result + x;
        return result;
    

    @Override
    public boolean equals(Object obj) 
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        Point other = (Point) obj;
        if (x != other.x) return false;
        return true;
    

    @Override
    public String toString() 
        return "Point" +
                "x=" + x +
                '';
    

7、缓存泄露

 例子:
/**
 * 演示内存泄漏
 *

 * @create 14:53
 */
public class MapTest 
    static Map wMap = new WeakHashMap();
    static Map map = new HashMap();

    public static void main(String[] args) 
        init();
        testWeakHashMap();
        testHashMap();
    

    public static void init() 
        String ref1 = new String("obejct1");
        String ref2 = new String("obejct2");
        String ref3 = new String("obejct3");
        String ref4 = new String("obejct4");
        wMap.put(ref1, "cacheObject1");
        wMap.put(ref2, "cacheObject2");
        map.put(ref3, "cacheObject3");
        map.put(ref4, "cacheObject4");
        System.out.println("String引用ref1,ref2,ref3,ref4 消失");

    

    public static void testWeakHashMap() 

        System.out.println("WeakHashMap GC之前");
        for (Object o : wMap.entrySet()) 
            System.out.println(o);
        
        try 
            System.gc();
            TimeUnit.SECONDS.sleep(5);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        System.out.println("WeakHashMap GC之后");
        for (Object o : wMap.entrySet()) 
            System.out.println(o);
        
    

    public static void testHashMap() 
        System.out.println("HashMap GC之前");
        for (Object o : map.entrySet()) 
            System.out.println(o);
        
        try 
            System.gc();
            TimeUnit.SECONDS.sleep(5);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        System.out.println("HashMap GC之后");
        for (Object o : map.entrySet()) 
            System.out.println(o);
        
    


结果:
String引用ref1,ref2,ref3,ref4 消失
WeakHashMap GC之前
obejct2=cacheObject2
obejct1=cacheObject1
WeakHashMap GC之后
HashMap GC之前
obejct4=cacheObject4
obejct3=cacheObject3
Disconnected from the target VM, address: '127.0.0.1:51628', transport: 'socket'
HashMap GC之后
obejct4=cacheObject4
obejct3=cacheObject3
分析:

8、监听器和回调

 


三、内存泄露案例分析

1、案例代码

代码:
public class Stack 
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() 
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    

    public void push(Object e)  //入栈
        ensureCapacity();
        elements[size++] = e;
    

    public Object pop() 
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null;
        return result;
    

    private void ensureCapacity() 
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    

分析:

 解决办法:

将代码中的pop()方法变成如下方法:
public Object pop()  //出栈
    if (size == 0)
        throw new EmptyStackException();
    return elements[--size];

 

以上是关于JVM之内存泄露的主要内容,如果未能解决你的问题,请参考以下文章

JVM之内存泄漏和内存溢出

5.JVM系列-堆内内存泄露案例分析解决

JVM系列之六:内存溢出内存泄漏 和 栈溢出

性能测试--jvm中内存泄露与溢出

jvm调优四:netty堆外内存泄露

如何用Java编写一段代码引发内存泄露