1. 程式人生 > 實用技巧 >JVM基礎知識和調優基礎原理

JVM基礎知識和調優基礎原理

JVM原理

什麼是jvm

java虛擬機器,就是個應用程式,工作在使用者態

詳解

JVM是按照執行時資料的儲存結構來劃分記憶體結構的,JVM在執行java程式時,將它們劃分成幾種不同格式的資料,分別儲存在不同的區域,這些資料統一稱為執行時資料。執行時資料包括java程式本身的資料資訊和JVM執行java需要的額外資料資訊。

JVM執行時資料區

  • 程式計數器–執行緒私有

    行號,指示程式執行到哪個位置

  • Java虛擬機器棧–執行緒私有
  • 本地方法棧–執行緒私有

    作業系統底層的方法

  • Java堆–執行緒公用

JVM記憶體分配

棧記憶體分配 -xss 預設1M

儲存引數、區域性變數、中間計算過程和其他資料。退出方法的時候,修改棧頂指標就可以把棧幀中的內容銷燬。

  • 棧的優點:存取速度比堆塊,僅次於暫存器,棧資料可以共享。
  • 棧的缺點:存在棧中的資料大小、生存期是在編譯時就確定的,導致其缺乏靈活性。

    stack out of memory

    一般情況下不會溢位,方法不會寫那麼大

    堆記憶體分配:

    儲存物件
  • 堆的優點:動態分配記憶體大小,生存期不必事先告訴編譯器,它是在執行期動態分配的,垃圾回收器會自動收走不再使用的空間區域。
  • 堆的缺點:執行時動態分配記憶體,在分配和銷燬時都要佔用時間,因此堆的效率較低。

    堆結構:

  • Young:E區,S0,S1
  • Old:
  • Permanent:

JVM堆配置引數:

概述

  • -Xms 初始堆大小

    預設實體記憶體的1/64(<1GB)

  • -Xmx最大堆大小

    預設實體記憶體的1/4(<1GB),實際中建議不大於4GB

  • 一般建議設定 -Xms=-Xmx

    好處是避免每次在gc後,調整堆的大小,減少系統記憶體分配開銷

  • 整個堆大小=年輕代大小+年老代大小+持久代大小

新生代:

  • 新生代=1個eden區+2個survivor區
  • -Xmn 年輕代大小(1.4 or later)

    -XX:NewSize,-XX:MaxNewSize(設定年輕代大小,1.4之前)

  • -XX:NewRatio

    年輕代(包括E區和兩個S區)與年老代的比值(除去持久代)
    一般情況下設定了Xms=Xmx並且設定了Xmn的情況下,該引數不需要設定。

  • -XX:ServivorRatio

    1個S區與E區大小的比值,預設設定為8,則1個S區佔整個年輕代的1/10

  • 新生代用來存放JVM剛分配的Java物件

老年代:

  • 老年代=整個堆-年輕代大小-持久代大小
  • 年輕代中經過垃圾回收沒有回收掉的物件被複制到年老代
  • 老年代儲存物件比年輕代年齡大的多,而且不乏大物件(快取)
  • 新建的物件也有可能直接進入老年代
    • 大物件,可通過啟動引數設定-XX:PretnureSizeThreshold=1024(單位為位元組,預設為0)來代表超過多大時就不在新生代分配,而是直接在老年代分配。
    • 大的陣列物件,切陣列中無引用外部物件
  • 老年代大小無配置引數

持久代:

  • 持久代=整個堆-年輕代大小-老年代大小
  • -XX:PermSize -XX:MaxPermSize

    設定持久代的大小,一般情況推薦把-XX:PermSize設定成-XX:MaxPermSize的值為相同的值,因為持久代大小的調整也會導致堆記憶體需要觸發fgc。

  • 存放Class、Method元資訊,其大小與專案的規模、類、方法的數量有關。一般設定為128M就足夠,設定原則是預留30%的空間。
  • 持久代的回收方式
    • 常量池中的常量,無用的類資訊,常量的回收很簡單,沒有引用了就可以被回收
    • 對於無用的類進行回收,必須保證3點:
      • 類的所有例項都已經被回收
      • 載入類的ClassLoader已經被回收
      • 類物件Class物件沒有被引用(即沒有通過反射引用該類的地方)

JVM記憶體垃圾回收:

垃圾收集演算法:

  • 引用計數演算法(瀕臨被拋棄
  • 根搜尋演算法:

    從GC Roots開始向下搜尋,搜尋所走過的路徑稱為引用鏈。當一個物件到GC Roots沒有任何引用鏈相連時,則證明物件是不可用的。即不可達物件。
    在Java語言中,GC Roots包括:

  • 虛擬機器棧中引用的物件。(大部分被回收的)
  • 方法區中靜態屬性實體引用的物件。
  • 方法區中常量引用的物件。
  • 本地方法棧中JNI引用的物件。

垃圾回收演算法:

複製演算法(Copying)

當空間存活的物件比較少時,極為高效,此演算法用於新生代記憶體回收,從E區回收到S0或S1

標記清除演算法(Mark-Sweep)

產生碎片,適合老年代垃圾回收。

標記整理壓縮演算法(Mark-Compac)

稍慢,適合老年代垃圾回收,解決碎片問題,物件連續,成本更高

名詞解釋:

  • 序列回收:gc單執行緒記憶體回收、會暫停所有使用者執行緒,用於client端
  • 並行回收:收集是指多個GC執行緒並行工作,但此時使用者執行緒是暫停的
  • 併發回收:是指使用者執行緒與GC執行緒同時執行(不一定是並行,可能交替,但總體上是同時執行的),不需要停頓使用者執行緒(其實CMS中使用者執行緒還是需要停頓的,只是非常短,GC執行緒在另一個CPU上執行)

JVM常見的垃圾回收器:

Serial回收器(序列回收器)

是一個單執行緒的收集器,只能使用一個CPU或者一條執行緒去完成垃圾收集,在進行垃圾收集時,必須暫停所有其他工作執行緒,直到收集完成

  • -XX:+UseSerialGC來開啟(新生代和老年代都開啟)
  • 使用複製演算法(新生代)標記-壓縮演算法(老年代)
  • 序列的、獨佔式的垃圾回收器
  • 缺點:Stop-The-World

ParNew回收器(並行回收器)

也是獨佔式回收器,在收集過程中,應用程式全部暫停。如果是單CPU上或者併發能力較弱的系統上,還不如序列回收器效能好。

  • -XX:+UseParNewGC開啟
  • -XX:ParallelGCThreads指定執行緒數,預設最好與CPU數量相當

新生代Parallel Scavenge回收器

吞吐量優先回收器

  • 關注CPU吞吐量,即執行使用者程式碼的時間/總時間,適合執行後臺運算
  • -XX:+UserParallelGC開啟,這也是在Server模式下的預設值
  • -XX:GCTimeRatio
  • -XX:MaxGCPauseMillis

老年代ParallelOld回收器

  • -XX:+UseParallelOldGC開啟

CMS(併發標記清除)回收器

用的最廣泛,標記和重新標記兩個階段仍然需要停止使用者執行緒,但時間很快

初始標記
併發標記
重新標記
併發清除

  • 標記-清除演算法:同時它又是一個使用多執行緒併發回收的垃圾收集器
  • -XX:ParallelCMSThreads:手工設定CMS執行緒數量,CMS預設啟動的執行緒數是(ParallelGCThreads+3)/4
  • -XX:+UseConcMarkSweepGC開啟
  • -XX:CMSInitialtingOccupancyFraction
    設定CMS收集器在老年代空間被使用多少後觸發垃圾收集,預設值為68%,僅在CMS收集器時有效,-XX:CMSInitiatingOccupancyFraction=70
  • -XX:+UseCMSCompactAtFullCollection
    由於CMS收集器會產生碎片,此引數設定在垃圾收集器後是否需要一次記憶體碎片整理過程,僅在CMS收集器時有效。
  • -XX:+CMSFullGCBeforeCompaction
    設定CMS收集器在進行若干次垃圾收集後再進行一次記憶體碎片整理過程,通常與UseCMSCompactAtFullCollection引數一起使用
  • -XX:CMSInitiatingPermOccupancyFraction
    設定持久代

GC效能指標

吞吐量

應用花在非GC上的時間百分比

GC負荷

花在GC時間百分比

暫停時間(看GClog)

應用劃在GC stop-the-world的時間

GC頻率

反應速度

從一個物件變成垃圾到這個物件被回收的時間

小結

  • 一個互動式的應用要求暫停時間越少越好,然而,一個非互動式的應用,希望GC負荷越低越好
  • 一個實時系統對暫停時間和GC負荷要求,都是越低越好

記憶體容量配置原則

年輕代大小選擇

  • 響應時間優先的應用

    儘可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇),在此情況下,年輕代收集發生的頻率也是最小的,同時減少到達老年代的物件

  • 吞吐量優先的應用

    儘可能設定大,可能到達Gbit的程度,因為對響應時間沒有要求,垃圾收集可以並行進行,一般適合8CPU以上的應用
    避免設定過小,當新生代設定過小時會導致

  • YGC次數更加頻繁
  • 可能導致YGC物件直接進入老年代,如果此時老年代滿了,會觸發FGC

老年代大小選擇

  • 響應時間優先的應用

    使用併發垃圾收集器(CMS)設定小了會造成記憶體碎片,高回收頻率以及應用暫停而使用傳統的標記清除方式,如果堆大了,需要較長的收集時間,最優化的方案,一般參考以下資料獲得:
    併發垃圾收集資訊、持久代併發收集次數、傳統GC資訊、花在年輕代和年老代回收上的時間比例

  • 吞吐量優先的應用

    一般吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代。原因是,這樣可以儘可能回收掉大部分短期物件,減少中期的物件,而年老代儘量存放長期存活物件。

java排障

使用jps獲取java程序的pid

1
# jps -lvm

匯出CPU佔用高程序的執行緒棧

1
jstack `$pid` >> java.txt

檢視對應程序的哪個執行緒佔用CPU過高

1
# top -H -p 22056

將執行緒的pid轉換為16進位制

1
# echo "obase=16;`$pid`"|bc

第二步中匯出的java.txt中查詢轉換為16進位制的執行緒pid,找到對應的執行緒棧

分析負載高的執行緒棧都是什麼業務操作,優化程式並處理問題

原文地址:https://blog.stanley.wang/page/2/