簡述JVM基礎(二):Java內存區域與內存溢出異常

1、概述

咱們在進行Java開發的時候,不多關心Java的內存分配等等,由於這些活都讓JVM給咱們作了。不只自動給咱們分配內存,還有自動的回收無需再佔用的內存空間,以騰出內存供其餘人使用。可是咱們常常面臨的一個問題就是內存泄漏,JVM沒法完成回收工做,致使內存佔用暴漲,最後可能讓程序奔潰。本章主要了解下運行時數據區域分佈狀況以及溢出異常。java

說明: 本系列多處摘抄《深刻理解Java虛擬機》中內容,主要精簡了本書的要點,並敘述本身對本書的理解。本人才疏學淺,文章中有不對的地方,還望批評指教。web

2、運行時數據區域



一、程序計數器

  • 線程私有微信

  • 當前線程所執行的字節碼的行號指示器多線程

  • Java多線程是經過再一個內核中輪流執行實現的,計數器就保證了切換線程的時候能夠回到原來正確的執行位置函數

  • 程序計數器必須每一個線程單獨一個,是線程私有的內存區域佈局

  • 程序計數器是惟一一個JVM沒有規範OutOfMemoryError的區域性能

二、Java虛擬機棧(java方法)

  • 線程私有學習

  • Java方法執行的內存模型,即方法執行時會建立一個棧幀,保存了須要的局部變量表、操做數棧、動態連接、方法出口等信息;spa

  • 線程請求的棧深度>JVM容許的深度時,報StackOverflowError;.net

  • 大多數的JVM能夠動態擴展內存,若是沒法申請到足夠的內存時,報OutOfMemoryError;

三、本地方法棧(native方法)

  • 同Java虛擬機棧

四、Java堆

  • 線程共享

  • 惟一目的:存放對象實例

  • 分類:新生代、老生代,或者Eden空間、From Survior 空間、To Survivor空間

  • 分類目的:更好的回收和分配內存

  • 沒有內存完成實例分配,或者不能再擴展,報OutOfMemoryError異常

  • 能夠本身配置大小(-Xmx和-Xms)

五、方法區

  • 線程共享

  • 目的:存儲類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據;

  • 該區內存回收目標:主要針對常量池的回收和對類型的卸載;

  • 沒法知足內存分配要求時,報OutOfMemoryError異常

六、運行時常量池

  • 注意:運行時常量池屬於方法區

  • 目的:存儲編譯期生成的各類字面量和符號引用

  • 特徵:並不是只有編譯期置入Class文件中的常量池內容才能進入運行時常量池,在運行期間也能夠置入新的常量,好比String的intern()方法;

  • 沒法申請足夠內存時,報OutOfMemoryError異常

3、 直接內存

  • 非運行時數據區域內存

  • Native函數分配堆外內存,堆內的DirectByteBuffer做爲這塊內存的引用

  • 性能顯著提升,避免了Java堆和native之間來回複製數據

4、 對象

一、New對象過程

  • new指令發出

  • 檢查new的參數是否在常量池中存在這個Class的符號引用

  • 檢查對應的Class是否已經初始化

    • 若沒有則先執行初始化過程

  • 分配內存,檢查堆是否規整(垃圾收集器是否帶有壓縮整理功能決定)

    • 規整:指針碰撞方式分配內存

    • 不規整:空閒列表方式分配內存

  • 內存空間初始化爲零值(不包括對象頭)

  • 對對象進行重要的配置

  • 執行 < init > 方法

二、對象的內存佈局

對象頭(Mark Word)

  • 自身運行時數據

    • GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID

    • 類型指針:肯定對象是哪一個Class的實例

實例數據

  • 存儲有效信息,定義的各類字段

  • 相同寬度的字段老是被分配到一塊兒

對齊填充

  • 不必定存在

  • 實例數據沒有對齊,須要填充

三、對象的訪問定位

  • 句柄(reference):

    • 堆中劃分句柄池

    • 句柄地址

      • 到對象實例數據的地址

      • 到對象類型的地址

    • 優點:穩定,對象移動時,(如GC時會移動),這個時候只改變指針地址。句柄信息不變,相對穩定;

  • 指針:

    • 直接存儲了上述的對象地址

    • 優點:速度快


5、OOM

  • 堆溢出:舉例一直new新的實例對象

  • 棧溢出:舉例無限循環調用執行某個方法

  • 方法區和運行時常量池溢出:

    • String.intern():若是常量池已存在,則返回String對象,若是不存在,則先添加到常量池,再返回String對象。

    • 動態定義大量的Class,須要注意內存的回收狀況。


6、小結

程序運行時,須要不停的將數據在內存中分配、計算等。JVM將不一樣類型的數據放在不一樣的位置,這樣分工纔可以讓程序有序的跑起來。咱們所定義的方法,以及new的對象實例都分別存在方法區和堆中,這兩個區域是屬於內存共享的地方,也就是說任何線程取的都是同樣的。可是,由於有線程的存在。因此,咱們須要給線程必要的私有空間。故,在程序運行的時候,咱們經過棧來保存該線程自由的局部變量、引用等,經過程序計數器保存了各個線程的執行位置。這樣,在線程切換的時候,才能找到本身的上一次執行位置,繼續完成未完成的工做。若是,程序執行過程當中沒有足夠的空間分配,就報對應的OOM異常。

小貼士
     

        本文由原做者井方哥獨家受權Open軟件開發小組發佈,著做權歸原做者全部。如需轉載請聯繫原做者申請受權。

       申請加羣交流學習請加主編微信:Jf-1994(井方哥),並備註:姓名-地區-公司-職業-加羣。



Open軟件開發小組



專一Android開發,歡迎關注open_dev





點擊「閱讀原文」


本文分享自微信公衆號 - Open軟件開發小組(open_dev)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索