JVM之JVM内存区域与内存分配(转载)
2019獨角獸企業重金招聘Python工程師標準>>>
先來看看JVM運行時候的內存區域
?大多數 JVM 將內存區域劃分為 Method Area(Non-Heap)(方法區),Heap(堆),Program Counter Register(程序計數器), VM Stack(虛擬機棧,也有翻譯成JAVA 方法棧的),Native Method Stack (本地方法棧),其中Method Area和Heap是線程共享的,VM Stack,Native Method Stack 和Program Counter Register是非線程共享的。為什么分為線程共享和非線程共享的呢?請繼續往下看。
?首先我們熟悉一下一個一般性的 Java 程序的工作過程。一個 Java 源程序文件,會被編譯為字節碼文件(以 class 為擴展名),每個java程序都需要運行在自己的JVM上,然后告知 JVM 程序的運行入口,再被 JVM 通過字節碼解釋器加載運行。那么程序開始運行后,都是如何涉及到各內存區域的呢?
?概括地說來,JVM初始運行的時候都會分配好Method Area(方法區)和Heap(堆),而JVM 每遇到一個線程,就為其分配一個Program Counter Register(程序計數器), VM Stack(虛擬機棧)和Native Method Stack (本地方法棧),當線程終止時,三者(虛擬機棧,本地方法棧和程序計數器)所占用的內存空間也會被釋放掉。這也是為什么我把內存區域分為線程共享和非線程共享的原因,非線程共享的那三個區域的生命周期與所屬線程相同,而線程共享的區域與JAVA程序運行的生命周期相同,所以這也是系統垃圾回收的場所只發生在線程共享的區域(實際上對大部分虛擬機來說知發生在Heap上)的原因。
1. 程序計數器
程序計數器是一塊較小的內存區域,作用可以看做是當前線程執行的字節碼的位置指示器。分支、循環、跳轉、異常處理和線程恢復等基礎功能都需要依賴這個計算器來完成,不多說。
2.VM Strack
先來了解下JAVA指令的構成:
JAVA指令由 操作碼 (方法本身)和 操作數 (方法內部變量) 組成。
1)方法本身是指令的操作碼部分,保存在Stack中;
2)方法內部變量(局部變量)作為指令的操作數部分,跟在指令的操作碼之后,保存在Stack中(實際上是簡單類型(int,byte,short 等)保存在Stack中,對象類型在Stack中保存地址,在Heap 中保存值);
虛擬機棧也叫棧內存,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對于棧來說不存在垃圾回收問題,只要線程一結束,該棧就 Over,所以不存在垃圾回收。也有一些資料翻譯成JAVA方法棧,大概是因為它所描述的是java方法執行的內存模型,每個方法執行的同時創建幀棧(Strack Frame)用于存儲局部變量表(包含了對應的方法參數和局部變量),操作棧(Operand Stack,記錄出棧、入棧的操作),動態鏈接、方法出口等信息,每個方法被調用直到執行完畢的過程,對應這幀棧在虛擬機棧的入棧和出棧的過程。
局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象的引用(reference類型,不等同于對象本身,根據不同的虛擬機實現,可能是一個指向對象起始地址的引用指針,也可能是一個代表對象的句柄或者其他與對象相關的位置)和 returnAdress類型(指向下一條字節碼指令的地址)。局部變量表所需的內存空間在編譯期間完成分配,在方法在運行之前,該局部變量表所需要的內存空間是固定的,運行期間也不會改變。
棧幀是一個內存區塊,是一個數據集,是一個有關方法(Method)和運行期數據的數據集,當一個方法 A 被調用時就產生了一個棧幀 F1,并被壓入到棧中,A 方法又調用了 B 方法,于是產生棧幀 F2 也被壓入棧,執行完畢后,先彈出 F2棧幀,再彈出 F1 棧幀,遵循“先進后出”原則。光說比較枯燥,我們看一個圖來理解一下 Java棧,如下圖所示
3.Heap
Heap(堆)是JVM的內存數據區。Heap 的管理很復雜,是被所有線程共享的內存區域,在JVM啟動時候創建,專門用來保存對象的實例。在Heap 中分配一定的內存來保存對象實例,實際上也只是保存對象實例的屬性值,屬性的類型和對象本身的類型標記等,并不保存對象的方法(以幀棧的形式保存在Stack中),在Heap 中分配一定的內存保存對象實例。而對象實例在Heap 中分配好以后,需要在Stack中保存一個4字節的Heap 內存地址,用來定位該對象實例在Heap 中的位置,便于找到該對象實例,是垃圾回收的主要場所。java堆處于物理不連續的內存空間中,只要邏輯上連續即可。
4.Method Area
Object Class Data(加載類的類定義數據) 是存儲在方法區的。除此之外,常量、靜態變量、JIT(即時編譯器)編譯后的代碼也都在方法區。正因為方法區所存儲的數據與堆有一種類比關系,所以它還被稱為 Non-Heap。方法區也可以是內存不連續的區域組成的,并且可設置為固定大小,也可以設置為可擴展的,這點與堆一樣。
垃圾回收在這個區域會比較少出現,這個區域內存回收的目的主要針對常量池的回收和類的卸載。
5.運行時常量池(Runtime Constant Pool)
方法區內部有一個非常重要的區域,叫做運行時常量池(Runtime Constant Pool,簡稱 RCP)。在字節碼文件(Class文件)中,除了有類的版本、字段、方法、接口等先關信息描述外,還有常量池(Constant Pool Table)信息,用于存儲編譯器產生的字面量和符號引用。這部分內容在類被加載后,都會存儲到方法區中的RCP。值得注意的是,運行時產生的新常量也可以被放入常量池中,比如 String 類中的 intern() 方法產生的常量。
常量池就是這個類型用到的常量的一個有序集合。包括直接常量(基本類型,String)和對其他類型、方法、字段的符號引用.例如:
◆類和接口的全限定名;
◆字段的名稱和描述符;
◆方法和名稱和描述符。
池中的數據和數組一樣通過索引訪問。由于常量池包含了一個類型所有的對其他類型、方法、字段的符號引用,所以常量池在Java的動態鏈接中起了核心作用.
很有用且重要關于常量池的擴展:Java常量池詳解 Integer與new Integer()的區別
6.Native Method Stack
與VM Strack相似,VM Strack為JVM提供執行JAVA方法的服務,Native Method Stack則為JVM提供使用native 方法的服務。
7.直接內存區
直接內存區并不是 JVM 管理的內存區域的一部分,而是其之外的。該區域也會在 Java 開發中使用到,并且存在導致內存溢出的隱患。如果你對 NIO 有所了解,可能會知道 NIO 是可以使用 Native Methods 來使用直接內存區的。
小結:
在此,你對JVM的內存區域有了一定的理解,JVM內存區域可以分為線程共享和非線程共享兩部分,線程共享的有堆和方法區,非線程共享的有虛擬機棧,本地方法棧和程序計數器。
8.JVM運行原理 例子
以上都是純理論,我們舉個例子來說明 JVM 的運行原理,我們來寫一個簡單的類,代碼如下:
?這個類沒有任何意義,不用猜測這個類是做什么用,只是寫一個比較典型的類,然后我們來看
看 JVM 是如何運行的,也就是輸入 java JVMShow 后,我們來看 JVM 是如何處理的:
???? 第 1 步 、向操作系統申請空閑內存。JVM 對操作系統說“給我 64M(隨便模擬數據,并不是真實數據) 空閑內存”,于是,JVM 向操作系統申請空閑內存作系統就查找自己的內存分配表,找了段 64M 的內存寫上“Java 占用”標簽,然后把內存段的起始地址和終止地址給 JVM,JVM 準備加載類文件。
???? 第 2 步,分配內存內存。JVM 分配內存。JVM 獲得到 64M 內存,就開始得瑟了,首先給 heap 分個內存,然后給棧內存也分配好。
???? 第 3 步,文件檢查和分析class 文件。若發現有錯誤即返回錯誤。
???? 第 4 步,加載類。加載類。由于沒有指定加載器,JVM 默認使用 bootstrap 加載器,就把 rt.jar 下的所有類都加載到了堆類存的Method Area,JVMShow 也被加載到內存中。我們來看看Method Area區域,如下圖:(這時候包含了 main 方法和 runStaticMethod方法的符號引用,因為它們都是靜態方法,在類加載的時候就會加載)
第 5 步、執行方法。執行 main 方法。執行啟動一個線程,開始執行 main 方法,在 main 執行完畢前,方法區如下圖所示:
(public final static String ClASS_CONST = "I'm a Const";? )
在 Method Area 加入了 CLASS_CONST 常量,它是在第一次被訪問時產生的(runStaticMethod方法內部)。
???? 堆內存中有兩個對象 object 和 showcase 對象,如下圖所示:(執行了JVMShowcase showcase=new JVMShowcase();? )
為什么會有 Object 對象呢?是因為它是 JVMShowcase 的父類,JVM 是先初始化父類,然后再初始化子類,甭管有多少個父類都初始化。
在棧內存中有三個棧幀,如下圖所示:
于此同時,還創建了一個程序計數器指向下一條要執行的語句。
第 6 步,釋放內存。釋放內存。運行結束,JVM 向操作系統發送消息,說“內存用完了,我還給你”,運行結束。?
?此外還有這個適合看看 Java 內存分配全面淺析
深入Java核心 Java內存分配原理精講???
詳細介紹Java的內存管理與內存泄露
?java內存分配研究
?Java虛擬機(JVM)中的內存設置詳解
轉載于:https://my.oschina.net/u/2297250/blog/384471
總結
以上是生活随笔為你收集整理的JVM之JVM内存区域与内存分配(转载)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 原生js判断css动画结束 css
- 下一篇: 最简单的EasyUI菜单栏