程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> JVM內存管理:雜談(借此也論一論obj=null)

JVM內存管理:雜談(借此也論一論obj=null)

編輯:關於JAVA

各位園友好,LZ是從某網站轉戰過來的博主,看到這裡很多博主都稱看友們為園友,LZ斗膽模仿一下,不過以前,LZ其實都是稱看友們為猿友的。之前LZ在某網站已經寫了一系列文章,已經全部復制到了園內的新博客,主要是設計模式的內容,各位有興趣的也可以去翻看一下,其中有不少還是頗受之前猿友們喜愛的。

作為一個程序猿,修煉的過程就猶如玄幻小說中的主角,不僅需要練習各種武技,內氣的修煉的一樣重要。雖然武技可以迅速的提升主角的實力,但是在內氣太差的情況下,根本發揮不出武技的十之一二。

因此,在介紹過設計模式這一類外功之後,LZ就直接轉戰內氣修煉,和各位猿友探討一下JVM的內容。

本來這一章應該是介紹與GC相關的內容,不過在此之前,LZ准備先和各位探討一下一個編程的小技巧。當然,這個小技巧其實也是與GC密切相關的。

不知道各位猿友有沒看過一些JAVA內存相關的文章,裡面在羅列建議的時候,經常會寫出這樣一條建議。

第XX條、請在使用完對象之後,顯示的將對象設置為null。

原話不一定如此,但意思是一樣的。話裡所描述的意思,就是說我們以後寫代碼應該這麼寫。

Object obj = new Object();
//to do something 
obj = null;

這段代碼有點C/C++的風格,obj=null代替了C/C++中的delete obj或者是free(obj),相當於在提示我們,就算有了GC,我們也要像沒有GC一樣,使用完對象就得將其賦為空值。

首先,LZ這裡要說明的是,將obj賦為空值,與C/C++中的delete其實有著天壤之別,LZ之所以說它們代替了delete和free,僅僅是因為它們出現在代碼中的位置類似而已。

obj=null只做了一件事,就是將obj這個引用變量與new Object()創造的實例的關聯斷開,實際上,實例所占用的內存空間依然沒有釋放。

URL:http://www.bianceng.cn/Programming/Java/201410/45819.htm

在提出這個建議的時候,很多博主或者圖書的作者(因為有不少博主估計也是從書上看的)的本意,是想盡快消除引用與實例的關聯,從而誘發GC在進行垃圾回收時,將實例所占用的內存釋放。在某些時候,這麼做也是為了消除內存洩露。

LZ個人覺得,許多博主或者是圖書作者,提出這個建議的初衷,主要是針對一些沒有接觸過GC原理以及內存管理的程序猿而建議的,因為在不明白相關知識的前提下,很容易掌握不好變量的作用域,從而導致不必要的內存浪費(個人覺得這個並不是主要目的,因為只要沒有發生內存洩露,內存終究是要被GC釋放的),甚至是內存洩露。

所以為了安全起見,有些高人們就提出了這麼一個建議。

有鑒於此,LZ個人覺得,在各位掌握了相關知識之後,完全可以忽略這個建議,而且這麼做明顯會降低代碼的清晰度以及增加編碼的負擔,然而所換來的好處,卻只是為了避免那根本不一定存在的內存洩露。

這裡解釋一下為何在有些時候不將對象賦為空值會造成內存洩露,我們考慮下面一段代碼。

import java.util.Arrays;
    
public class Stack {
        
    private static final int INIT_SIZE = 10;
    
    private Object[] datas;
        
    private int size;
    
    public Stack() {
        super();
        datas = new Object[INIT_SIZE];
    }
        
    public void push(Object data){
        if (size == datas.length) {
            extend();
        }
        datas[size++] = data;
    }
        
    public Object pop(){
        if (size == 0) {
            throw new IndexOutOfBoundsException("size is zero");
        }
        return datas[--size];
    }
        
    private void extend(){
        datas = Arrays.copyOf(datas, 2 * size + 1);
    }
        
}

這段代碼是一個簡單的長度可伸縮的棧實現,在你寫一段測試代碼去測試它的時候,會發現它毫無問題。但是不好意思,這裡面就有一個明顯的“內存洩露”,這就是因為一些對象或者說引用沒有設置為空值所造成的。      

如果你還沒看出來,LZ稍微提示一下,各位就會意識到了。這段代碼裡面,數組裡的對象只會無限增長,而不會隨著棧中元素的彈出而減少,減小的僅僅是對外展示的棧的大小size。考慮一個極端的場景,假設你曾經往棧中放入了100萬個對象,最後使用了99萬9千9百9十9個,從外部看來,棧中還只剩一個可用對象了,但是我們的棧依然持有著100萬個對象的引用。如果JVM可以活過來的話,想必一定會把你蹂躏到死的。

我們缺少的就是將對象賦為null值的那一步,所以pop方法應該改為下面的方式,而且各位可以去看一下ArrayList中remove方法的源碼,也是類似的思路。

public Object pop(){
        if (size == 0) {
            throw new IndexOutOfBoundsException("size is zero");
        }
        Object data = datas[--size];
        datas[size] = null;
        return data;
    }

這個內存洩露是非常隱蔽的,而且實際使用當中不一定就能發現,因為隨著Stack對象的回收,整個數組也會被回收,到時內存洩露就被掩蓋了。

所以個人覺得上述的那個建議是完全沒有必要的,就算遵從了上面的建議,如果對內存管理不熟悉的話,也不會想到上面這個代碼中的問題。如果想徹底的避免內存洩露的發生,機械式的主動將對象賦為空值,並不是一個可以從根本上解決問題的辦法。

真正能夠解決問題的辦法,就是掌握好GC的策略與原理,定義一個變量時多注意變量的作用域,如此才可以更好的避免內存洩露。

所以學好GC原理還是很有必要的,希望准備走JAVA之路的朋友,尤其是從培訓機構出來的猿友們(此處沒有鄙視培訓機構出來的猿友們的意思,因為LZ本人也是半路出家,從培訓機構走上編程之路的程序猿之一),一定不要只專注於各個框架的使用以及業務的深入,雖然這些事很有必要,但並不是全部。

follow me and go the memory world 吧(語法都是浮雲)。

作者:zuoxiaolong(左潇龍)

出處:博客園左潇龍的技術博客--http://www.cnblogs.com/zuoxiaolong

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved