1. 程式人生 > >JAVA記憶體模型入門

JAVA記憶體模型入門

概述

jvm 入門篇,想要學習jvm,必須先得了解JVM記憶體模型,JVM記憶體模型,JVM記憶體模型,JVM記憶體模型,JVM記憶體模型。重要的事情說多遍。

記憶體劃分

java虛擬機器按照執行時記憶體使用區域劃分如圖:

 

Paste_Image.png

區域 是否執行緒共享 是否會記憶體溢位
程式計數器 不會
java虛擬機器棧
本地方法棧
方法區

一、程式計數器(Program Counter Register)

程式計數器就是記錄當前執行緒執行程式的位置,改變計數器的值來確定執行的下一條指令,比如迴圈、分支、方法跳轉、異常處理,執行緒恢復都是依賴程式計數器來完成。
    Java虛擬機器多執行緒是通過執行緒輪流切換並分配處理器執行時間的方式實現的。為了執行緒切換能恢復到正確的位置,每條執行緒都需要一個獨立的程式計數器,所以它是執行緒私有的。
    如果執行緒正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果正在執行的是Native方法,這個計數器值則為空(Undefined)。此記憶體區域是唯一一個在Java虛擬機器規範中沒有規定任何OutOfMemoryError情況的區域。

二、java虛擬機器棧(VM Stack)

java虛擬機器棧是執行緒私有,生命週期與執行緒相同。建立執行緒的時候就會建立一個java虛擬機器棧。
    虛擬機器執行java程式的時候,每個方法都會建立一個棧幀,棧幀存放在java虛擬機器棧中,通過壓棧出棧的方式進行方法呼叫。
    棧幀又分為一下幾個區域:區域性變量表、運算元棧、動態連線、方法出口等。
    平時我們所說的變數存在棧中,這句話說的不太嚴謹,應該說區域性變數存放在java虛擬機器棧的區域性變量表中。
    java的8中基本型別的區域性變數的值存放在虛擬機器棧的區域性變量表中,如果是引用型的變數,則只儲存物件的引用地址。

注意:

  • 當用戶請求web伺服器,每個請求開啟一個執行緒負責使用者的響應計算(每個執行緒分配一個虛擬機器棧空間),如果併發量大時,可能會導致記憶體溢位(OutOfMemoneyError),可以適當的把每個虛擬機器棧的大小適當調小一點,減少記憶體的使用量來提高系統的併發量。
  • 當棧空間調小以後,又會引發方法呼叫深度的的問題。因為,每個方法都會生成一個棧幀,如果方法呼叫深度很深就意味著,棧裡面存放大量的棧幀,可能導致棧記憶體溢位(StackOverFlowError)。

三、本地方法棧(Native Method Stack)

本地方法棧 為虛擬機器使用到本地方法服務(native)。本地方法棧為執行緒私有,功能和虛擬機器棧非常類似。執行緒在呼叫本地方法時,來儲存本地方法的區域性變量表,本地方法的運算元棧等等資訊。

本地方法:是非java語言實現的方法,例如,java呼叫C語言,來操作某些硬體資訊。

四、堆(Heap):

堆是被所有執行緒共享的區域,實在虛擬機器啟動時建立的。堆裡面存放的都是物件的例項(new 出來的物件都存在堆中)。
    我們平常所說的垃圾回收,主要回收的就是堆區。為了提升垃圾回收的效能,又把堆分成兩塊區新生代(young)年老代(old),更細一點劃分新生代又可劃分為Eden區和2個Survivor區(From Survivor和To Survivor)。
如下圖結構:

Paste_Image.png

 

  • Eden:新建立的物件存放在Eden區
  • From Survivor和To Survivor:儲存新生代gc後還存活的物件。(使用複製演算法,導致有一個Survivor空間浪費)Hotspot虛擬機器新生代Eden和Survivor的大小比值為4:1,因為有兩個Survivor,所以Eden:From Survivor:To Survivor比值為8:1:1。
  • 老年代:物件存活時間比較長(經過多次新生代的垃圾收集,預設是15次)的物件則進入老年的。
    當堆中分配的物件例項過多,且大部分物件都在使用,就會報記憶體溢位異常(OutOfMemoneyError)。

五、方法區

方法區是被所有執行緒共享區域,用於存放已被虛擬機器載入的類資訊,常量,靜態變數等資料。被Java虛擬機器描述為堆的一個邏輯部分。習慣是也叫它永久代(permanment generation)
    永久代也會垃圾回收,主要針對常量池回收,型別解除安裝(比如反射生成大量的臨時使用的Class等資訊)。
    常量池用於存放編譯期生成的各種位元組碼和符號引用,常量池具有一定的動態性,裡面可以存放編譯期生成的常量;執行期間的常量也可以新增進入常量池中,比如string的intern()方法。
    當方法區滿時,無法在分配空間,就會丟擲記憶體溢位的異常(OutOfMemoneyError)。
java8中已經沒有方法區了,取而代之的是元空間(Metaspace)。

六:直接記憶體

直接記憶體(Direct Memory)並不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域,但是這部分記憶體也被頻繁地使用,而且也可能導致OutOfMemoryError異常出現。
    JDK1.4加的NIO中,ByteBuffer有個方法是allocateDirect(int capacity) ,這是一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可以使用Native函式庫直接分配堆外記憶體,然後通過一個儲存在Java堆裡面的DirectByteBuffer物件作為這塊記憶體的引用進行操作。這樣能在一些場景中顯著提高效能,因為避免了在Java堆和Native堆中來回複製資料。

jvm 記憶體模型 就是這麼簡單,如果看完還不太理解的,就多看幾遍,把 jvm 記憶體模型背下來,記到腦子裡。