1. 程式人生 > >Android記憶體基礎——Java記憶體管理機制

Android記憶體基礎——Java記憶體管理機制

參考連結

參考資料1

背景介紹

  • Java優勢之一就是其具有垃圾回收機制。在大部分情況下,JVM的GC(垃圾回收器)能夠幫助我們回那些不可到達的物件(就是未被引用的物件)。
  • 當然,在一些情況下,我們仍然需要自己去釋放記憶體(就是把物件置null,把容器、陣列清空),否則就會引起記憶體洩漏,記憶體洩漏嚴重時將容易引發OutOfMemoryError,詳情見記憶體洩漏
  • 此外,由於GC會停止所有的執行緒,包括UI執行緒,所以頻繁的GC必然會導致畫面卡頓(Android中每16ms為一幀),因此還應避免GC的頻繁發生。一個導致GC頻繁發生的原因就是記憶體抖動,點選連結看詳情。
    GC時執行緒被停止示意圖
  • 所以,理解Java的記憶體機制,有助於幫助我們在寫程式碼的過程中避免記憶體洩漏。

基本概念

Java記憶體層級

Created with Raphaël 2.1.0Code Segment:存放程式碼Data Segment:存放靜態變數,方法Stack Segment:存放變數,即引用Heap Segment:存放非靜態物件

Heap Segment

Java8之後,Heap Segment真正意義上的是由Young GeneriationOld Generiation組成的。物件在其中是標記複製演算法來判定一個物件是否應該被清理掉。
Heap Segment中發生的GC稱為Major GC,只會影響Heap Segment區。
記憶體圖

Young Generiation中的GC變化

  • 當物件被建立後,首先會被加入eden區。當eden區滿了之後,就會觸發一次GC,存活下來的物件會被複制到survivor區。
  • 當不為空的Survivor區滿了,同樣會觸發一次GC。
  • 當短時間內有大量物件建立和釋放同樣會造成記憶體抖動,會觸發CG。
  • 如圖所示,survivor有兩個區域,其中一個總是保持為空。
  • 現假設兩個Survivor區分別為S0,S1,並且首次GC時,eden區中存活的物件被複制到S0中。當再次發生GC時,S0和eden中仍然存活的物件就會被複制到空的S1中,此時S0為空;再次發生GC時,S1和eden中存活的物件將被複制到S0中,此時S1為空;再次發生GC…就是這樣進行的。當一個物件被來回複製轉移的次數達到閥值(預設為15次,可以通過使用-XX:MaxTenuringThreshold
    該命令來調整閥值)時,這個物件將被複制到Old Generiation區中,此時該物件將會變的相對安全,因為Old Segment區的GC頻率相對較低。

Old Segment中的GC變化

  • 該區域滿了之後會觸發一次GC,在該次GC中,一些年齡較大的物件會被清理掉。
  • 若多次觸發GC後,該區域仍然處於滿的狀態,則會丟擲OutOfMemoryError
  • 以兩種情況下,新建物件會被直接複製到該區域中:
    • 當新建物件所需要的記憶體大於1/2的單個survivor區記憶體時;
    • 當新建物件被該區中的物件引用時,或者引用了該區域中的物件。

總結

  • Java記憶體塊被劃分為:Code SegmentDataSegmentStackSegmentHeap Segment,其中Heap Segment由於設計物件的建立與銷燬,所以它應該重點關注的物件。
  • Heap Segment又被劃分為兩塊:Young GeneriationOld Generiation
  • Young Genertiation中又被劃分為Eden區和兩個Survivor區,物件在其中採用標記複製演算法來判定一個物件是應該清理還是移到Old Generiation中。該記憶體區域發生GC的頻率較高。
  • Old Generiation發生GC的頻率相對較低。當有大物件被建立,或者和該區域有關的物件被建立時,它將會被直接移動到該區域中。