JAVA的記憶體模型及結構
原文連結 譯文連結 作者:Tai Truong 譯者:Jaxon
所有的Java開發人員可能會遇到這樣的困惑?我該為堆記憶體設定多大空間呢?OutOfMemoryError的異常到底涉及到執行時資料的哪塊區域?該怎麼解決呢?
Java記憶體模型堆記憶體存放物件以及陣列的資料,方法區存放類的資訊(包括類名、方法、欄位)、靜態變數、編譯器編譯後的程式碼,本地區包含執行緒棧、本地方法棧等存放執行緒
方法區有時被稱為持久代(PermGen)。
所有的物件在例項化後的整個執行週期內,都被存放在堆記憶體中。堆記憶體又被劃分成不同的部分:伊甸區(Eden),倖存者區域(Survivor Sapce),老年代(Old Generation Space)。
方法的執行都是伴隨著執行緒的。原始型別的本地變數以及引用都存放線上程棧中。而引用關聯的物件比如String,都存在在堆中。為了更好的理解上面這段話,我們可以看一個例子:
import java.text.SimpleDateFormat; import java.util.Date; import org.apache.log4j.Logger; public class HelloWorld { private static Logger LOGGER = Logger.getLogger(HelloWorld.class.getName()); public void sayHello(String message) { SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.YYYY"); String today = formatter.format(new Date()); LOGGER.info(today + ": " + message); } }
這段程式的資料在記憶體中的存放如下:
通過JConsole工具可以檢視執行中的Java程式(比如Eclipse)的一些資訊:堆記憶體的分配,執行緒的數量以及載入的類的個數;
Java記憶體結構堆記憶體
堆記憶體同樣被劃分成了多個區域:
- 包含伊甸(Eden)和倖存者區域(Survivor Sapce)的新生代(Young generation)
- 老年代(Old Generation)
不同區域的存放的物件擁有不同的生命週期:
- 新建(New)或者短期的物件存放在Eden區域;
- 倖存的或者中期的物件將會從Eden區域拷貝到Survivor區域;
- 始終存在或者長期的物件將會從Survivor拷貝到Old Generation;
生命週期來劃分物件,可以消耗很短的時間和CPU做一次小的垃圾回收(GC)。原因是跟C一樣,記憶體的釋放(通過銷燬物件)通過2種不同的GC實現:Young GC、Full GC。
為了檢查所有的物件是否能夠被銷燬,Young GC會標記不能銷燬的物件,經過多次標記後,物件將會被移動到老年代中。
哪兒的OutOfMemoryError
對記憶體結構清晰的認識同樣可以幫助理解不同OutOfMemoryErrors:
Exception in thread “main”: java.lang.OutOfMemoryError: Java heap space
原因:物件不能被分配到堆記憶體中Exception in thread “main”: java.lang.OutOfMemoryError: PermGen space
原因:類或者方法不能被載入到老年代。它可能出現在一個程式載入很多類的時候,比如引用了很多第三方的庫;
Exception in thread “main”: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
原因:建立的陣列大於堆記憶體的空間
Exception in thread “main”: java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space?
原因:分配本地分配失敗。JNI、本地庫或者Java虛擬機器都會從本地堆中分配記憶體空間。
Exception in thread “main”: java.lang.OutOfMemoryError: <reason> <stack trace>(Native method)
原因:同樣是本地方法記憶體分配失敗,只不過是JNI或者本地方法或者Java虛擬機發現;
參考連結: