【Java】JVM入門解析(三)

在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

(圖片來源於網絡,侵刪)


概述

  • 1)一個JVM實例只存在一個堆內存,堆也是Java內存管理的核心區域
  • 2)Java堆區在JVM啓動的時候即被創建,其空間大小也就確定了,是JVM管理的最大一塊內存空間(堆內存大小是可以調節的)
  • 3)《Java虛擬機規範》規定,堆可以處於物理上不連續的內存空間中,但在邏輯上它應該被視爲連續的
  • 4)所有的線程共享Java堆,在這裏還可以劃分線程私有的緩衝區(Thread Local Allocation Buffer,TLAB)
  • 5)數組和對象可能永遠不會存儲在堆上,因爲棧幀中保存引用,這個引用指向對象或數組在堆中的位置
  • 6)在方法結束後,堆中的對象不會馬上被移除,僅僅在垃圾收集的時候纔會被移除
  • 7)堆,是GC執行垃圾回收的重點區域

架構圖

在這裏插入圖片描述

堆內存細分

Java7之前堆內存邏輯上分爲

  • 新生代
  • 老年代
  • 永久代

Java8之後堆內存邏輯上分爲

  • 新生代
  • 老年代
  • 元空間

注意:雖然堆內存中邏輯上分爲新生代、老年代和元空間,但是物理上的堆只包括新生代、老年代,而元空間作爲方法區的具體實現

堆空間大小

Java堆區用於存儲Java對象實例,那麼堆的大小在JVM啓動時就已經定義好了,可以通過“-Xms”和“-Xmx”來進行設置

  • “-Xms“ 用於表示堆區的起始內存,等價於 -XX:InitialHeapSize
  • “-Xmx“ 用於表示堆區的最大內存,等價於 -XX:MaxHeapSize

一旦堆區中的內存大小超過 “-Xmx“所指定的最大內存時,將會拋出OutOfMemoryError異常

通常會將 -Xms 和 -Xmx 兩個參數配置相同的值,其目的是爲了能夠在Java垃圾回收機制清理完堆區後不需要重新分隔計算堆區的大小,從而提升性能

默認情況下,
初始化內存大小:物理電腦內存 / 64
最大的內存大小:物理電腦內存 / 4

新生代和老年代

Java堆區可細分爲:新生代(YoungGen)和 老年代(OldGen)
其中新生代又可以劃分爲Eden區(伊甸園區)、Survivor0區(倖存者0區)和Survivo1區(倖存者1區)

在這裏插入圖片描述
下面的參數一般不會調整,使用默認即可:
在這裏插入圖片描述
配置新生代和老年代在堆結構的佔比:

  • 默認 -XX:NewRatio=2,表示新生代佔1,老年代佔2,即新生代佔整個堆的 1/3

配置新生代中的Eden和兩外兩個Survivor空間佔比:

  • 默認比例是 8:1:1 可通過 -XX:SurvivoRatio 參數進行調整

幾乎所有的Java對象都是在Eden區被new出來的

絕大部分的Java對象的銷燬都在新生代進行了,即”朝生夕死“

可通過 -Xmn 設置新生代最大內存大小,如果使用了該參數,-XX:NewRatio 參數就失效了,一般使用默認值即可

對象分配過程

概述

爲新對象分配內存時一個非常嚴謹和複雜的任務,所以過程很複雜,下面概述一下

  • 1)new出來的對象先放在Eden區,此區有大小限制
  • 2)當Eden區空間填滿時,而程序又需要創建對象,此時會阻塞用戶線程,JVM垃圾回收器將對Eden區進行垃圾回收(Minor GC),將Eden區中的不再被其他對象所引用的對象進行銷燬,再加載新的對象放到Eden區
  • 3)然後將Eden區中的剩餘對象移動到Survivor0區
  • 4)如果再次觸發垃圾回收,對Eden區和from區(因爲Survivor0和1區只能有一個區域能存放數據,所以將存放數據的稱之爲 from 區,不存放數據的稱之爲 to 區)進行垃圾回收,然後將Eden和from區中倖存下來的放到 to 區
  • 5)後續不斷觸發垃圾回收,倖存的對象不斷從 from 移動到 to 區,持續整個過程
  • 6)對象在 from 和 to 區不斷的移動之後,當次數達到15次時,就會從新生代移動到老年代了,這個次數可以通過 -XX:MaxTenuringThreshold 來進行設置

注意:有一些對象並不會需要經歷15次移動纔會移動到老年代,而是有可能創建對象時直接存儲到了老年代,這樣的原因比如有:該對象過大,新生代存儲不了,直接就放在了老年代了

總結
  • 關於垃圾回收:頻繁的發生在新生代回收,很少在老年代回收,幾乎不在永久代/元空間回收
通過下圖更好的理解

在這裏插入圖片描述

GC概述

Minor GC、Major GC 和 Full GC

JVM在GC時,並非每次都對上面三個區域進行垃圾回收,大部分時候的回收的都是進行在新生代

針對HotSpot VM的實現,它裏面的GC按照回收區域又分爲兩大類型:

  • 部分收集:不是對整個Java堆的垃圾進行回收
    • 新生代收集(Minor GC / Young GC):只是新生代的垃圾回收
    • 老年代收集(Major GC / Old GC):只是老年代的垃圾回收
    • 混合收集(Mixed GC):收集整個新生代以及部分老年代的垃圾回收
  • 整堆收集(Full GC):針對整個Java堆和方法區進行垃圾回收

Minor GC觸發時機:

  • 1)當Eden空間不足時,就會觸發Minor GC;Survivor空間不足不會觸發GC
  • 2)Minor GC是非常頻繁的
  • 3)會引發STW,暫停其他用戶的線程,等垃圾回收結束,用戶線程纔會恢復運行

Major GC觸發時機:

  • 1)老年代空間不足時,會觸發Major GC
  • 2)Major GC速度比Minor GC慢10倍以上,STW的時間更長,所以儘量避免出現Major GC
  • 3)如果Major GC後內存還不足,就會出現OOM

Full GC觸發時機:

  • 1)調用System.gc(),系統建議執行Full GC,但是不是必定執行
  • 2)老年代空間不足
  • 3)方法區空間不足
  • 4)通過Minor GC後進入老年代的平均大小大於老年代的可用內存
  • 5)由Eden區、from區向to區複製時,對象大小大於to區可用內存,則把該對象轉存到老年代,且老年代的可用內存小於該對象大小

說明:Full GC一定要儘量避免觸發!!!
在這裏插入圖片描述

分代思想

思考:爲什麼要把Java堆進行分代?有什麼好處?

分代是爲了優化GC性能!
如果沒有分代,那麼所有對象都在一個區域裏面,每次GC時,都會掃描區域的所有對象,然後進行GC,這樣是非常消費性能和時間的。而且經過前人大量的實踐表明:70% - 99% 的對象都是臨時對象,所以將Java堆劃分爲兩個區域,一個專門用來存儲那些朝生夕死的對象,一個用來存儲非常持久的對象。即新生代和老年代,然後通過不同的GC方式,進行有效的垃圾回收,提高GC速度,提高性能!

內存分配策略

針對不同的年齡段的對象分配原則如下:

  • 1)優先分配Eden
  • 2)大對象直接分配到老年代(儘量避免程序中出現過多的大對象)
  • 3)長期存活的對象分配到老年代
  • 4)動態對象年齡判斷(如果倖存者區中相同年齡的所有對象大小總和大於倖存者空間的一半,則年齡大於或等於該年齡的對象可以直接進入老年代,無需等到MaxTenuringThreshold中要求的年齡)
  • 5)空間分配擔保 (-XX:HandlePromotionFailure)

TLAB

什麼是TLAB?

TLAB即Thread Local Allocation Buffer,Java對Eden區進行劃分,爲每個線程分配了一個私有的緩存區域

TLAB有什麼用?

堆區是線程共享的,任何線程都可以訪問堆區中的共享數據,由於對象實例的創建在JVM中非常頻繁,因此在併發環境下從堆區中劃分內存空間是線程不安全的,爲了避免多個線程操作同一地址,需要使用加鎖等機制,進而影響分配速度。所以引入TLAB,使用TLAB可以避免一系列的非線程安全問題,同時還能夠提升內存分配的吞吐量,因此我們可以將這種內存分配方式稱之爲快速分配策略

可以通過 ”-XX:UseTLAB“ 設置是否開啓TLAB空間
默認情況下,TLAB空間的內存非常小,僅佔有Eden區的 1%,當然我們可以通過 ”-XX:TLABWasteTargetPercent“ 設置TLAB空間所佔用Eden區的百分比大小

方法區

概述

在JDK7以前,習慣上把方法區,稱爲永久代;JDK8開始,使用元空間取代了永久代,而且元空間使用的是本地內存
在這裏插入圖片描述

棧、堆、方法區的交互關係

在這裏插入圖片描述

方法區存儲什麼?

在這裏插入圖片描述

存儲已被虛擬機加載的類型信息、常量、靜態變量、即時編譯器編譯後的代碼緩存等
在這裏插入圖片描述

方法區的內部結構

類型信息

對每個加載的類型(類、接口、枚舉、註解),JVM必須在方法區中存儲以下類型信息:

  • 1)這個類型的全限定類名
  • 2)這個類型直接父類的完整有效名
  • 3)這個類型的修飾符
  • 4)這個類型直接接口的一個序列列表
域(Field)信息

JVM必須在方法區中保存類型的所有域的相關信息以及域的聲明順序
域的相關信息包括:

  • 1)域名稱
  • 2)域類型
  • 3)域修飾符
方法信息

JVM必須保存所有方法的以下信息,同域信息一樣包括聲明順序:

  • 1)方法名稱
  • 2)方法的返回類型
  • 3)方法參數的數量和類型
  • 4)方法的修飾符
  • 5)方法的字節碼
  • 6)異常表

都看到這裏了,點贊評論一下吧!!!

在這裏插入圖片描述

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