内存泄漏
在JAVA中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点:
- 对象是可达的,即在有向图中,存在有向路可以与其相连;
- 对象是无用的,即程序以后不会再使用这些对象。
如果对象满足上面的1、2条件,这些对象就可以判定为JAVA中的内存泄漏,这些对象不会被GC所回收,然而他们却占用内存。
示例演示
/**
* 测试HashMap的内存泄露
*/
public class HashMapLeakTest {
public static void main(String[] args) {
Map<HashKey, Integer> map = new HashMap<HashKey, Integer>();
HashKey p = new HashKey("zhangsan","12333-suu-1232");
map.put(p, 1);
p.setName("lisi"); // 因为p.name参与了hash值的计算,修改了之后hash值发生了变化,所以下面删除不掉
map.remove(p);
System.out.println(map.size());
}
}
/**
* 用于测试的key 类
*/
class HashKey {
private final String id;
private String name;
public HashKey(String name, String id) {
this.name = name;
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
return name.hashCode()+id.hashCode();
}
}
以上代码中运行会发现,在remove后,map的size仍然为1,也就是没有删除节点p,这个时候节点p在map中存在,也就是说该对象是可达的,然而该对象我们代码中时要删除的也就是说是没有用的对象了,所以这样就出现了内存泄露的情况。
原因分析
原因比较简单,就是因为这个对象hashCode在存放到HashMap之后发生了变化,我们去remove时按照新的hash值进行查找,而该对象是按照之前的旧的hash值存放在HashMap中,hash值发生了变化,导致查找的索引位置时不一样的,于是在HashMap中就无法这个对象,因此也就没有办法删除该节点。
解决方案
知道了原因,要解决这个问题或者避免这种内存泄露的情况发生,只需要对hashCode方法进行修改,不使用易变的字段参与hash计算,推荐使用final类型的字段计算hash值。
@Override
public int hashCode() {
return id.hashCode();
}
上述代码虽然解决了内存泄露的问题,但还存在内存溢出的情况。