文章太長了,分2篇寫吧,上一篇:再看 JVM(1)java
想必你們對堆內存都是耳熟能詳了,本身都去仔細研究過了,可是這裏仍是要重點說明,儘可能力爭全面,有些細節點的點仍是有不少人不知道的 φ(≧ω≦*)♪算法
幾乎
全部的對象實例都在堆空間分配內存這個不少人不知道哦 ヽ(✿゚▽゚)ノ數組
由於堆是進程內線程間共享的,那麼天然併發爭強數據是少不了的,爲了減小線程之間因並帶來的性能損失,推出了 TLAB
這麼個技術併發
TLAB:線程私有緩衝區,堆裏面分出一小部分空間來,再分紅好幾塊,每一個線程佔一塊,那就是每一個線程獨有一下塊,每個小塊稱爲 TLAB 併發性更好,沒有競爭 app
大部分現代垃圾收集器都是基於分帶收集理論設計,而決定堆空間的又是GC,因此 JVM 採用哪一種類型的垃圾收集器,堆空間的結構就趨近哪一種構型設計工具
咱們以 JDK1.8 Hotspot 虛擬機爲準,目前開發絕大部分都是 JDK1.8 的,由於以後就收費了 o( ̄ヘ ̄o#)post
對象分2類:性能
堆內存和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
顯示指定纔有效
Xms
- 堆內存初始值,默認=物理內存的 1/64Xmx
- 堆內存最大值,默認=物理內存的 1/4Xms500m
- 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/10Xmn
- 新生代最大值,通常不動,通常都用比例,這個寫了比例就不算數了-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:
S0S1C/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
複製代碼
逃逸分析:棧上分配,標量替換
Full GC中,元數據指向元數據的那些指針都不用再掃描了。不少複雜的元數據掃描的代碼(尤爲是CMS裏面的那些)都刪除了。 元空間只有少許的指針指向Java堆。這包括:類的元數據中指向java/lang/Class實例的指針;數組類的元數據中,指向java/lang/Class集合的指針。 沒有元數據壓縮的開銷 減小了根對象的掃描(再也不掃描虛擬機裏面的已加載類的字典以及其它的內部哈希表) 減小了Full GC的時間 G1回收器中,併發標記階段完成後能夠進行類的卸載
遇到 OOM 呢, 第一時間看內存分佈,用工具 dump 出一張內存快照出來,工具備不少
接口的匿名實現類其實是被做爲一種類型來使用的,在每個匿名實現類在方法區都會佔據一塊 class 空間
StringTable
誰有error,誰有gc