再看 JVM(2)

文章太長了,分2篇寫吧,上一篇:再看 JVM(1)java

堆內存

想必你們對堆內存都是耳熟能詳了,本身都去仔細研究過了,可是這裏仍是要重點說明,儘可能力爭全面,有些細節點的點仍是有不少人不知道的 φ(≧ω≦*)♪算法

堆內存特色

  • 幾乎全部的對象實例都在堆空間分配內存
  • 方法結束後,堆中的對象不會立刻被移除,僅僅在垃圾回收時纔會移除
  • 棧上保存對象引用,對象自己仍是儲存在堆內存中的

TLAB 線程私有緩衝區

這個不少人不知道哦 ヽ(✿゚▽゚)ノ數組

由於堆是進程內線程間共享的,那麼天然併發爭強數據是少不了的,爲了減小線程之間因並帶來的性能損失,推出了 TLAB 這麼個技術併發

TLAB:線程私有緩衝區,堆裏面分出一小部分空間來,再分紅好幾塊,每一個線程佔一塊,那就是每一個線程獨有一下塊,每個小塊稱爲 TLAB 併發性更好,沒有競爭 app

堆內存結構

大部分現代垃圾收集器都是基於分帶收集理論設計,而決定堆空間的又是GC,因此 JVM 採用哪一種類型的垃圾收集器,堆空間的結構就趨近哪一種構型設計工具

咱們以 JDK1.8 Hotspot 虛擬機爲準,目前開發絕大部分都是 JDK1.8 的,由於以後就收費了 o( ̄ヘ ̄o#)post

對象分2類:性能

  • 生命週期很是端的瞬時對象,這類對象的建立和銷燬都很是快,好比方法裏面建立的對象
  • 生命週期很是長,某些極端狀況下和JVM生命週期同樣長,好比okhttp的單例,application的單例,最典型的就是一些連接的操做對象了,妥妥的超長生命週期 o(一︿一+)o

堆內存和GC算法都是圍繞這2種生命週期的對象來的,堆內存分紅:新生代和老年代2個部分,新生代保存那些生命週期短的對象,尤爲是通過IBM的研究,80%的對象生命週期都只有很是短,像棧幀裏面開闢的對象都是這種類型的。老年代保存那些生命週期及其漫長的對象,新生代的對象要是通過足夠GC次數後會升級到老年代裏優化

至於 Eden、S0、S1 懶的說了,你們本身去查把,估計你們都知道 o( ̄ε ̄*)spa

Eden、S0、S1 默認的比例是 8:1:1,但打印出來其實是 6:1:1,原理是默認開啓了 自適應內存分配策略,固然你就是把它關了也仍是 6:1:1,除非你經過 -XX:SurvivorRatio=8 顯示指定纔有效

堆內存 JVM 配置、命令

  • Xms - 堆內存初始值,默認=物理內存的 1/64
  • Xmx - 堆內存最大值,默認=物理內存的 1/4
  • Xms500m - VM options 這麼寫
  • -XX:NewRatio=2 - 新生代老年代比例,2的意思是新生代是1佔總數的1/3,老年代代是2佔總數的2/3,通常咱們不改這個參數,由於新生代小了,意味這GC回收頻率就要高了
  • -XX:SurvivorRatio=8 - Eden、S0、S1 的比例,8的意思 Eden 是8佔總數的8/10,S0是1佔總數的1/10,S1是1佔總數的1/10
  • Xmn - 新生代最大值,通常不動,通常都用比例,這個寫了比例就不算數了
  • -XX:+UseAdaptiveSizePolicy - 自適應內存分配策略,-號是取消設置,+號是採用設置,這個其實不起做用的...
  • jinfo -flag NewRatio 進程ID - 打印新生代老年代比例
  • jinfo -flag SurvivorRatio 進程ID - 打印新生代內比例

經過 Runtime 對象能夠獲取這2個參數

long Xms = Runtime.getRuntime().totalMemory();
long Xmx = Runtime.getRuntime().maxMemory();
複製代碼

通常狀況下咱們把 Xms、Xmx 設置成相等的,爲的是減小系統壓力。他倆要是不等的話,堆內存在需求增加的狀況下會不停的去申請內存,新申請的內存和原來內存是不連續的,內存碎片化會下降內存讀寫性能。內存需求減小的狀況下,系統會回收堆內存不用的空間,這樣頻繁的來來回回申請、回收內存會極大的系統壓力,更況且GC自己就很耗費性能還會阻塞用戶進程,GC以後咱們再來這麼一下系統性能壓力就更大了 (๑•̀ㅂ•́)و✧

打印堆內存有2個方式:

  • jsts -gc 進程ID: 這是命令行的,隨時都能能用
  • -XX:+PrintGCDetails: 這是配置到 VM options 裏面的,只有進程結束時才能代印出數據,前面章節介紹過了

看下命令行打印出來的數據,認識下參數:

➜  ~ jstat -gc 28763
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
25600.0 25600.0  0.0    0.0   153600.0 61443.3   409600.0     0.0     4480.0 774.4  384.0   75.9       0    0.000   0      0.000    0.000
複製代碼
  • C結尾的是總數,U結尾的是使用量
  • EC/EU: 新生代
  • OC/OU: 老年代
  • S0C/S0U: S0
  • S1C/S1U: S1

不過這裏有個點要知道,咱們用代碼把堆內存打印出來,參數咱們設置的是:-Xms600m -Xmx600m

public static void main(String[] args) {

    long Xms = Runtime.getRuntime().totalMemory() / 1024 / 1024;
    long Xmx = Runtime.getRuntime().maxMemory() / 1024 / 1024;

    System.out.println("Xms:" + Xms);
    System.out.println("Xmx:" + Xmx);

}
複製代碼

實際打印出來的是 575,爲啥???

Xms:575
Xmx:575
複製代碼

由於這裏少了一個 s1 的大小,堆內存中真正能存數據的就是 Ednt+s0或者s1其中的一個,s0、s1 是爲了相互賦值的,一個時刻只有一個用來存儲對象數據,另外一個留着準備給GC複製對象用

jvisualvm 截圖看下,S0、S1 的大小是25M

實際上 -XX:+PrintGCDetails 打印出來的新生代的 total 也是575,也是不算 S1 的

Xms:575
Xmx:575
Heap
 PSYoungGen      total 179200K, used 12288K [0x00000007b3800000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 153600K, 8% used [0x00000007b3800000,0x00000007b44001b8,0x00000007bce00000)
  from space 25600K, 0% used [0x00000007be700000,0x00000007be700000,0x00000007c0000000)
  to   space 25600K, 0% used [0x00000007bce00000,0x00000007bce00000,0x00000007be700000)
 ParOldGen       total 409600K, used 0K [0x000000079a800000, 0x00000007b3800000, 0x00000007b3800000)
  object space 409600K, 0% used [0x000000079a800000,0x000000079a800000,0x00000007b3800000)
 Metaspace       used 3387K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K
複製代碼

逃逸分析:棧上分配,標量替換

OOM

Full GC中,元數據指向元數據的那些指針都不用再掃描了。不少複雜的元數據掃描的代碼(尤爲是CMS裏面的那些)都刪除了。 元空間只有少許的指針指向Java堆。這包括:類的元數據中指向java/lang/Class實例的指針;數組類的元數據中,指向java/lang/Class集合的指針。 沒有元數據壓縮的開銷 減小了根對象的掃描(再也不掃描虛擬機裏面的已加載類的字典以及其它的內部哈希表) 減小了Full GC的時間 G1回收器中,併發標記階段完成後能夠進行類的卸載

遇到 OOM 呢, 第一時間看內存分佈,用工具 dump 出一張內存快照出來,工具備不少

  1. 先看是否是有不合理代碼生成了大量對象而且這些對象內存泄露了,佔用了大量內存出去
  2. 再看內存泄露,通常單單內存泄露不會 OOM,可是能夠優化內存使用
  3. 增長屋裏內存
  4. 看看是否是某些大致積對象聲明週期過長,好比 bitmap

接口的匿名實現類其實是被做爲一種類型來使用的,在每個匿名實現類在方法區都會佔據一塊 class 空間

StringTable

誰有error,誰有gc
相關文章
相關標籤/搜索