天天看點

JVM記憶體管理機制--運作時資料區域(詳解)

JVM記憶體管理機制--運作時資料區域(詳解)

一、介紹

Java源代碼檔案(.java字尾)會被Java編譯器編譯為位元組碼檔案(.class字尾),然後由JVM中的類加載器加載各個類的位元組碼檔案,加載完畢之後,交由JVM執行引擎執行。在整個程式執行過程中,JVM會用一段空間來存儲程式執行期間需要用到的資料和相關資訊,這段空間一般被稱作為Runtime Data Area(運作時資料區),也就是我們常說的JVM記憶體。是以,在Java中我們常常說到的記憶體管理就是針對這段空間進行管理(如何配置設定和回收記憶體空間)。

java虛拟機在執行java程式的過程中,會把它所管理的記憶體分成不同的區域來存放不同子產品的資料資訊。他們有不同的用途,建立和銷毀時間,有兩種情況:

第一種:随jvm虛拟機啟動而建立的資料區:方法區和heap,所有的線程均共有他們。

第二種:每個java線程獨有的資料區:程式計數器、虛拟機棧、本地方法區棧。

下面逐個詳細介紹:

1 方法區

1.方法區又叫靜态區,跟堆一樣,被所有的線程共享。方法區包含所有的class和static變量。

2.方法區中包含的都是在整個程式中永遠唯一的元素,如class,static變量。

3.—,全局變量和靜态變量的存儲是放在一塊的,初始化的全局變量和靜态變量在一塊區域, 未初始化的全局變量和未初始化的靜态變量在相鄰的另一塊區域.

也稱”永久代” 、“非堆”, 它用于存儲虛拟機加載的類資訊、常量、靜态變量、是各個線程共享的記憶體區域。預設最小值為16MB,最大值為64MB,可以通過-XX:PermSize 和 -XX:MaxPermSize 參數限制方法區的大小。

運作時常量池:是方法區的一部分,Class檔案中除了有類的版本、字段、方法、接口等描述資訊外,還有一項資訊是常量池,用于存放編譯器生成的各種符号引用,這部分内容将在類加載後放到方法區的運作時常量池中.

當JVM使用類裝載器裝載某個類時,它首先要定位對應的class檔案,然後讀入這個class檔案,最後,JVM提取該檔案的内容資訊,并将這些資訊存儲到方法區,最後傳回一個class執行個體。上面是對類的裝載過程作了個簡單的描述,看了上面一段文字,也許你會問:方法區是什麼?裡面存了哪些内容?下面我們将對方法區作一個詳細的描述。

方法區是什麼?有哪些特點?

方法區是系統配置設定的一個記憶體邏輯區域,是用來存儲類型資訊的(類型資訊可了解為類的描述資訊)。方法區主要有以下幾個特點:

一.方法區是線程安全的。由于所有的線程都共享方法區,是以,方法區裡的資料通路必須被設計成線程安全的。例如,假如同時有兩個線程都企圖通路方法區中的同一個類,而這個類還沒有被裝入JVM,那麼隻允許一個線程去裝載它,而其它線程必須等待

二.方法區的大小不必是固定的,JVM可根據應用需要動态調整。同時,方法區也不一定是連續的,方法區可以在一個堆(甚至是JVM自己的堆)中自由配置設定。

三.方法區也可被垃圾收集,當某個類不在被使用(不可觸及)時,JVM将解除安裝這個類,進行垃圾收集

方法區裡存放的是哪些内容?

方法區裡存的都是類型資訊,也就是類的資訊,而類的資訊又包括以下内容:

1.類的全限定名(類的全路徑名)

2.類的直接超類的全限定名(如果這個類是Object,則它沒有超類)

3.這個類是類型(類)還是接口

4.類的通路修飾符,如public、abstract、final等

5.所有的直接接口全限定名的有序清單(假如它實作了多個接口)

6. 常量池

7.字段、方法資訊、類變量資訊(靜态變量) 裝載該類的裝載器的引用(classLoader)、類型引用(class) .

其實,我們沒必要全部記住,隻要根據上面内容有個大概的了解,然後對類型這個概念有個大概的認識即可。下面我們将主要對常量池和類變量資訊作一下分析。

先說類變量吧,類變量内容少些,描述起來比較容易。類變量,顧名思義,就是屬于類的變量,所有類的執行個體都共享的變量,也就是常說的靜态變量。關于類變量,我們隻要知道方法區裡有個靜态區,靜态區是專門用來存放靜态變量以及靜态塊的。所有類的執行個體都共享方法區中的内容。通路類變量的方式可通過執行個體(對象)來通路,也可通過類型來直接通路,java規範推薦使用類型來直接通路。

2 堆(HEAP)

對于大多數Java程式來說,堆是虛拟機管理的記憶體最大的一個地方,它存放了幾乎所有的執行個體對象與數組,它被程式中所有線程所共享。java堆是垃圾集收器管理的主要地方。Java中,程式員基本不用去關心空間釋放的問題,Java的垃圾回收機制會自動進行處理。是以這部分空間也是Java垃圾收集器管理的主要區域。另外,堆是被所有線程共享的,在JVM中隻有一個堆。

3 程式計數器

程式計數器是記憶體管理中的一個小部分區域,它可以看過每個線程所執行的位元組碼的行号訓示器,位元組解釋器執行時就是改變這個行号訓示器來進行擷取下一條需要執行的位元組碼指令,分支、跳轉、循環、異常處理、線程恢複等都依賴這個計數器來完成。

由于java虛拟機的多線程是通過線程輪流切換、并配置設定處理器執行時間的方式來實作的,在任何一個确定的時刻,一個處理器隻執行一個線程中的一條指令。是以,為了在每個線程切換後繼續擷取到正确的指令,每個線程中都存在一個程式計數器來記錄每個線程的下個需要執行的指令。各個線程之間的程式計數器均互相不影響。

由于程式計數器中存儲的資料所占空間的大小不會随程式的執行而發生改變,是以,對于程式計數器是不會發生記憶體溢出現象(OutOfMemory)的。

4 java虛拟機棧

與程式計數器一樣,虛拟機棧也是每個線程私有的,他的生命周期與線程一樣。虛拟機棧描述的是java方法執行的記憶體模型,也就是,每個方法在執行的同時都會建立一個棧幀,多個方法執行時會存在多個棧幀,但目前執行的方法根據棧的特性總是在棧頂,如圖,每個棧幀包含的資訊:

JVM記憶體管理機制--運作時資料區域(詳解)

每個方法的開始執行到結束,都對應每個棧幀在虛拟機棧中的入棧和出棧的過程。其中圖中的局部變量表中存放各種基本類型(char,byte,short,int,long,float ,double ,boolean)、對象引用和returnAddress(指向了一條位元組碼指令的位址)。

5 本地方法棧

與虛拟機棧基本類似,差別在于虛拟機棧為虛拟機執行的java方法服務,而本地方法棧則是為Native方法服務。

另外:有關垃圾回收的知識,可以參考一遍文章,寫的簡單明了,很容易接收:

虛拟機–新生代與老年代GC

http://my.oschina.net/sunnywu/blog/332870