Android 编码规范:(六)消除过期的对象引用

尽管Java不像C/C++那样需要手工管理内存资源,而是通过更为方便、更为智能的垃圾回收机制来帮助开发者清理过期的资源。即便如此,内存泄露问题仍然会发生在你的程序中,只是和C/C++相比,Java中内存泄露更加隐匿,更加难以发现,见如下代码:

  1. // Can you spot the "memory leak"?  
  2.   
  3. import java.util.*;  
  4.   
  5. public class Stack {  
  6.     private Object[] elements;  
  7.     private int size = 0;  
  8.     private static final int DEFAULT_INITIAL_CAPACITY = 16;  
  9.   
  10.     public Stack() {  
  11.         elements = new Object[DEFAULT_INITIAL_CAPACITY];  
  12.     }  
  13.   
  14.     public void push(Object e) {  
  15.         ensureCapacity();  
  16.         elements[size++] = e;  
  17.     }  
  18.   
  19.     public Object pop() {  
  20.         if (size == 0)  
  21.             throw new EmptyStackException();  
  22.         return elements[--size];  
  23.     }  
  24.   
  25.     /** 
  26.      * Ensure space for at least one more element, roughly 
  27.      * doubling the capacity each time the array needs to grow. 
  28.      */  
  29.     private void ensureCapacity() {  
  30.         if (elements.length == size)  
  31.             elements = Arrays.copyOf(elements, 2 * size + 1);  
  32.     }  
  33. }  
以上示例代码,在正常的使用中不会产生任何逻辑问题,然而随着程序运行时间不断加长,内存泄露造成的副作用将会慢慢的显现出来,如磁盘页交换、OutOfMemoryError等。那么内存泄露隐藏在程序中的什么地方呢?当我们调用pop方法是,该方法将返回当前栈顶的elements,同时将该栈的活动区间(size)减一,然而此时被弹出的Object仍然保持至少两处引用,一个是返回的对象,另一个则是该返回对象在elements数组中原有栈顶位置的引用。这样即便外部对象在使用之后不再引用该Object,那么它仍然不会被垃圾收集器释放,久而久之导致了更多类似对象的内存泄露。修改方式如下:
  1. public Object pop() {    
  2.     if (size == 0)     
  3.         throw new EmptyStackException();    
  4.     Object result = elements[--size];    
  5.     elements[size] = null;  
  6.     return result;    
  7. }   

由于现有的Java垃圾收集器已经足够只能和强大,因此没有必要对所有不在需要的对象执行obj = null的显示置空操作,这样反而会给程序代码的阅读带来不必要的麻烦,该条目只是推荐在以下3中情形下需要考虑资源手工处理问题:

1) 类是自己管理内存,如例子中的Stack类。

2) 使用对象缓存机制时,需要考虑被从缓存中换出的对象,或是长期不会被访问到的对象。

3) 事件监听器和相关回调。用户经常会在需要时显示的注册,然而却经常会忘记在不用的时候注销这些回调接口实现类。


posted @ 2012-05-29 22:03  andriod2012  阅读(369)  评论(0编辑  收藏  举报