1. 程式人生 > >JVM筆記【1】-- 執行時資料區

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

[TOC] # (一)java記憶體區域管理 C/C++每一個new操作都需要自己去delete/free,而java裡面有虛擬機器自動管理記憶體,不容易出現記憶體洩漏或者溢位的問題,但是不容易出現不代表不出現,瞭解虛擬機器怎麼使用和管理記憶體是十分重要的是,對程式優化或者問題排查有幫助。 執行時區域主要分為: - 執行緒私有: - 程式計數器:`Program Count Register`,執行緒私有,沒有垃圾回收 - 虛擬機器棧:`VM Stack`,執行緒私有,沒有垃圾回收 - 本地方法棧:`Native Method Stack`,執行緒私有,沒有垃圾回收 - 執行緒共享: - 方法區:`Method Area`,以`HotSpot`為例,`JDK1.8`後元空間取代方法區,有垃圾回收。 - 堆:`Heap`,垃圾回收最重要的地方。 ![image-20201222221827719](https://markdownpicture.oss-cn-qingdao.aliyuncs.com/blog/image-20201222221827719.png) ## 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虛擬機器》,無比欽佩,強烈推薦! **【作者簡介】**: 秦懷,公眾號【**秦懷雜貨店**】作者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。這個世界希望一切都很快,更快,但是我希望自己能走好每一步,寫好每一篇文章,期待和你們一起交流。 此文章僅代表自己(本菜鳥)學習積累記錄,或者學習筆記,如有侵權,請聯絡作者核實刪除。人無完人,文章也一樣,文筆稚嫩,在下不才,勿噴,如果有錯誤之處,還望指出,感激不盡~ ![](https://markdownpicture.oss-cn-qingdao.aliyuncs.com/blog/202010120008