天天看點

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

【JVM】

  • 記憶體結構
    • 1.程式計數器(PC Registers)
    • 2.本地方法棧(Native Method Stack)
    • 3.方法區(Method Area)
      • 3-1.運作時的常量池(Run-Time Constant Pool)
    • 4.堆(Heap)
      • 4-1.堆記憶體結構(新生代和老年代)
    • 5.JVM棧(JVM Stack)
  • JVM調優
  • 記憶體模型
  • 指針壓縮

記憶體結構

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮
Name 線程私有/共享 設定參數 Function
程式計數器(PC Registers) 私有 固定大小 儲存目前線程執行的方法
本地方法棧(Native Method Stack) 私有 固定大小 JNI調取本地方法
方法區(Method Area) 共享 -XX static,class類結構,常量(池)
堆(Heap) 共享 -Xms:初始堆值;-Xmx:最大堆值 new出來的執行個體對象和數組
JVM棧(JVM Stack) 私有 -Xss (每個線程)方法的棧幀、局部變量表、操作數棧

1.程式計數器(PC Registers)

  • 一塊小記憶體,每個線程都有
  • PC存儲目前方法:
    • 線程正在執行的方法稱為該線程的目前方法
    • 目前方法為本地(native)方法時,pc值未定義(undefined)
    • 目前方法為非本地方法時,pc包含了目前正在執行指令的位址
  • 目前唯一一塊不會引發OutOfMemoryError異常

2.本地方法棧(Native Method Stack)

  • 存儲native方法的執行資訊(Java調用C程式執行代碼時的資訊),線程私有

    (C函數就叫Native方法)

  • 引發的異常:
    • 棧的深度超過虛拟機規定深度,就會引發StackOverflowError異常
    • 無法擴充記憶體,OutOfMemoryError異常
  • 比如:安卓開發用到的C語言,都會使用Java語言JNI,就是Java語言去調用C語言

3.方法區(Method Area)

  • 存儲JVM已經加載過類的結構

    (類加載器:在我們程式運作的時候,需要加載很多類進來,比如rt.jar裡面那些基礎的類都要加載進來,對于一個JVM來說,它加載的類是有限制的,一個JVM它可能不能負擔很多很多類,這個就被方法區給受限,那麼方法區時存儲JVM已經加載過類的結構)

  • 所有線程共享,所有線程加載過的類都會放在方法區裡面
  • 那麼,運作時的常量池、類資訊、常量、靜态變量等
    • 在Java(JVM)啟動的時候呢,會建立方法區,邏輯上屬于堆記憶體的一部分
    • (當類的位元組碼檔案被加載到記憶體(?這裡指方法區)時,類的執行個體方法不會被配置設定入口位址,在該類建立對象後,類中的執行個體方法才配置設定入口位址(堆)。)
  • 很少做垃圾回收
  • 無法滿足記憶體配置設定要求,OutOfMemoryError異常
  • “-XX”參數設定大小
  • static靜态變量或方法不要定義太多,因為GC垃圾回收機制是不會回收的

(什麼時候會加載幾十萬個類呢?比如說我們通常會将JSP編譯成Servlet,那麼,所有的網頁頁面都會被編譯成一個Java類,如果一個系統裡面,它的網頁數量過多,它用到的Java類就會非常多,那麼這個方法區的大小就會對程式的運作就有影響)

3-1.運作時的常量池(Run-Time Constant Pool)

  • Class檔案中常量池的運作時表示
  • (每個Class裡面都有一個Constant Pool,那麼在運作的時候,這個類被加載進來,Class裡的Constant Pool就會被映射到這塊記憶體區域上)編譯期确定的常量

    屬于方法區的一部分

    動态性

  • Java語言并不要求常量一定隻有在編譯期産生(不一定都是編譯期就能确定的常量)
  • 比如String.intern方法也可以将字元串放入運作時的常量池

    引發的異常:

    無法滿足記憶體配置設定要求,OutOfMemoryError異常

4.堆(Heap)

  • 虛拟機啟動時開始建立,
  • 什麼時候會把資料放在堆記憶體上呢?
    • 當我們在程式裡面采用new操作,或者定義一個數組的時候,那我們都是在堆上配置設定記憶體的
    • 對象執行個體和數組都是堆上配置設定記憶體的
  • 占了太大比例,是以,這一塊也是垃圾回收的主要區域:沒有用的垃圾對象進行回收
  • 設定大小

    “-Xms”初始堆值,“-Xmx”最大堆值

  • 引發的異常

    無法滿足記憶體配置設定要求,OutOfMemoryError異常:

    1:程式消耗的性能太多,記憶體不夠用

    2:記憶體洩漏,有些對象沒有及時地被垃圾回收

    那麼需要對程式進行細微的檢查

  • 因為線程共享,這裡定義的成員變量就是共享資料,存線上程安全的問題

4-1.堆記憶體結構(新生代和老年代)

5.JVM棧(JVM Stack)

  • (這個變量是放在棧裡面的,這個變量是放在堆裡面的,實際上就是指JVM棧包含的)
  • 每個線程有自己獨立的一個Java虛拟機棧:有10個線程就有10個JVM棧
  • “-Xss”設定每個線程堆棧大小
  • Java方法的執行基于棧

    每個方法從調用到完成對應一個棧幀在棧中入棧、出棧的過程(壓棧)

  • 棧幀存儲局部變量表、操作數棧等

    局部變量表存放方法中存放在“棧”裡面的東西,也就是局部變量之類的東西

  • (定義一個小int,一個byte這一類的,我們都是在JVM棧上面來定義)
  • 局部變量

    (在Java位元組碼那一章裡面可以知道棧幀,我們在每一個棧幀裡面,都存儲着局部變量表、操作數棧等等,你把一個class檔案進行反編譯以後,你就會看到裡面的Code區域都有很多指令,那麼每個指令,實際上,都是從局部變量表裡面,把資料搬到操作數棧上面處理,處理完都又把它放回局部變量表裡面去)

  • 在多線程情況下,共享同一個局部變量,不會發生線程安全。因為棧就是每個線程都有自己的局部變量表
  • 引發的異常:

    棧的深度超過虛拟機規定深度,就會引發StackOverflowError異常

    (使用過多的變量)無法擴充記憶體,OutOfMemoryError異常

JVM調優

  • 一般:初始堆記憶體(-Xms)和最大堆記憶體(-Xmx)都是一緻
  • 初始堆記憶體設定比較小的話,垃圾回收機制會比較頻繁去回收,這樣會耗程式。
  • -XMS和-XMX設定成一緻,這樣做的好處是可以減少程式運作時垃圾回收次數,進而提高效率
  • 最大堆記憶體跟電腦硬體配置:一般接近電腦組態
    • 作業系統配置設定給每個程序的記憶體是有限制的,譬如32位的Windows限制為2GB。

      虛拟機提供了參數來控制Java堆和方法區的這兩部分記憶體的最大值。剩餘的記憶體為2GB(作業系統限制)減 Xmx(最大堆容量),再減MaxPermSize(最大方法區容量),程式計數器消耗記憶體很小,可以忽略掉

  • 設定新生代大小與老年代優化參數:
    • "-Xmn"新生代大小,一般設為整個堆的1/3到1/4左右
    • “-XX:SurvivorRatio”:設定新生代中edenhe from/to空間的比例關系n/1
    • “-XX:NewRatio”:老/新
    • 需求:可能會有很多經常使用的對象:這時,老年代越大越好,新生代就越小,盡量減少老年代的GC
    • 需求:有些對象不怎麼經常使用,但是使用新對象特别多。這時新生代要大點好

記憶體模型

指針壓縮

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

線性位址不開是8位元組,開是4位元組

線性位址8位元組=64bit=48在用+16保留

2^48=256T

對象記憶體結構

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

空對象:

未開啟指針壓縮 16B

-XX:+/-UseCompressedOops

開啟指針壓縮:

16B=8B(Mark Word)+4b(KClass pointer)+0(執行個體資料)+4B(對齊填充)

普通對象:

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

對象頭裡面才有填充

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

記憶體位址

16=10

24從48算起=30

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

oop多少位 32+3=35

2^35

擴容補0

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

jol

【JVM】記憶體結構和JVM調優記憶體結構JVM調優記憶體模型指針壓縮

虛拟機棧溢出

  • 調用鍊過長
  • 死循環
  • 無限遞歸
  • java -XX:+PrintFlagsFinal -version

    grep ThreadStack

-Xss160k