Java內存區域棧、堆、方法區如何劃分

1. 概述

Java程序一般不在乎內存是怎麼分配的,因為有虛擬機幫忙管理,但偶爾會出現奇怪的問題或者內存溢出報錯,這些都非常頭疼,如果不瞭解虛擬機是怎麼管理內存的,那排查錯誤將會非常艱難。

如果僅僅把內存區域認為對象放入堆內存,局部變量放入棧內存略微粗糙,因為實際JVM分配數據比這個要複雜的多。

JVM所管理的內存主要包含運行時數據區域為棧、堆、方法區和程序計數器,如圖所示:

Java內存區域棧、堆、方法區如何劃分

2. 程序計數器

因為Java是多線程來執行的,線程切換後要恢復到原理執行的位置,程序計數器是為這個來服務的。

這樣每個線程都有一個獨立的程序計數器,主要作用是將通過計數器的值選取下一條需要執行的字節碼指令。

3. 棧

Java棧也是線程私有的,每個方法被執行的時候會創建一個棧幀用於存儲局部變量表,操作棧,動態鏈接,方法出口等信息。

局部變量表存放了Java的基本數據類型(boolean、byte、char、short、int、float、long、double)、以及對象引用(reference類型),如果把對象類比成電視機,那麼reference類型就相當於遙控器,你要打開電視機,只需要點擊遙控器的開關就可以。

注意,Java中並不存在string基礎類型,僅有String類

棧內存中並沒有自動垃圾回收(GC),只存在入棧和出棧,入棧則分配內存,出棧則釋放內存。

4. 堆

Java堆被所有線程共享,,和C,C++不一樣,這一塊由JVM管理內存自動垃圾回收,存放對象實例。

堆內存是可以動態大小的,物理上可以不連續,但邏輯上連續的即可,如果堆上面的內存沒有空間分配,會報OutOfMemoryError異常,如果遇到這種錯誤,通常需要使用工具Dump出當前的內存快照以便事後分析。

5. 方法區

方法區和堆一樣是內存共享的,主要存放類信息、常量 (static final)、靜態變量等數據,垃圾回收在該區域同樣會存在

運行時常量池也存放在方法區,運行時常量池包含字符串常量池,Java性能好的一方面,就是把字符串單獨出來處理,這樣做有幾個好處:

1. 在編譯期間把字符串就放到常量池中

2. 同樣的字符串只有一份,防止到處過多佔用內存

6. 對象訪問

簡單舉例:

Book book = new Book();

book會作為一個reference類型存在棧區,new Book() 為實例的結構化內存(包含各個實例字段的數據)反映在堆區,另外,在Java堆中還必須包含能查找到此類型(如對象類型,父類,實現的接口,方法等)的地址信息,這些類型的數據則存儲在方法區內。如圖所示:

Java內存區域棧、堆、方法區如何劃分

7. 小結

堆和棧的概念可以說是Java開發底層的問題,是基本的數據結構,對於瞭解運行時數據區有很大的意義。

當了解運行時數據區的原理是,就會了解為什麼兩個字符串對象不能用“==”來比較,為什麼靜態變量在非線程安全的等這些問題有很大的意義

最後簡單的一個小例子,看各個信息是劃分到哪個存儲區域

Java內存區域棧、堆、方法區如何劃分


分享到:


相關文章: