淺談Java--內存泄漏


  JAVA的垃圾回收機制,讓許多程序員覺得內存管理不是很重要,但是內存內存泄露的事情恰恰這樣的疏忽而發生,特別是對於Android開發,內存管理更為重要,養成良好的習慣,有利於避免內存的泄漏.
 
對象的幾種狀態:
    這里可以把許多對象和引用看成是有向圖,頂點可以是對象也可以是引用,引用關系就是有向邊。
  1. 可達狀態:對象創建的時候,有引用指向它,這個時候在對象和引用之間建立了引用關系,即由引用發射有向邊指向對象,這個對象就是出於可達狀態
  2. 可恢復狀態:當引用不指向一個對象的時候,該對象就處於可恢復狀態,這時候在系統回收該對象之前,會調用finalize方法進行資源清理,如果調用這個方法后能重新讓引用變量去引用他,那么他又恢復到可達狀態,不然會變成不可達狀態。
  3. 不可達狀態:當對象和引用變量失去了引用關系,並且調用了finalize方法后,不能恢復到可達狀態,那么將永久性失去引用,此時系統才會真正去回收對象占用的內存。
 
 
何為內存泄漏:
    內存泄漏就是程序運行中,java垃圾回收機制,會對一些不適用的內存進行回收,然后再重新分配,保證系統能再次用到這些內存,Java中所有不可達狀態都會被回收,但是一些處於可達狀態,但是程序卻再也不會用到的對象(占着茅坑不拉屎),這些內存就無法被回收,他們對於程序員來說已經沒用,但是對於垃圾回收機制來說,他們是可達的,所以是“有用的”這些占用的內存就會出現內存泄漏。
 
 用一個棧的例子說明:

package CrazyJava;
import javax.management.RuntimeErrorException;
/**
 * 
 * @author ccf
 *
 */
public class neicun {
    /**
     * @param args
     */
    class Stack {
        private Object[] elementData;
        private int size;
        private int capacityIncrement;
        public Stack(int initialCapacity) {
            elementData = new Object[initialCapacity];
        }
        public Stack(int initialCapacity, int capacityIncrement) {
            elementData = new Object[initialCapacity];
            this.capacityIncrement = capacityIncrement;
        }
        public void push(Object object) {
            ensureCapacity();
            elementData[size++] = object;// 后加
        }
        public Object pop() {
            if (size == 0)
                throw new RuntimeException("空棧異常");
            Object ele = elementData[--size];// 這里為局部變量,當方法結束,局部變量會被回收
            elementData[size] = null;// 消去強引用關系,避免產生內泄漏。
            return ele;// 返回棧頂元素 size自減1個長度
        }
        public int size() {
            return size;
        }
        private void ensureCapacity() {
            // TODO Auto-generated method stub
            // 數組已經滿了。進行擴容。
            if (elementData.length == size) {
                Object[] oldElmentata = elementData;
                int newLength = 0;
                if (capacityIncrement > 0) {
                    newLength = elementData.length + capacityIncrement;
                } else {
                    newLength = (int) (elementData.length * 1.5);
                }
                elementData = new Object[newLength];
                System.arraycopy(oldElmentata, 0, elementData, 0, size);
            }
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Stack stack = new neicun().new Stack(10);
        for (int i = 0; i < 10; i++) {
            stack.push("元素" + i);
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(stack.pop());
        }
    }
}

  

 
 
代碼中pop() 函數實現出棧,其中  elementData[size] = null;切斷了元素和其引用的引用關系,這樣可以使元素進入不可達狀態,從能被系統回收,如果不切斷引用,該元素將一直可以可達狀態,就會常駐內存。
而針對上一句Object ele  = elementData[ --size];   就是為了暫存這個元素,用於作為return的返回值,有人會問這個為什么不會造成內存泄露,他們之間不是已經有了引用和被引用的關系,原因是這里的Object對象是局部變量,局部變量的生命周期跟方法生命周期一樣,該方法結束了,搞局部變量會被垃圾回收機制收回,所以這個擔心是沒必要的。
 
 
         從以上看來,內存泄漏可能性還是很大的,強引用類型(以后的博文會講)使我們用得最多的類型,這類引用只有再失去引用的時候才會被回收,其他情況都是不能被回收的,所以良好的編程習慣是避免內存泄漏的好辦法
 
 
 
  怎樣避免內存泄漏:
    1. 盡量多使用直接量     
      例如String類型 
      String a =“ccf”  //采用直接量,JVM字符串池會緩存這個字符串
      String b = new String(“ccf”); //但是直接調用構造方法的話,因為String內部是基於數組,所以會產生 字符數組存儲ccf三個字符。
    2. 多使用StringBuilder和StringBuffer
      使用StringBuilder和StringBuffer進行字符串的操作,可以減少使用String進行字符串操作產生的臨時字符串
    3. 釋放無用的對象引用,就像上面棧的 pop ()方法
    4. 少用靜態變量,靜態變量生命周期跟類一樣,為類加載到類卸載這段時間,也就是知道程序結束。
    5. 避免在循環和經常調用的方法創建對象,因為這些情況會產生大量對象,特別是像for循環這些。
    6. 緩存經常用到的對象,避免重復去創建相同對象,想android中Adapter中重寫getView就經常用到對象緩存技術。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM