Java虛擬機 -- JVM內存區域模塊

JVM內存模型  --  JDK8

線程私有

  • 程序計數器
    • 當前線程所執行的字節碼行號指示器
    • 通過改變計數器的值來選取下一條需要執行的字節碼指示器(比如:循環,跳轉,異常處理都需要依賴該計數器來完成)
    • 和線程是一對一的關係即“線程私有”
    • 不會發生內存泄露問題,程序計數器是邏輯計數器而非物理計數器
  • 虛擬機棧
    • 生命週期與線程相同
    • 每個方法在執行時都會創建一個棧幀,用於存儲局部變量表,操作數棧,動態鏈接,方法出口等信息。
      • 局部變量表:存放編譯期可知的各種基本數據類型,對象引用
      • 操作數棧:入棧,出棧,複製,交換的一些信息
    • 深遞歸會導致虛擬機創建棧幀過多而導致StackOverflowError
    • 當虛擬機可以動態擴展時,如果無法申請足夠多的內存就會拋出OOM異常
  • 本地方法棧
    • 與虛擬機棧相似,主要標記爲native的方法

線程共享

  • Java堆(GC堆)
    • 對象實例與數組的分配區域
    • GC所管理的區域
    • Java堆可以處於物理上不連續的內存空間,只要邏輯上連續即可。由於當前主流的虛擬機都是可擴展的(通過 -Xmx和 -Xms控制),如果堆中沒有內存完成實例分配,並且堆也無法再擴展時,就會拋出OOM異常。
  • 方法區
    • 用於存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。在JDK8以前的HotSpot虛擬機中,方法區也被稱爲“永久代”(JDK8已經被元空間取代)
    • 永久代並不意味着數據進入方法區就永久存在,此區域的內存回收主要是針對常量池的回收以及對類型的卸載。JVM規範規定:當方法區無法滿足內存分配需求時,將拋出OOM異常。
      • 元空間與永久代的區別
        • 元空間使用的是本地內存,而永久代使用JVM內存
        • 字符串常量池存在永久代中,容易出現內存溢出與性能問題
        • 永久代會爲GC帶來不必要的複雜性
  • 運行常量池(方法區的一部分)

字面量:字符串(JDK1.7後移動到堆中) 、final常量 、基本數據類型的值

String str = "str";
int i = 1;
這裏的"str"與1就是字面量

符號引用 :符號引用就是某個變量,在編譯的時候,無法確定其內存地址

String str = "Hello World";
System.err.println(str);
這裏的str就相當於符號引用

額爲擴展:在上一篇文章中類加載的解析階段,將符號引用轉化爲直接引用,就可以理解成將一個漂浮在字符串常量池中的符號引用用一根線將它固定起來,通過這根線可以直接定位到該目標,但是在這個階段給目標上並沒有實際意義上的值,而單純是一個字面量,只有在初始化階段纔會真正去初始化類變量等信息(個人理解)。

 

JVM三大性能調優參數

  • - Xss:規定了每個線程虛擬機棧的大小
    • 一般情況下256k就足夠,此配置將影響此進程中併發線程數的大小
  • - Xms:堆的初始大小
  • - Xmx:堆擴容後所能達到的最大值
    • 通常情況下將 - Xms與 - Xmx設置爲一樣,因爲當heap不夠用時發生擴容,會發生內存抖動,影響程序運行時的穩定性
      • 內存抖動是指內存頻繁地分配和回收,而頻繁的gc會導致卡頓,嚴重時和內存泄漏一樣會導致OOM。

 

Java內存模型中的堆與棧

內存分配策略:

  • 靜態存儲:編譯時確定每個數據目標在在運行時的存儲空間需求
  • 棧式存儲:數據區需求在編譯時未知,運行時模塊入口前確定
  • 堆式存儲:編譯時或運行時模塊入口都無法確定,動態分配

堆與棧的聯繫:棧中保存的是堆內存(引用對象,數組)的首地址。

 

Java內存模型中堆與棧的區別

  • 管理方式:棧是自動釋放,堆需要GC回收
  • 分配方式:棧支持靜態和動態分配,而堆僅支持動態分配
  • 效率方面:由於棧的操作只有入棧,出棧等基本操作,而堆由於是動態分配所以會使用更加複雜的存儲結構,在帶來靈活性的同時,效率降低很多
  • 碎片相關:棧產產色的碎片遠小於堆
  • 空間大小:棧比堆小

 

不同版本之間的intern()方法的區別

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章