相信,網上不少java性能優化的帖子裏都會有這麼一條: 儘可能把不使用的對象顯式得置爲null.這樣有助於內存回收java
能夠明確的說,這個觀點是基本錯誤的.sun jdk遠比咱們想象中的機智.徹底能判斷出對象是否已經no ref..可是,我上面用的詞是"基本".也就是說,有例外的狀況.這裏先把這個例外狀況給提出來,後續我會一點點解釋.這個例外的狀況是, 方法前面中有定義大的對象,而後又跟着很是耗時的操做,且沒有觸發JIT編譯..總結這句話,就是: 除非在一個方法中,定義了一個很是大的對象,而且在後面又跟着一段很是耗時的操做.而且,該方法沒有知足JIT編譯條件,不然顯式得設置 obj = null是徹底沒有必要的性能優化
上面這句話有點繞,可是,上面說的每個條件都是有意義的.這些條件分別是jvm
1 同一個方法中 2 定義了一個大對象(小對象沒有意義) 3 以後跟着一個很是耗時的操做. 4 沒有知足JIT編譯條件
上面4個條件缺一不可,把obj顯式設置成null纔是有意義的. 下面我會一一解釋上面的這些條件性能
這個條件是最容易理解的,若是大對象定義在其餘方法中,那麼是不須要設置成Null的,測試
1 public class Test 2 { 3 4 public static void main(String[] args){ 5 6 foo(); 7 8 System.gc(); 9 } 10 11 public static void foo(){ 12 byte[] placeholder = new byte[64*1024*1024]; 13 } 14 }
對應的輸出以下,能夠看到64M的內存已經被回收優化
D:\>java -verbose:gc Test [GC 66798K->66120K(120960K), 0.0012225 secs] [Full GC 66120K->481K(120960K), 0.0059647 secs]
其實很好理解,placeholder是foo方法的局部變量,在main方法中調用的時候,其實foo方法對應的棧幀已經結束.那麼placeholder指向的大對象天然被gc的時候回收了.spa
這句話的意思也很好理解.只有定義的是大的對象,咱們才須要關心他儘快被回收.若是你只是定義了一個 String str = "abc"; 後續手動設置成null讓gc回收是沒有任何意義的.線程
這裏理解是:後面的這個耗時的可能超過了一個GC的週期.例如code
1 public static void main(String[] args) throws Exception{ 2 byte[] placeholder = new byte[64*1024*1024]; 3 Thread.sleep(3000l); 4 // dosomething 5 }
在線程sleep的三秒內,可能jvm已經進行了好幾回ygc.可是因爲placeholder一直持有這個大對象,因此形成這個64M的大對象一直沒法被回收,甚至有可能形成了知足進入old 區的條件.這個時候,在sleep以前,顯式得把placeholder設置成Null是有意義的. 可是,若是沒有這個耗時的操做,main方法能夠很是快速的執行結束,方法返回,同時也會銷燬對應的棧幀.那麼就是回到第一個條件,方法已經執行結束,在下一次gc的時候,天然就會把對應的"垃圾"給回收掉.對象
jit編譯的觸發條件,這裏就很少闡述了.對應的測試代碼和前面同樣
1 public class Test 2 { 3 public static void main(String[] args) throws Exception{ 4 byte[] placeholder = new byte[64*1024*1024]; 5 placeholder = null; 6 //do some time-consuming operation 7 System.gc(); 8 } 9 }
在解釋執行中,咱們認爲placeholder = null;是有助於對這個大對象的回收的.在JIT編譯下,JIT編譯器進行控制流和數據流分析後,生成的OopMap就提供比較精確的信息,不須要經過」=null」來告知對象使命已經完成.退一步說,這時即便有」=null」操做,也會被優化掉,生成出來的本地代碼與沒有」=null」操做的版本是如出一轍的.