【JVM】第1篇:JVM內存模型

JVM內存區域

圖片描述

(1)程序計數器
看作當前線程所執行的字節碼行號顯示器;任意時刻,一個CPU都會執行一條線程中的指令,爲了線程切換後能回到正確位置,每一個線程都須要一個獨立的線程計數器;執行native方法時,計數器值爲空;此區域沒有任何OutOfMemoryError的區域;html

(2)虛擬機棧
虛擬機棧的生命週期和線程同步,虛擬機棧中的局部變量表用於存儲各類基本數據類型、對象引用類型;long和double會佔用兩個局部變量表的空間,局部變量表在編譯器完成空間分配;
StackOverflowError:線程請求過多
OutOfMemoryError:若是虛擬機棧擴展時沒法申請到足夠內存;java

(3)本地方法棧
與虛擬機棧發揮的做用很是類似,區別是:虛擬機棧爲Java方法服務的;本地方法棧是爲native方法服務的;算法

(4)Java堆
存放實例對象的以及數組的;
OutOfMemoryError異常;數組

堆內存劃分:
圖片描述tomcat

Young Generation:新生代
全部新生成的對象首先都是放在年輕代的。年輕代的目標就是儘量快速的收集掉那些生命週期短的對象。年輕代分三個區:一個Eden ['i:dən]區,兩個 Survivor[sə'vaɪvə]區(通常而言)。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被複制到Survivor[sə'vaɪvə]區(兩個中的一個),當這個 Survivor區滿時,此區的存活對象將被複制到另一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複製過來的而且此時還存活的對象,將被複制年老區。多線程

Old Generation:老年代
在年輕代中經歷了N次垃圾回收後仍然存活的對象,就會被放到年老代中。所以,能夠認爲年老代中存放的都是一些生命週期較長的對象。併發

Perm Area:永久帶
用於存放靜態文件,現在Java類、方法等。jvm

(5)方法區
虛擬機加載的類信息,常量,靜態變量等;
運行時常量池也是方法區的一部分:存放編譯器生成的各類字面量和符號引用;
OutOfMemoryError異常;佈局

Java對象建立以及內存佈局

(1)虛擬機執行new指令 ----> 檢查指令參數是否能在常量池中定位到類的符號引用 ----> 若是沒有檢查到引用表明的類,則先執行類的加載過程 ----> 類加載經過後,爲新生對象分配內存 ----> 分配到的內存空間初始化爲零 ----> 虛擬機對對象進行設置 ----> 執行init方法,完成對象建立
(2)對象在內存中存儲佈局可分爲3塊區域:
對象頭:一部分存儲對象自身運行時數據,如hashCode,線程持有的鎖等;一部分是類型指針,肯定對象是哪一個類的實例;
實例數據:就是程序代碼中定義的各類類型字段內容;
對齊填充:沒有特殊含義,僅僅起到佔位符的做用;ui

垃圾回收器和內存分配策略

如何判斷對象已經死亡,既內存須要回收?

  • 引用計數器法:給對象添加一個引用計數器,每當一個地方引用它時,計數器值加一;引用失效,計數器值一,值爲零時對象就再也不使用了;缺點時沒法解決對象之間的循環引用;
  • 可達性分析算法:當一個對象到 GC root 沒有任何引用鏈時證實對象不可用了;

圖片描述
Java語言中可做爲GC roots的對象有:
虛擬機棧引用的對象,方法區中類的靜態屬性、常量引用的對象,本地方法區中Native方法引用的對象;

垃圾收集算法

(1)標記-清除算法:
首先標記處全部須要回收的對象,標記完成後統一回收;執行下圖所示:
圖片描述
主要有兩個缺點:一是執行效率不高,另外一個是會產生大量不連續的內存碎片,致使再次分配較大對象時,沒法獲得連續的內存空間而再一次觸發垃圾回收機制;

(2)複製算法:
爲了解決標記-清除算法的效率問題,可使用複製算法;它將內存劃分爲大小相等的兩塊,每次使用其中一塊,當一塊內存使用完了,就將存活的對象複製到另外一塊,而後回收已使用過的內存;
圖片描述
該算法在對象存活較多時,效率底下,同時內存空間浪費;適合於新生代內存;

(3)標記-整理算法:
標記全部可回收對象,它不是直接對可回收對象進行清理;而是讓全部的存活對象都向一端移動,而後直接清理掉端邊界之外的內存;
圖片描述

(4)分代收集算法:
目前商業虛擬機都採用分代收集算法。

Java中垃圾回收器類型

垃圾回收器種類

  • Serial ['sɪərɪəl]收集器
    新生代收集器,使用複製算法,使用一個線程進行GC,串行,其它用戶工做線程暫停。該收集器簡單高效。
  • ParNew [pɑː][njuː]收集器
    新生代收集器,使用複製算法,Serial收集器的多線程版,用多個線程進行GC,並行,其它工做線程暫停。使用-XX:+UseParNewGC開關來控制使用ParNew+Serial Old收集器組合收集內存;使用-XX:ParallelGCThreads來設置執行內存回收的線程數。
  • Parallel Scavenge 收集器
    吞吐量優先的垃圾回收器,做用在新生代,使用複製算法,關注CPU吞吐量,即運行用戶代碼的時間/總時間。使用-XX:+UseParallelGC開關控制使用Parallel Scavenge+Serial Old收集器組合回收垃圾。
  • Serial Old收集器
    老年代收集器,單線程收集器,串行,使用標記整理算法,使用單線程進行GC,其它工做線程暫停。
  • Parallel Old ['pærəlel]收集器
    吞吐量優先的垃圾回收器,做用在老年代,多線程,並行,多線程機制與Parallel Scavenge差不錯,使用標記整理算法,在Parallel Old執行時,仍然須要暫停其它線程。
  • CMS(Concurrent Mark Sweep)收集器
    老年代收集器,致力於獲取最短回收停頓時間(即縮短垃圾回收的時間),使用標記清除算法,多線程,優勢是併發收集(用戶線程能夠和GC線程同時工做),停頓小。使用-XX:+UseConcMarkSweepGC進行ParNew+CMS+Serial Old進行內存回收,優先使用ParNew+CMS(緣由見Full GC和併發垃圾回收一節),當用戶線程內存不足時,採用備用方案Serial Old收集。

如何讀懂 GC 日誌

其實每一個垃圾回收器的日誌是不同的,可是爲了方便查看日誌它們也有必定的共性,以下所示的一段 GC 日誌:

33.125: [GC [DefNew: 3324k->152k(3712k),0.0025925 secs] 3324->152k(11904k),0.003168 secs]

100.667: [Full GG [Tenurend: 0k->210k(1024k),0.00149142 secs]4603k->210k(19456k),
[Perm : 2999k->2999k(21248k)],0.0150007 secs] [Times:user=0.01 sys=0.00,real=0.02 secs]
  • 日誌最前面的數字 "33.125:"和"100.667:" 表示GC發生的時間,是從Java虛擬機啓動以來通過的時間
  • "[DefNew"、"[Tenured"、"[Perm"表示GC發生的時間,這裏顯示的區域名稱和使用GC收集器密切相關,上面例子中使用的Serial收集器,它的新生代命名爲Default New Generation,因此顯示"[DefNew"
  • 方括號內部的3324k->152k(3712k)表示"GC前該區域已使用容量->GC後該內存區域已使用容量(該內存區域總容量)"
  • 方括號外的3324->152k(11904k)表示"GC前Java堆已使用容量->GC後Java堆已使用容量(Java堆總容量)"
  • "0.0025925 secs"表示該內存區域GC所用時間,單位秒

和GC有關的JVM參數

作GC調優須要大量的實踐,耐心和對項目的分析。作GC的調優很大程度上依賴於對系統的分析,系統擁有怎樣的對象以及他們的平均生命週期。舉個例子,若是一個應用大可能是短生命週期的對象,那麼應該確保Eden區足夠大,這樣能夠減小Minor GC的次數。能夠經過-XX:NewRatio來控制新生代和老年代的比例,好比-XX:NewRatio=3表明新生代和老年代的比例爲1:3。須要注意的是,擴大新生代的大小會減小老年代的大小,這會致使Major GC執行的更頻繁,而Major GC可能會形成用戶線程的停頓從而下降系統吞吐量。JVM中能夠用NewSize和MaxNewSize參數來指定新生代內存最小和最大值,若是兩個參數值同樣,那麼就至關於固定了新生代的大小。
圖片描述
(1)Minor ['maɪnə] GC:從年輕代空間(包括 Eden 和 Survivor 區域)回收內存被稱爲 Minor GC;
(2)Major['meɪdʒə] GC:是清理老年代;
(3)Full GC:是清理堆空間,包括年輕代和老年代;
參考文章:Minor GC,Major GC,Full Gc

GC參數瞭解

內存相關經常使用參數

(1) -Xms : 堆內存的初始大小,默認是物理內存的1/64
(2) -Xmx : 堆內存的最大值,默認不超過物理內存
(3) -Xmn : 年輕代堆內存大小
(4) -Xss : 棧內存大小設置
(5) -XX:PermSize : 內存永久區的初始大小
(6) -XX:MaxPermSize : 內存永久區的大小
(7) -XX:SurvivorRatio : Eden區與Survivor區的大小比值;設置成8,則兩個Survivor區與一個Eden區的比值是2:8
(8) -XX:+UseAdaptiveSizePolicy : 動態調整 Java 堆內存中各個區域的大小以及即進入老年代的年齡

收集器使用相關參數

(1) -XX:UseParallelGC : 使用 Parallel Scavenge(年輕代並行的多線程收集器) + Serial Old(老年代單線程收集器) 收集器
(2) -XX:UseParNewGC : 使用 ParNew + Serial Old 收集器
(3) -XX:ParallelGCThreads : 並行收集的線程數
(4) -XX:UseParallelOldGC : 使用 Parallel Scavenge(年輕代並行的多線程收集器) + Parallel Old 收集器
(5) -XX:MaxGCPauseMillis : 設置GC的最大停頓時間,僅在使用 Parallel Scavenge 收集器時生效
(6) -XX:+UseConcMarkSweepGC : 使用 ParNew + CMS + Serial Old 組合收集器
(7) -XX:GCTimeRatio : GC時間佔總時間的比例,默認99,即容許 1% 的GC時間。僅在使用 Parallel Scavenge 收集器時生效

調試參數:

(1) -XX:+DisableExplicitGC : 忽略來自程序中System.gc()方法觸發的垃圾回收
(2) -XX:+PrintGCDetails : 打印GC的相信信息
(3) -XX:+PrintHeapAtGC :
(4) -XX:+PrintTenuringDistribution :
(5) -XX:+PrintGCTimeStamps : 打印GC停頓耗時
(6) -XX:+PrintGCDateStamps :
(7) -XX:+HeapDumpOnOutOfMemoryError :
(8) -XX:ErrorFile :
(9) -XX:HeapDumpPath :

-XX:CMSFullGCsBeforeCompaction=0 : cms 收集器每次進入Full GC都進行碎片整理
-XX:+UseCMSCompactAtFullCollection
-XX:CMSInitiatingOccupancyFraction=80 : cms 收集器觸發比例(當空間使用了80%後就觸發回收)
-XX:ReservedCodeCacheSize=128m :
-XX:InitialCodeCacheSize=128m

jd線上機器jvm參數設置:

/export/servers/jdk1.6.0_25/bin/java -server

-Xms128M -Xmx256M -Xss256K
-XX:PermSize=32M
-XX:MaxPermSize=32M
-XX:+UseAdaptiveSizePolicy
-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:GCTimeRatio=39
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps -Xloggc:/export/home/tomcat/logs/loghub.360buy.com/jcollector/gc.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:ErrorFile=/export/home/tomcat/logs/loghub.360buy.com/jcollector/hs_err.log
-XX:HeapDumpPath=/export/home/tomcat/logs/loghub.360buy.com/jcollector/heap_dump.hprof -classpath

mt線上機器jvm參數設置:

JVM_ARGS="-server -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djava.io.tmpdir=/tmp -Djava.net.preferIPv6Addresses=false"
JVM_GC="

-XX:+DisableExplicitGC 
-XX:+PrintGCDetails 
-XX:+PrintHeapAtGC 
-XX:+PrintTenuringDistribution 
-XX:+UseConcMarkSweepGC 
-XX:+PrintGCTimeStamps 
-XX:+PrintGCDateStamps

"
JVM_GC=$JVM_GC"

-XX:CMSFullGCsBeforeCompaction=0 
        -XX:+UseCMSCompactAtFullCollection 
        -XX:CMSInitiatingOccupancyFraction=80
    "

JVM_HEAP="

-XX:SurvivorRatio=8 
    -XX:PermSize=256m 
    -XX:MaxPermSize=256m 
    -XX:+HeapDumpOnOutOfMemoryError 
    -XX:ReservedCodeCacheSize=128m 
    -XX:InitialCodeCacheSize=128m
"

JVM_SIZE="-Xmx4g -Xms4g -Xmn1g"

參考文章
JDK 1.8內存模型
全面理解Java內存模型
年輕代、年老點和持久代

相關文章
相關標籤/搜索