JVM系列(三)內存劃分

? ? ? ?我們的java代碼經(jīng)過了類加載到了JVM運行時候,就需要使用到多塊內存空間,不同的空間存放不同的數(shù)據(jù),配合代碼流程,才能讓程真正運行起來。

? ? ? JVM內存劃分在JDK1.8之后和之前是略有不同主要是將方法區(qū)調整為了元空間

JDK1.8之前:

JDK1.8之后:

? ? ? ?圖上線程區(qū)的域的數(shù)據(jù)是私有的,其他區(qū)域的數(shù)據(jù)是共享的。

? ? ? ?內存的劃分其實就是為了我們的代碼在運行的過程中根據(jù)不同的需要來使用的,具體有以下:

(1)方法區(qū)(存放類)

? ? ? ? 方法區(qū)與堆一樣,是各個線程共享內存區(qū)域,方法區(qū)也被稱為非堆或者永久代。方法區(qū)在JDK1.8以前的版本,代表JVM的一塊區(qū)域。主要存放.class文件里加載進來的類,同時也會存放一些類似常量池的東西在這個區(qū)域。 但是在JDK1.8之后這里就改名為MetaSpace(元空間),主要還是用來存放各種類相關信息。

? ? ? ? 整個永久代有一個JVM本身設置的固定大小上限,無法進行調整。但是1.8后元空間使用的是直接內存,受本機內存的限制,內存溢出的幾率比永久代小了很多

(2)運行時常量

? ? ? .class文件中除了存放類版本、字段、方法、接口等描述信息外、還有常量池(用于存放編譯期生成的各種字面量和符號引用),?運行時常量是方法區(qū)的一部分,受到方法區(qū)內存限制,當常量無法在申請到內存空間時候會拋出OutofMemoryError異常。在JDK1.7之前運行時常量池邏輯包含字符串常量池,JDK1.7字符串常量池被從方法區(qū)拿到了堆中。運行時常量池還在方法區(qū),JDK1.8移除了永久代,運行時常量轉移到元空間中

(3)程序計數(shù)器(執(zhí)行代碼指令)

? ? ? ?我們所開發(fā)的代碼是.java文件是面向開發(fā)人員的,但是計算機是無法讀懂的,所以就需要編譯為.class字節(jié)碼文件,字節(jié)碼才是計算機可以理解的文件。字節(jié)碼的指令會讓程序執(zhí)行各種操作。

? ? ? ? 程序計數(shù)器可以看作是當前線程所執(zhí)行字節(jié)碼指令的行號指示器,主要有兩個作用

1.字節(jié)碼解釋器通過改變程序計數(shù)器,來依次讀取需要執(zhí)行的指令,從而實現(xiàn)代碼的流程控制(順序執(zhí)行、選擇、循環(huán)、異常處理)

2.多線程的情況下,程序計數(shù)器用于記錄當前線程執(zhí)行的位置,從而當線程被切換回來也能知道上次運行的位置。每條線程間的程序計數(shù)器是互不影響的,獨立存儲、也就是"線程私有"。

? ? ? ?程序計數(shù)器是唯一一個不會出現(xiàn)OutofMemoryError的內存區(qū)域,生命周期是從線程的創(chuàng)建至線程的銷毀。

(4)JAVA虛擬機棧

? ? ? ?我們在方法中經(jīng)常會定義一些局部變量,就會保存在JAVA的虛擬機棧中。JAVA虛擬機棧是由多個幀棧組成,如果線程執(zhí)行了一個方法,那么就會對這個方法調用,創(chuàng)建一個對應的幀棧都包含(局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息)。每一次函數(shù)調用就會有一個對應的幀棧被壓入JAVA棧,每個函數(shù)調用結束后就會有一個幀棧被彈出,棧的順序是先進后出

? ? ? ?JAVA虛擬機棧也是私有的,生命周期也是從線程的創(chuàng)建至線程的銷毀。虛擬機棧會出現(xiàn)兩種錯誤:

1.StackOverFlowError: 如果虛擬棧的內存大小不允許動態(tài)擴展,那么當線程請求棧的深度大于java虛擬機棧的最大深度時,會拋出該異常

2.OutofMemoryError: 虛擬機棧的內存大小允許動態(tài)擴展,如果虛擬機在動態(tài)擴展棧時,無法申請到足夠的內存空間,會拋出該異常

(5)本地方法棧

? ? ? ? 與JAVA虛擬機棧相似,區(qū)別:虛擬棧執(zhí)行JAVA的方法,而本地方法棧是為虛擬機使用到的Native方法服務(在HotSpot虛擬機中與JAVA虛擬機棧合二為一)。棧的順序是先進后出,也會產生兩種異常

(6)堆

? ? ? ? JAVA的堆內存是用來存放我們代碼中創(chuàng)建的各種對象,是所有線程共享的一塊區(qū)域。幾乎所有的對象實例以及數(shù)組都是在這里分配內存,但是隨著JIT動態(tài)編譯技術的發(fā)展與逃逸分析技術的成熟,從JDK1.7開始已經(jīng)默認開啟了逃逸分析,如果某些方法的對象引用沒有被返回或者是未被使用(也就是未逃逸出去),那么對象可以直接在棧上分配內存。堆的順序是先進先出

? ? ? ?JAVA堆是垃圾回收的主要區(qū)域,因此也叫GC堆。是最容易產生?OutOfMemoryError 異常,例如:

1.OutOfMemoryError: GC Overhead Limit Exceeded: 當JVM花費很長的時間執(zhí)行垃圾回收,并且只能回收到很小的堆空間時,就會引發(fā)此異常

2.java.lang.OutOfMemoryError: Java heap space: 如果在新建對象時,堆內存空間存放不下,就會引發(fā)此異常(和配置的內存大小有關,與物理內存無關)

? ? ? ? 棧與堆都是JAVA用來存儲數(shù)據(jù)的地方。棧的優(yōu)勢為存儲比堆快,僅次于CPU中的寄存器,缺點是存在棧中的數(shù)據(jù)大小與生命期是確定的,缺乏靈活性。堆的優(yōu)勢可以動態(tài)的分配內存大小,生命期也是非固定的,缺點是需要在運行是進行動態(tài)分配,存取速度較慢

(7)直接內存

? ? ? ?直接內存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是虛擬機的內存區(qū)域,但是這部分內存也被頻繁使用到,也會導致OutOfMemoryError異常。本地內存不受java堆堆限制,但是會受到本機總內存大小及處理器尋址空間限制

? ? ? ? ?在JDK1.4中加入的NIO(New Input/OutPut)類,引入了一種基于渠道(channel)與緩沖區(qū)(Buffer)的I/O方式,它可以直接使用Native函數(shù)庫直接分配堆外內存,然后通過一個存儲在JAVA堆中的DirectByteBuffer對象作為這塊的內存引用操作,這樣避免了在Java堆與Native堆來回的復制數(shù)據(jù),從而提高了性能

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容