JVM筆記【1】-- 執行時資料區

第十六封發表於2020-12-26

(一)java記憶體區域管理

C/C++每一個new操作都需要自己去delete/free,而java裡面有虛擬機器自動管理記憶體,不容易出現記憶體洩漏或者溢位的問題,但是不容易出現不代表不出現,瞭解虛擬機器怎麼使用和管理記憶體是十分重要的是,對程式優化或者問題排查有幫助。

執行時區域主要分為:

  • 執行緒私有:
    • 程式計數器:Program Count Register,執行緒私有,沒有垃圾回收
    • 虛擬機器棧:VM Stack,執行緒私有,沒有垃圾回收
    • 本地方法棧:Native Method Stack,執行緒私有,沒有垃圾回收
  • 執行緒共享:
    • 方法區:Method Area,以HotSpot為例,JDK1.8後元空間取代方法區,有垃圾回收。
    • 堆:Heap,垃圾回收最重要的地方。

image-20201222221827719

1.1 程式計數器

空間很小,當前執行緒執行的位元組碼的行號指示器(執行緒獨有,指示當前執行到哪,下一步需要執行哪一個位元組碼),分支,迴圈,跳轉,異常處理,執行緒恢復都需要依賴它。
執行緒私有:java多執行緒其實是執行緒輪流切換並分配處理器執行時間的方式實現,一個核一個具體的時間點,只會執行一個執行緒的指令。執行緒切換需要儲存和恢復正確的執行位置(保護和恢復現場),所以不同的執行緒需要不同的程式計數器。

  • 執行java方法時,程式計數器記錄的是正在執行的位元組碼指令地址
  • 執行Native方法,程式計數器為空

唯一一個沒有規定任何OutOfMemory的區域,也沒有GC(垃圾回收)。

1.2 虛擬機器棧

執行緒私有,生命週期和執行緒一樣,主要是記錄該執行緒Java方法執行的記憶體模型。虛擬機器棧裡面放著好多棧幀。注意虛擬機器棧,對應是Java方法,不包括本地方法。
一個Java方法執行會建立一個棧幀,一個棧幀主要儲存:

  • 區域性變數表
  • 運算元棧
  • 動態連結
  • 方法出口
    每一個方法呼叫的時候,就相當於將一個棧幀放到虛擬機器棧中(入棧),方法執行完成的時候,就是對應著將該棧幀從虛擬機器棧中彈出(出棧)。

每一個執行緒有一個自己的虛擬機器棧,這樣就不會混起來,如果不是執行緒獨立的話,會造成呼叫混亂。

大家平時說的java記憶體分為堆和棧,其實就是為了簡便的不太嚴謹的說法,他們說的棧一般是指虛擬機器棧,或者虛擬機器棧裡面的區域性變數表。

區域性變數表一般存放著以下資料:

  • 基本資料型別(boolean,byte,char,short,int,float,long,double
  • 物件引用(reference型別,不一定是物件本身,可能是一個物件起始地址的引用指標,或者一個代表物件的控制程式碼,或者與物件相關的位置)
  • returAddress(指向了一條位元組碼指令的地址)

區域性變數表記憶體大小編譯期間確定,執行期間不會變化。空間衡量我們叫Slot(區域性變數空間)。64位的long和double會佔用2個Slot,其他的資料型別佔用1個Slot。

異常:

  • StackOverflowError:執行緒請求的棧深度大於虛擬機器允許的深度
  • OutOfMemoryError:記憶體不足

1.3 本地方法棧

和虛擬機器棧類似,對應本地方法,Native,虛擬機器規範允許語言,使用方式和資料結構不同,有些可能將虛擬機器棧和本地方法棧合併。
異常與虛擬機器棧一致:

  • StackOverflowError:執行緒請求的棧深度大於虛擬機器允許的深度
  • OutOfMemoryError:記憶體不足

1.4 java堆

堆是記憶體管理最大的一塊,執行緒共享。

虛擬機器規範中說,所有的物件例項和陣列都要在堆上分配。但是實際上不是所有的物件都在堆上分配,這個和JIT編譯器的發展和逃逸分析技術相關。Why?
// TODO
堆的細分:新生代,老年代,再細分有Eden,From survivor,To survivor等。

堆中也有可能有執行緒私有的區域,分配緩衝區。

物理上可以不連續,但是邏輯上是連續的。

異常:

  • OutOfMemoryError:記憶體不足

1.5 方法區

名為非堆,但是實際和堆一樣,是執行緒共享的區域,主要存貯以下資訊:

  • 已被虛擬機器載入的類資訊
  • 常量
  • 靜態變數
  • 即時編譯器編譯後的程式碼

方法區不等於永久代,指示Hotspot虛擬機器將GC分代收集擴充到方法區,也就是用永久代實現了方法區,而其他的虛擬機器不一定,不是固定的。JDK1.7將永久代的字串常量移出了。

方法區回收垃圾的效果不是很好,可以選擇不回收,虛擬機器可以決定,當然也可能發生記憶體洩漏。
異常:

  • OutOfMemoryError:記憶體分配異常

1.5.1 執行時常量池

執行時常量池時方法區的一部分,但是不是全部,Class檔案主要包括:

  • 類的版本
  • 欄位
  • 方法
  • 介面
  • 常量池,存放編譯產生的字面量和符號引用,一般除了描述Class檔案的符號引用,還有直接引用也在裡面。是動態的,執行時可以產生,比如String.intern()方法。

異常:

  • OutOfMemoryError:記憶體分配異常

(二)直接記憶體

不是虛擬機器執行時資料區,也不是規範規定的區域,但是使用頻繁且可能會有OutOfMemoryError:記憶體分配異常出現。
比如,NIO(1.4)基於Channel與Buffer的I/O,可以用Native函式直接分配堆外記憶體,通過儲存在Java堆中的DirectByteBuffer物件作為引用來操作,提高效能,不需要Java堆和Native堆都來回複製資料。

直接記憶體受物理的記憶體,或者處理器定址空間之類的限制。

本文系JVM學習相關筆記,整理來自周志明老師的《深入理解Java虛擬機器》,無比欽佩,強烈推薦!

【作者簡介】
秦懷,公眾號【秦懷雜貨店】作者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。這個世界希望一切都很快,更快,但是我希望自己能走好每一步,寫好每一篇文章,期待和你們一起交流。

此文章僅代表自己(本菜鳥)學習積累記錄,或者學習筆記,如有侵權,請聯絡作者核實刪除。人無完人,文章也一樣,文筆稚嫩,在下不才,勿噴,如果有錯誤之處,還望指出,感激不盡~

相關文章