- 内存泄漏的原因:
- 内存泄漏的几种情况:
- 1.长生命周期的对象持有短生命周期对象的引用
- 2.连接未关闭
- 3.变量作用域不合理
- 4.内部类持有外部类
- 5.Hash值改变
- 内存泄漏和内存溢出区别
Java程序程序在申请内存后,无法释放已申请的内存空间。
内存泄漏的几种情况: 1.长生命周期的对象持有短生命周期对象的引用这种情况一般发生在使用容器造成内存泄漏。
举个例子:
定义一个栈,可以进栈和出栈,有个成员变量数组可以保存栈内的元素。
public class Stack {
public Object[] elements;//数组来保存
private int size =0;
private static final int Cap = 16;
public Stack() {
elements = new Object[Cap];
}
public void push(Object e){ //入栈
elements[size] = e;
size++;
}
public Object pop(){ //出栈
size = size -1;
Object o = elements[size];
// elements[size] = null; //让GC 回收掉
return o;
}
}
测试类
public class UseStack {
public static void main(String[] args) {
Stack stack = new Stack(); //new一个栈
Object o = new Object(); //new一个对象
System.out.println("o="+o);
stack.push(o); //入栈
Object o1 = stack.pop(); //出栈
//o对象没什么用
System.out.println("o1="+o1);
System.out.println(stack.elements[0]); //打印栈中的数据
}
}
o对象放入栈再出栈,就没啥用了,看下栈中是否还有元素。
运行结果:
o=java.lang.Object@1b6d3586
o1=java.lang.Object@1b6d3586
java.lang.Object@1b6d3586
可以看到红色的地址,表示栈中还有元素。内存泄漏了,解决方式
elements[size] = null; //让GC 回收掉
打开这行代码。
再看运行结果:
2.连接未关闭o=java.lang.Object@1b6d3586
o1=java.lang.Object@1b6d3586
null
栈中的元素已经释放,不会内存泄漏了。
如数据库连接、网络连接(socket)和IO连接等,只有连接被关闭后,垃圾回收器才会回收对应的对象。
解决方式
手动关闭流:
Scanner scanner = null;
try {
scanner = new Scanner(new File("test.txt"));
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
//这里必须手动关闭流
if (scanner != null) {
scanner.close();
}
}
或者用java7出现的 try with resouce:
try (Scanner scanner = new Scanner(new File("test.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
将声明对象防止try的小括号中,他会自动帮我们关闭连接资源。
3.变量作用域不合理- 1.一个变量的定义的作用范围大于其使用范围
- 2.如果没有及时地把对象设置为null
举例如下:
class Test(){
private Object o;
public void test1(){
o = new Object();
//some other codes;
}
public void test2(){
//some codes
}
}
此例中,对象o的作用域仅仅限于第一个方法,另外一个方法并不会用到它。但是,第一个方法结束之后o所分配的内存并不会被释放,z只有在整个类的对象被释放时对象o才会被释放,因此造成了内存泄漏。
,可以把该对象设置为第一个方法中的局部变量。也可以在第一个方法结束时将其赋值为null,这样的话在第一个方法结束时该对象就可以被回收。
4.内部类持有外部类Java的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏
如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏(你认为垃圾回收器会回收掉外部类的实例,但由于内部类持有外部类的引用,导致垃圾回收器不能正常工作)
解决方法1
:你可以在内部类的内部显示持有一个外部类的软引用(或弱引用),并通过构造方法的方式传递进来,在内部类的使用过程中,先判断一下外部类是否被回收;
解决方法2
:内部类设置成静态内部类。
举例如下:
public class NoStaticInternal {
/**
* @param args
*/
public int k=13;
private static String string="hu";
protected float j=1.5f;
public static void show(){
System.out.println("show");
}
private void add(){
System.out.println("add");
}
public static void main(String[] args) {
NoStaticInternal m=new NoStaticInternal();
//非静态内部类的构造方式
//Child c=m.new Child();
Child c= new Child();
c.test();
}
//内部类Child --静态的,防止内存泄漏
static class Child{
public int i;
public void test(){
//System.out.println("k=:"+k);
System.out.println("string:"+string);
//add();
//System.out.println("j=:"+j);
show();
}
}
}
内部类一般定义类静态,这样内部类没有父类的引用,不需要对父类初始化。
5.Hash值改变在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄露。
例子:
public class Node {
private int x;
private int y;
public Node(int x, int y) {
super();
this.x = x;
this.y = y;
}
//重写HashCode的方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
//改变y的值:同时改变hashcode
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.setY(7);//nod2的Hash值改变
hashSet.remove(nod2);//删掉nod2节点
System.out.println(hashSet.size());
}
运算结果:
2
可以看出,在修改了node2的参与哈希值计算的字段之后,用remove()方法也无法移除node2。但是hashSet仍然有2个元素大小,说明hashSet仍然持有该对象的引用,因此该对象无法被回收,造成内存泄漏。
内存泄漏和内存溢出区别相同与不同
:
内存溢出:实实在在的内存空间不足导致;
内存泄漏:该释放的对象没有释放,多见于自己使用容器保存元素的情况下。
如何避免
:
内存溢出:检查代码以及设置足够的空间
内存泄漏:一定是代码有问题
往往很多情况下,内存溢出往往是内存泄漏造成的。
开通了个微信公众号:
搜索: 怒放de每一天
后续可能不定时推送相关文章,期待和大家一起成长!!
大功告成!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)