《深入了解java虛拟機》學習筆記1——Java記憶體結構
java虛拟機規範規定的java虛拟機記憶體其實就是java虛拟機運作時資料區,其架構如下:
其中方法區和堆是由所有線程共享的資料區。
Java虛拟機棧,本地方法棧和程式計數器是線程隔離的資料區。
(1).程式計數器:
是一塊較小的記憶體空間,其作用可以看作是目前線程所執行的位元組碼的行号訓示器,位元組碼解析器工作時通過改變程式計數器的值來選取下一條需要執行的位元組碼指令。程式的分支、循環、跳轉、異常處理以及線程恢複等基礎功能都是依賴程式計數器來完成。
Java虛拟機的多線程是通過線程輪流切換并配置設定處理器執行時間片來實作,在任何一個時刻,一個處理器隻會執行一條線程指令,是以,為了確定線程切換之後能恢複到正确的執行位置,每條線程都需要一個獨立的程式計數器,是以程式計數器是線程私有的記憶體。
程式計數器是java虛拟機中唯一一個沒有規定任何記憶體溢出OutOfMemoryError的記憶體區域。
(2).java虛拟機棧:
Java虛拟機棧也是線程私有的,它的生命周期與線程相同。虛拟機棧描述的是java方法執行的記憶體模型:每個方法被執行時都會同時建立一個棧幀用于存放局部變量表、操作數棧、動态連接配接和方法出口等資訊。每個方法被調用直至執行完成過程,就對應着一個棧幀在虛拟機中從入棧到出棧的過程。
Java虛拟機棧的局部變量表存放了編譯器可知的8種java基本類型資料、對象引用(注意不是對象執行個體本身)、方法傳回位址returnAddress。
Java虛拟機棧的局部變量表空間機關是槽(Slot),其中64位長度的double和long類型會占用兩個slot,其餘的資料類型隻占用一個slot。局部變量表所需記憶體空間在編譯期間完成配置設定,當進入一個方法時,該方法需要在幀中配置設定多大的局部變量空間是完全确定的,在方法運作期間不會改變局部變量表的大小。
Java虛拟機棧有兩種異常狀況:如果線程請求的棧深度大于虛拟機所允許的最大深度時,抛出StackOverflowError異常;如果虛拟機棧可以動态擴充,當擴充時無法申請到足夠記憶體時會抛出OutOfMemoryError異常。
(3).本地方法棧:
本地方法棧與java虛拟機棧作用非常類似,其差別是:java虛拟機棧是為虛拟機執行java方法服務,而本地方法棧是為虛拟機調用的作業系統本地方法服務。
Java虛拟機規範沒有對本地方法棧的實作和資料結構做強制規定,Sun HotSpot虛拟機直接把java虛拟機棧和本地方法棧合二為一。
與java虛拟機棧類似,本地方法棧也會抛出StackOverflowError異常和OutOfMemoryError異常。
(4).堆:
堆是java虛拟機所管理的記憶體區域中最大一塊,java堆是被所有線程所共享的一塊記憶體區域,在java虛拟機啟動時建立,堆記憶體的唯一目的就是存放對象執行個體。幾乎所有的對象執行個體都是在堆配置設定記憶體。
Java堆是垃圾收集器管理的主要區域,從垃圾回收的角度看,由于現在的垃圾收集器基本都采用的是分代收集算法,是以java堆還可以初步細分為新生代和年老代。
Java虛拟機規範規定,堆可以處于實體上不連續的記憶體空間中,隻要邏輯上是連續的即可。在實作上即可以是固定大小的,也可以是可動态擴充的。如果在堆中沒有記憶體完成執行個體配置設定,并且堆大小也無法在擴充時,将會抛出OutOfMemoryError異常。
(5).方法區:
方法區與堆一樣,是被各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯後的代碼等資料。雖然java虛拟機規範把方法區描述為堆的一個邏輯部分,但是方法區卻有一個别名叫Non-Heap(非堆)。
Sun HotSpot虛拟機把方法區叫永久代(Permanent Generation),方法區中最重要的部分是運作時常量池。Class檔案中除了有類的版本、字段、方法、接口等描述資訊外,還有一項資訊是常量池,用于存放編譯期生成的各種字面變量、符号引用、直接引用等,這些内容将在類加載後存放到方法區的運作時常量池中,另外在運作期間也可以将新的常量存放到常量池中,如String的intern()方法。
方法區和運作時常量池在無法滿足記憶體配置設定時,也會抛出OutOfMemoryError異常。
(6).直接記憶體:
直接記憶體并不是java虛拟機運作時資料區的一部分,也不是java虛拟機規範中定義的記憶體區域,但是在java開發中還是會使用到。
JDK1.4中新引入的NIO(new I/O),引入了一種基于通道(Channel)和緩沖區(Buffer)的I/O方式,可以使用作業系統本地方法庫直接配置設定堆外記憶體,然後通過一個存儲在java堆裡面的DirectByteBuffer對象作為堆外直接記憶體的引用進行操作,避免了java堆記憶體和本地直接記憶體間的資料拷貝,可以顯著提高性能。
雖然直接記憶體并不直接收到java虛拟機記憶體影響,但是如果java虛拟機各個記憶體區域總和大于實體記憶體限制,進而導緻直接記憶體不足,動态擴充時也會抛出OutOfMemoryError異常。
java虛拟機記憶體結構中的程式計數器、虛拟機棧和本地方法棧這三個區域随線程建立而生,随線程銷毀而滅,是以這三個區域的記憶體配置設定和回收是确定的,java垃圾收集器重點關注的是java虛拟機的堆記憶體和方法區記憶體。

