筆者做爲Java小菜雞,近期在看JVM時發現本身很容易忘記JVM最基礎的部分:運行時數據區域大體劃分,特此摘記下來以便未來重溫。java
話很少說,先上示意圖程序員
正如圖中所示,JVM數據區大體分爲:程序計數器、Java虛擬機棧、本地方法棧(有些虛擬機廠商會選擇將其與虛擬機棧結合)、Java堆、方法區(包含運行時常量池)以及直接內存(這部分實際上不屬於Java虛擬機,可是在Java堆中有變量DirectByBuffer能夠直接指向該區域)算法
依次記錄下本身對這些區域的理解:併發
- 程序計數器:屬於線程私有的數據區,即每條線程都會有本身的程序計數器,這是一片比較小的內存區域,用來指示當前線程下一條執行的指令字節碼的地址。有一點須要注意的,當線程執行的不是Java方法,而是Native方法的時候,程序計數器會設爲空(Undefined)。
- 該區域是惟一一片沒有規定任何OutOfMemoryError的區域
- Java虛擬機棧:與程序計數器同樣,是屬於線程私有的,用來記錄Java方法執行的內存模型
- Java的方法在執行的時候都會爲其建立一個棧幀,棧幀內保存的是此方法的局部變量表、操做數棧、動態連接、方法出入口等,每個方法在執行期間都對應着一個棧幀。
- 局部變量表中存放的是編譯期間所知的各類數據類型(基本數據類型+對象引用+returnAddress類型),進入方法時,此方法在幀中分配多大的局部變量表就徹底肯定了,而且在運行期間是不會改變變量表的大小的
- 本地方法棧(Native Method Stack):與虛擬機棧的做用及其類似,當時就筆者理解,Native棧負責記錄的是Native服務或者是其餘方法的信息。在有些虛擬機中會將這部分與虛擬機棧合併(如: Sun Hot Spot)
- Java 堆:分配內存區域最大的一塊,幾乎全部的對象實例都存放在這片區域。特別注意:這塊區域是多條線程共享的區域
- Java堆也是 GC (Garbage Collection)的主要區域,從GC收集器所使用的 分代收集算法 來看,這一部分又能細分爲:新生代與老生代,再進一步細分能夠劃分爲:Eden空間(永生代 佔比8)、From Survivor 空間(From存活區 佔比1)、To Survivor空間(To存活區 佔比1)。可是從內存分配的角度來看,因爲做爲共享的區域,其也可劃分爲多個線程私有的分配緩衝區(Thread Local Allocation Buffer),也就是咱們俗稱的線程變量副本區域。
- Java堆不要求使用連續的存儲空間,只需邏輯連續便可
- 方法區(Method Area):與Java堆同樣,都屬於多條線程共享的內存區域,它主要用於存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼(IL)等數據。
- 又稱Java 堆的邏輯區,可是與Java堆是由區分的。
- 能夠選擇不對方法區進行GC。由於此區域GC的出現頻率較低,也主要是針對常量池的回收和已加載類的卸載。
- 運行時常量池(Runtime Constant Pool):隸屬方法區的一部分,在不少地方會單獨將此部分拿出來討論。
- Class文件中除了類的基本信息(版本、字段、方法、接口等)還有此類的常量池(存放編譯期間生成的各類字面量和符號引用),而這個常量池即是存放於運行時常量池中的(在類加載後)
- 運行期間也能夠對常量池進行擴充,例如:String.intern() 將會將此字符串對象的值寫入常量池中並返回其在常量池中的引用。
- 直接內存:筆者的理解,與本地方法區的意圖類似,借用非JVM的內存(本機的內存)進行操做。
重新建一個對象的角度來看,當new關鍵字出現後,JVM會去檢查new後的類是否已經加載到了方法區中(類的常量池等),若已經加載,則根據其所需內存在Java堆中爲準備進行的實例化操做劃分出一片內存區域。每次分配內存的時候,實際上會根據JVM採用的不一樣的收集器決定其內存的分配方式(取決於收集器是否具備壓縮整理的功能)大體分爲兩種分配方式:jvm
- 指針碰撞(Bump the pointer) 將內存區域劃分爲兩片區域,一邊是已經分配的內存區,一邊是未分配的,中間依靠一個指針進行內存的分配(須要分配新的內存則將指針向下移動)。這種方式能確保內存分配的規整。
- 使用常見的Serial、ParNew收集器(帶有Compact過程)會採用此分配方式
- 空閒列表(Free list)不要求內存的規整,須要維護一個記錄內存分配狀況的列表。
- 使用基於 標記-清理(Mark-Sweep) 收集算法的收集器(如:CMS)會採用此分配方式
同時,有常見的兩種方案用於保證能支持併發分配內存:對內存分配採用同步的機制以及採用TLAB。在內存分配後,JVM就會對分配的內存空間進行初始化(設爲零值),而後確認該對象的類型、運行狀態等信息(設置對象的對象頭)。在完成上述的步驟後,從JVM的角度看,一個新對象已經生成了,接下來就開始執行程序員設置的init部分,按照咱們的意願對此對象進行初始化了。ide
補充說明下:wordpress
- 對象的內存分配包含三塊區域:對象頭、實例數據、對齊填充。
- 對象的兩種定位方式:句柄和直接指針