内存泄漏介绍
程序在申请内存后,无用内存无法释放已申请的内存空间。
长生命周期的对象持有短生命周期对象的引用
例如:将ArrayList设置为静态变量,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏
连接未关闭
如数据库连接、网络连接和IO连接等,只有连接被关闭后,垃圾回收器才会回收对应的对象。
这些未释放的网络连接、IO连接等,也会给数据库、系统带来负担
变量作用域不合理
1.一个变量的定义的作用范围大于其使用范围
在局部方法中定义大于这个方法作用域范围的变量
2.如果没有及时地把对象设置为null
例如:ThreadLocal中的值没有及时remove
内部类持有外部类
Java的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏
如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏(你认为垃圾回收器会回收掉外部类的实例,但由于内部类持有外部类的引用,导致垃圾回收器不能正常工作)
解决方法:你可以在内部类的内部显示持有一个外部类的软引用(或弱引用),并通过构造方法的方式传递进来,在内部类的使用过程中,先判断一下外部类是否被回收;
Hash值改变
在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄露。
代码示例
public class Node {
private int x;
private int y;
public Node(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Node{" +
"x=" + x +
", y=" + y +
'}';
}
/**
* 重写HashCode的方法,演示基于hash的容器内存泄露
*
* @return 对象的hashCode
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
/**
* 改变y的值:同时改变hashcode
*
* @param y 新的y值
*/
public void setY(int y) {
this.y = y;
}
public static void main(String[] args) {
HashSet<Node> hashSet = new HashSet<Node>();
Node nod1 = new Node(1, 3);
Node nod2 = new Node(3, 5);
hashSet.add(nod1);
hashSet.add(nod2);
//nod2的Hash值改变
nod2.setY(7);
//删掉nod2节点
hashSet.remove(nod2);
// 删除nod1节点
hashSet.remove(nod1);
// 此时你感觉应该大小为0。但是大小为1。因为修改了对象的hashCode值导致内存泄露和程序逻辑错误
System.out.println(String.format("hashSet大小:%s hashSet值:%s", hashSet.size(), hashSet.toString()));
}
}