第6条:消除过期的对象引用

  虽然当我们用完对象后,java有垃圾回收机制进行回收,但是并没有那么的智能,对于某些被引用的对象,就算我们已经不在使用它了,但是java的回收机制是不会回收他们的,人们称之为“内存泄漏”。

  很多时候内存泄漏都是“人们无意识的内存引用”造成的,举个简单的例子:

List<String> list = new ArrayList<>();
String str = "testString";
list.add(str);
str  = null;

  看似上面的str被回收了,但是,事实上创建str时所开辟的内存空间是不会被回收的,因为list依然持有对str的引用这就是一个典型的“无意识的内存引用”。为了防止这些“无意识的内存引用”,我们应该了解对象相互引用的时候是存在怎样的依赖关系的。

  下面我们在看看一个简单的栈实现例子:

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;
    }

    private void ensureCapacity(){
        if(elements.length == size){
            elements = Arrays.copyOf(elements,2*size+1);
        }
    }
  
  public Object pop(){
    if(size == 0){
      throw new EmptyStackException();
    }
    Object result = elements[--size];
    return result;
  }
}

  看似这段程序没有明显的错误,也能运行,但是这里面存在一个内存泄漏问题,如果栈先增长了,然后在收缩,那么栈中弹出的对象将不被当做垃圾回收,即使使用栈的程序不在引用这些对象了,它们也不会被回收,因为栈内部维护着对这些对象的过期引用,所谓的过期引用是指永远不在会被解除的引用,这里就是凡在elements数组的活动部分之外的任何引用都是过期的。

  对于上述这些修复的方法还是比较简单的,一旦对象是过期引用,只需要清空这些引用即可,对应上述例子中的Stack而已,只要一个单元被弹出栈,那就是过期引用,只需要在pop方法的return result上面加上elements[size] = null;即可。

  当然,我们不要因为害怕内存泄漏而在所有的地方都手动回收内存,这样会导致我们的代码凌乱臃肿,不利于管理,我们应该把目标集中在那些长声明周期的变量中,哪些是长生命周期的变量呢,最明显的一个就是static修饰的变量,我们应该把目光放在这些变量上。  

 

posted @ 2017-04-11 20:20  哀&RT  阅读(281)  评论(0编辑  收藏  举报