内存泄漏的8种情况

  • Post author:
  • Post category:其他

一 静态集合类

静态集合类,如 HashMap、LinkedList 等等。如果这些容器为静态的,那么它们的生命周期与 JVM 程序一致,则容器中的对象在程序结束之前不能被释放,从而造成内存泄漏。简单而言,长生命周期对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

package chapter03;

import java.util.ArrayList;
import java.util.List;

public class MemoryLeak {
    static List list = new ArrayList();
    // 该方法执行完后,obj 引用的对象发生内存泄漏
    public void oomTest() {
        Object obj = new Object(); // 局部变量
        list.add(obj);
    }
}

二 单例模式

单例模式,和静态集合导致内存泄漏原因类似,因为单例的静态特性,它的生命周期和 JVM 的生命周期一样长,所以如果单例对象如果持有外部对象引用,那么这个外部对象也不会被回收,那么就会发生内存泄漏。

三 内部类持有外部类

内部类持有外部类,如果一个外部类的实例的方法返回一个内存类的实例对象。

这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象不会被垃圾回收,这也造成内存泄漏。

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

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

在对数据库进行操作时,首先需要建立与数据库的连接,当不再使用时,需要调用 close 方法来释放与数据库的连接。只有连接被关闭,垃圾回收器才会回收对应对象

否则,如果在访问数据库的过程中,对 Connection、Statement 或 ResultSet 不显示地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。

五 变量不合理的作用域

package chapter03;

public class UsingRandom {
    private String msg;
    public void receivemsg(){
        // String msg; // 可以将 msg 放在方法内
        readFromNet(); // 从网络中接收数据保存到 msg 中
        saveDB(); // 把 msg 保存到数据库中
        msg = null; // 加这个防止内存泄漏
    }
}

六 改变哈希值

1 说明

2 代码

package chapter03;

import java.util.HashSet;

/**
* 演示内存泄漏
*/
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 + '\'' +
                '}';
    }
}

3 运行结果——内存溢出,并且运行异常

[Person{id=1002, name=’BB’}, Person{id=1001, name=’CC’}]

[Person{id=1002, name=’BB’}, Person{id=1001, name=’CC’}, Person{id=1001, name=’CC’}]

[Person{id=1002, name=’BB’}, Person{id=1001, name=’CC’}, Person{id=1001, name=’CC’}, Person{id=1001, name=’AA’}]

4 结果分析

5 代码

package chapter03;

import java.util.HashSet;

/**
* 演示内存泄漏
*/
public class ChangeHashCode1 {
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        Point cc = new Point();
        cc.setX(10); // hashCode = 41
        hs.add(cc);

        cc.setX(20); // 修改了计算 hash 的成员值。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 +
                '}';
    }
}

6 运行结果——内存溢出,并且运行异常

hs.remove = false

hs.size = 2

[Point{x=20}, Point{x=20}]

7 结果分析

七 缓存泄露

1 说明

2 代码——内存泄漏

package chapter03;

import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;

/**
* 演示内存泄漏
*/
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);
        }
    }
}

3 运行结果

String引用ref1,ref2,ref3,ref4 消失

WeakHashMap GC之前

obejct2=cacheObject2

obejct1=cacheObject1

WeakHashMap GC之后

HashMap GC之前

obejct4=cacheObject4

obejct3=cacheObject3

HashMap GC之后

obejct4=cacheObject4

obejct3=cacheObject3

4 结果分析

八 监听器和回调


版权声明:本文为chengqiuming原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。