2017-04-20 58 views
0

我目前正在優化我的應用程序,並且我注意到它佔用了近1G內存。我做了一些分析,發現問題: 我通過在我製作的紋理類中創建保存像素數據的int數組來分配大量內存。 但是這讓我感到困惑,因爲當創建一個新的數組時,舊的數組不再被使用,並且沒有任何地方引用它。Java - 未引用的陣列仍然佔用內存

我已經寫了重新產生此問題的測試程序:

public class Main { 

    static class ArrayHolder { 
     int[] array; 

     void init() { 
      array = null; 
      array = new int[4000]; 
     } 
    } 

    public static void main(String[] args) { 
     ArrayHolder holder = new ArrayHolder(); 
     for (int i = 0; i < 1000000; i++) { 
      holder.init(); 
     } 
     System.out.println(holder.array.length); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

當我運行這段代碼的程序使用最多的RAM 600兆字節。我不明白爲什麼,因爲在ArrayHolder中調用init()方法時,數組字段被設置爲新數組,並且舊數組的引用丟失。這並不意味着它不再佔用空間了嗎? 對不起,如果這是一個沒有問題的問題,但有人可以解釋這是爲什麼,我在這裏做錯了什麼?

+0

您的舊陣列只能在gc的判斷下啓動。如果gc在重新分配內存後很快運行,那麼該空間將被回收,否則它不會。 – SMA

+0

大型陣列被添加到終身空間,並且只有填滿時纔會默認進行GC編輯。例如睡眠不會觸發GC。簡而言之,像這樣創建大量的大型數組很不理想,原因很多。 –

回答

1

array參考到Java中的數組。在這方面,它們像來自java.lang.Object的任何其他常規對象。一旦不再有對一個數組的引用,它就會被安排進行垃圾收集,這不一定會立即發生。

釋放內存中的這種延遲不太可能導致您的麻煩:垃圾收集器將在需要時收集未提交的對象。

請注意,即使垃圾收集器確實運行了,但仍無法保證進程將內存交還給操作系統,否則操作系統將從進程中奪回內存。

+0

謝謝你指出。你如何着手減少這個特定例子的內存使用量?或者你會把它留給GC?因爲對於簡單的應用程序來說1演出似乎有點太過分了。 – RagingRabbit

+0

我只會信任GC。 1GB在我的192GB機器上僅僅是微不足道的,因此如果CPU繁忙,1GB的尖峯不足以觸發我盒子上的GC。 – Bathsheba

+0

我使用JVisualVM再次運行程序,並使用它執行垃圾回收,但內存使用情況未發生變化。 GC是否決定不收集或是一個問題? – RagingRabbit

2

僅僅因爲你的對象沒有引用並不意味着它不會佔用任何內存。 GC必須運行,然後才能從內存中「移除」。你可以在內存中沒有引用的對象佔用空間。檢查GC是否已經運行,如果沒有明確調用它(不是一個好主意)。此外,我建議您查找是否確實有一些對象的強引用。

您使用System.gc()明確調用GC。

您可以添加諸如'-XX:+ PrintGCDetails'之類的JVM參數來檢查GC是否已經運行。

+0

他將如何檢查垃圾收集器是否運行,以及如何調用它,以及爲什麼他不應該這樣做? – bleistift2

+0

@ bleistift2 - 編輯我的答案,添加你所需要的。至於爲什麼調用GC明確不是一個好主意,去低谷http://stackoverflow.com/questions/66540/when-does-system-gc-do-anything – TheLostMind

+0

請注意'System.gc()'是諮詢。 – Bathsheba