1. 程式人生 > >Java JVM虛擬機器7塊記憶體區劃分【入門】

Java JVM虛擬機器7塊記憶體區劃分【入門】

順口溜:器池堆,棧棧區區

                棄池堆,站站曲曲(一離開衛生間,肚子疼的站不起身)

Java中記憶體的管理機制是通過JVM來自動管理的。瞭解JVM的基礎原理有利於進一步理解Java。

我們首先要了解java記憶體的分配機制,在java虛擬機器規範裡,JVM被分為7個記憶體區域。

虛擬機器規範中的7個記憶體區域分別是三個執行緒私有的和四個執行緒共享的記憶體區。

執行緒私有的記憶體區域:執行緒私有的記憶體區域與執行緒具有相同的生命週期,它們分別是:指令計數器執行緒棧本地執行緒棧

四個共享區:四個共享區是所有執行緒共享的,在JVM啟動時就會分配,分別是:方法區

 常量池直接記憶體區(即我們通常所說的JVM的記憶體分為堆和棧中的堆,後者就是前面的執行緒棧)。

1 、指令計數器。

我們都知道java的多執行緒是通過JVM切換時間片執行的,因此每個執行緒在某個時刻可能在執行也可能被掛起,那麼當執行緒掛起之後,JVM再次排程它時怎麼知道該執行緒要執行那條位元組碼指令呢?這就需要一個與該執行緒相關的記憶體區域記錄該執行緒下一條指令,而指令計數器就是實現這種功能的記憶體區域。有多少執行緒在編譯時是不確定的,因此該區域也沒有辦法在編譯時分配,只能在建立執行緒時分配,所以說該區域是執行緒私有的,該區域只是指令的計數,佔用的空間非常少,所以虛擬機器規範中沒有為該區域規定OutofMemoryError。

2、執行緒棧(虛擬機器棧)。

每個棧空間的預設大小為0.5M,在1.7裡調整為1M,每呼叫一次方法就會壓入一個棧幀,如果壓入的棧幀深度過大,即方法呼叫層次過深,就會丟擲StackOverFlow,,SOF最常見的場景就是遞迴中,當遞迴沒辦法退出時,就會拋此異常,Hotspot提供了引數設定改區域的大小,使用-Xss:xxK,就可以修改預設大小。

3、本地執行緒棧。

該區域主要是給呼叫本地方法的執行緒分配的,該區域和執行緒棧的最大區別就是,在該執行緒的申請的記憶體不受GC管理,需要呼叫者自己管理,JDK中的Math類的大部分方法都是本地方法,一個值得注意的問題是,在執行本地方法時,並不是執行位元組碼,所以之前所說的指令計數器是沒法記錄下一條位元組碼指令的,當執行本地方法時,指令計數器置為undefined。

本地方法棧與執行緒棧所發揮的作用是非常相似的,其區別在於執行緒棧為虛擬機器執行Java方法服務,而本地方方法棧是為虛擬機器使用到的Native方法服務。

4、方法區。

這塊區域是用來存放JVM裝載的class的類資訊,包括:類的方法、靜態變數、型別資訊(介面/父類),我們使用反射技術時,所需的資訊就是從這裡獲取的。

一般來說這個區域的記憶體回收目標是針對常量池的回收和對型別的解除安裝。

5、常量池。

變數用final修飾,不能再修改它的值,所以就成為常量,而這常量將會存放在常量區,這些常量在編譯時就知道佔用空間的大小,但並不是說明該區域編譯就固定了,執行期也可以修改常量池的大小,典型的場景是在使用String時,你可以呼叫String的 intern(),JVM會判斷當前所建立的String物件是否在常量池中,若有,則從常量區取,否則把該字元放入常量池並返回,這時就會修改常量池的大小(常量的大小是不可改變的)。

6、直接記憶體區。

直接記憶體區並不是JVM可管理的記憶體區。在JDK1.4中提供的NIO中,實現了高效的R/W操作,這種高效的R/W操作就是通過管道機制實現的,而管道機制實際上使用了本地記憶體,這樣就避免了從本地原始檔複製JVM記憶體,再從JVM複製到目標檔案的過程,直接從原始檔複製到目標檔案,JVM通過DirectByteBuffer操作直接記憶體。

==?在JDK1.4中新加入了NIO類,引入了一種基於通道與緩衝區的I/O方式,它可以使用Native函式直接分配堆外記憶體,然後通過一個儲存在Java堆裡面的DirectByteBuffer物件作為這塊記憶體的引用進行操作。這樣能在一些場景中顯著提高效能,因為避免了在Java堆和Native堆中來回複製資料。

7、堆。

堆是JVM中的最常見的、最為熟知的記憶體區,我們通常所說的GC主要就是在這塊區域中進行的,所有的java物件都在這裡分配,這也是JVM中最大的記憶體區域,被所有執行緒共享,成千上萬的物件在這裡建立,也在這裡被銷燬。

物件訪問

在Java語言中,物件的訪問時如何進行的?即使最普通的物件訪問也會涉及到Java堆、Java棧、方法區這三個最重要記憶體區域,如下面這段程式碼:

Object obj = new Object();

假設這句程式碼出現在方法體中,那“Object obj”這部分的語義將會反映到Java棧的本地變量表中,作為一個reference型別出現。而“new Object()”這部分語義將會反映到Java堆中,形成一塊儲存了Object型別所有例項資料的結構化記憶體。在Java堆中還必須包含能查詢到此物件型別資料(如物件型別,父類,實現的介面,方法等)的地址資訊,這些型別資料則儲存在方法區中。