文章目錄
前言
Java作爲一種面向對象的,跨平臺語言,其對象、內存等一直是比較難的知識點。而且很多概念的名稱看起來又那麼相似,很多人會傻傻分不清楚。比如本文我們要討論的JVM內存結構、Java內存模型和Java對象模型,這就是三個截然不同的概念,但是很多人容易弄混。本文我們先來看一下什麼是JVM內存結構。
敘述
我們都知道,Java代碼是要運行在虛擬機上的,而虛擬機在執行Java程序的過程中會把所管理的內存劃分爲若干個不同的數據區域,這些區域都有各自的用途。其中有些區域隨着虛擬機進程的啓動而存在,而有些區域則依賴用戶線程的啓動和結束而建立和銷燬。
(圖片來源於網絡)
運行時數據區域
Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分爲若干個不同的數據區域。這些區域都有各自的用途,以及創建和銷燬的時間,有的區域隨着虛擬機進程的啓動而存在,有些區域則是依賴用戶線程的啓動和結束而建立和銷燬。
方法區(Method Area)
- 用於存放已被加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
- 和java堆一樣不需要連續的內存,並且可以動態擴展,動態擴展失敗一樣會拋出OutOfMemoryError 異常。
- 對這塊區域進行垃圾回收的主要目標是對常量池的回收和對類的卸載,但是一般比較難實現,HotSpot虛擬機把它當成永久代來進行垃圾回收。
- 方法區邏輯上屬於堆的一部分,但是爲了與堆進行區分,通常會被稱爲“非堆”。
運行時常量池(Runtime Constant Pool)
- 運行時常量池是方法區的一部分。
- Class 文件中的常量池(編譯器生成的各種字面量和符號引用)會在類加載後被放入這個區域。
- 除了在編譯期生成的常量,還允許動態生成,例如 String 類的 intern()。這部分常量也會被放入運行時常量池。
堆(Heap)
- 被所有線程共享,在虛擬機啓動時創建,用來存放對象實例,幾乎所有的對象實例都在這裏分配內存。
- 對於大多數應用來說,Java堆(Java Heap)是虛擬機所管理的內存中最大的一塊。
- 由於現在收集器基本都是採用的分代收集算法,所以Java堆中還可以細分爲:新生代和老年代;新生代又有Eden空間、From Survivor空間、To Survivor空間三部分。
- Java 堆不需要連續內存,並且可以通過動態增加其內存,增加失敗會拋出 OutOfMemoryError 異常
虛擬機棧(JVM Stack)
- 線程私有的,它的生命週期與線程相同。
- 虛擬機棧描述的是Java方法執行的內存模型;每個方法被執行的時候都會同時創建一個棧幀(Stack Frame)用於存儲局部變量表、操作棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。
- 局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型),它不等同於對象本身,根據不同的虛擬機實現,它可能是一個指向對象起始地址的引用指針,也可能指向一個代表對象的句柄或者其他與此對象相關的位置)和returnAddress類型(指向了一條字節碼指令的地址)。局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
- 該區域可能拋出以下異常:
- 當線程請求的棧深度超過最大值,會拋出 StackOverflowError 異常;
- 棧進行動態擴展時如果無法申請到足夠內存,會拋出 OutOfMemoryError 異常。
本地方法棧(Native Method Stacks)
- 線程私有。
- 爲虛擬機使用到的Native 方法服務。如Java使用c或者c++編寫的接口服務時,代碼在此區運行。
程序計數器(Program Counter Register)
- 線程私有,它的生命週期與線程相同。
- 可以看做是當前線程所執行的字節碼的行號指示器。
- 如果線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是Natvie方法,這個計數器值則爲空(undefined)。
- 程序計數器中存儲的數據所佔空間的大小不會隨程序的執行而發生改變,所以此區域不會出現OutOfMemoryError的情況。
小結
1.按是否多線程內存共享劃分:
2.按是否存在垃圾收集行爲劃分:
3.按是否拋出OutOfMemoryError異常劃分:
4.按是否拋出OutOfMemoryError異常劃分:
感謝您的閱讀~~