Java記憶體管理的進一步理解-模擬過程圖解--轉載 java的記憶體管理分為: 1、堆記憶體;2、棧記憶體;3、方法區;4、本地方法區 /* 1:方法區 方法區存放裝載的類數據信息包括: (1):基本信息: 1)每個類的全限定名 2)每個類的直接超類的全限定名(可約束類型轉換) 3)該類是類還是介面 4) ...
Java記憶體管理的進一步理解-模擬過程圖解--轉載
java的記憶體管理分為:
1、堆記憶體;2、棧記憶體;3、方法區;4、本地方法區
/*
1:方法區
方法區存放裝載的類數據信息包括:
(1):基本信息:
1)每個類的全限定名
2)每個類的直接超類的全限定名(可約束類型轉換)
3)該類是類還是介面
4)該類型的訪問修飾符
5)直接超介面的全限定名的有序列表
(2):每個已裝載類的詳細信息:
1)運行時常量池:
存放該類型所用的一切常量(直接常量和對其它類型、欄位、方法的符號引用),它們以數組形式通過索引被訪問,是外部調用與類聯繫及類型對象化的橋梁。它是類文件(位元組碼)常量池的運行時表示。(還有一種靜態常量池,在位元組碼文件中)。
2)欄位信息:
類中聲明的每一個欄位的信息(名,類型,修飾符)。
3)方法信息:
類中聲明的每一個方法的信息(名,返回類型,參數類型,修飾符,方法的位元組碼和異常表)。
4)靜態變數
5)到類 classloader 的引用:即到該類的類裝載器的引用。
6)到類 class 的引用:
虛擬機為每一個被裝載的類型創建一個 class 實例, 用來代表這個被裝載的類。
2:棧記憶體
Java棧記憶體以幀的形式存放本地方法的調用狀態(包括方法調用的參數,局部變數,中間結果等)。每調用一個方法就將對應該方法的方法幀壓入 Java 棧,成為當前方法幀。當調用結束(返回)時,就彈出該幀。
編譯器將源代碼編譯成位元組碼(.class)時, 就已經將各種類型的方法的局部變數, 操作數棧大小確定並放在位元組碼中,隨著類一併裝載入方法區。當調用方法時,通過訪問方法區中的類的信息,得到局部變數以及操作數棧的大小。
也就是說: 在方法中定義的一些基本類型的變數和對象的引用變數都在方法的棧記憶體中分配。 當在一段代碼塊定義一個變數時,Java 就在棧中為這個變數分配記憶體空間,當超過變數的作用域後,Java 會自動釋放掉為該變數所分配的記憶體空間, 該記憶體空間可以立即被另作它用。
棧記憶體的構成:
Java 棧記憶體由局部變數區、操作數棧、幀數據區組成。
(1):局部變數區為一個以字為單位的數組,每個數組元素對應一個局部變數的值。調用方法時,將方法的局部變數組成一個數組,通過索引來訪問。若為非靜態方法,則加入一個隱含的引用參數 this,該參數指向調用這個方法的對象。而靜態方法則沒有this參數。因此,在靜態方法里無法訪問對象信息。
(2):操作數棧也是一個數組,但是通過棧操作來訪問。所謂操作數是那些被指令操
作的數據。當需要對參數操作時如 a=b+c,就將即將被操作的參數壓棧,如將 b 和 c 壓棧,
然後由操作指令將它們彈出,並執行操作。虛擬機將操作數棧作為工作區。
(3):幀數據區處理常量池解析,異常處理等
3:堆記憶體
堆記憶體用來存放由 new 創建的對象和數組。在堆中分配的記憶體,由 Java 虛擬機的自動垃圾回收器來管理。
在堆中產生了一個數組或對象後,還可以在棧中定義一個特殊的變數, 讓棧中這個變數的取值等於數組或對象在堆記憶體中的首地址,棧中的這個變數就成了數組或對象的引用變數。
引用變數就相當於是為數組或對象起的一個名稱, 以後就可以在程式中使用棧中的引用變數來訪問堆中的數組或對象。
4:本地方法棧記憶體
與調用的本地方法的語言相關,如:調用的是一個c語言方法則為一個c棧。本地方法可以回調java方法。若有java方法調用本地方法,虛擬機就運行這個本地方法。
在虛擬機看來運行這個本地方法就是執行這個java方法,如果本地方法拋出異常,虛擬機就認為是這個java方法拋出異常。
Java通過Java本地介面JNI(Java Native Interface)來調用其它語言編寫的程式,在Java裡面用native修飾符來描述一個方法是本地方法。
*/
下麵通過一個簡單的代碼示例,理解Java中,記憶體是怎麼進行分配與管理的。示例如下:
public class JavaRamExplain { public static void main(String[] args) { Person.whatCountry(); Person sam = new Person("sam", 20); sam.introduceSelf(); } } class Person { private String name; private int age; public static String country = "china"; public Person(String name, int age) { this.name = name; this.age = age; } public void introduceSelf() { System.out.println("My name's " + name + ",i'm " + age + " years old."); } public static void whatCountry() { System.out.println("This people come from " + country); } }
當開始運行JavaRamExplain類時,JVM便會開始進行記憶體的分配管理工作。
圖解過程為:
(圖片較大,看不清可以圖片右鍵-查看圖像,進行查看原圖)
可以看到到第八步工作結束後:
棧記憶體中的數據全部被釋放(所以說棧記憶體中的數據的生命周期是已知並固定的,因為隨著方法的執行結束,棧記憶體便會進行釋放);
堆記憶體中的Person對象,現已沒有任何對象引用指向它,所以它將被視作記憶體中的“垃圾”,等待回收。(堆記憶體中的數據由java的垃圾回收機制自動進行處理,所以其生命周期不確定)
由圖中也可以看到:
類的方法會進行壓棧和彈棧,對應的,方法中用到的參數(變數)便會相應的在棧記憶體中進行存儲和釋放。所以棧記憶體用於存放局部變數(包括基本類型和引用類型)。
類的對象會被存放到堆記憶體中,相應的該對象所包含的成員變數也會被存放到該空間。
類本身將在jvm通過.class運行該類時就會被載入到記憶體中的方法區內,顧名思義,方法區用於保存類的方法代碼,其中普通方法被存放在非靜態區,靜態成員(方法和變數)被存放在方法區中專門的靜態區當中。
棧記憶體是方法的運行區域(因為方法中定義的局部變數需要在棧里開闢空間),方法區是方法的存放區域。