JVM

JVM內存模型優點

  1. 內置基於內存的併發模型:      多線程機制

  2. 同步鎖Synchronization

  3. 大量線程安全型庫包支持

  4. 基於內存的併發機制,粒度靈活控制,靈活度高於數據庫鎖。

  5. 多核並行計算模型

  6. 基於線程的異步模型。


JVM性能的人爲問題

  1. 關鍵原因是:沒有正確處理好對象的生命週期。

  2. 需要從需求中找出存在自然邊界的業務對象,將其對應落實到內存中,成爲內存模型In-memory Domain Model。

  3. 有大小邊界限制的內存是緩存,沒有永遠使用不完的內存,緩存=“有邊界的”內存。

  4. 緩存是Domain Model對象緩存,不同於傳統意義上數據庫緩存的定義。

  5. 分佈式緩存可以提高巨量數據處理計算能力。


Java內存種類

  1. Stack棧內存
    存取速度快,數據可多線程間共享。 
    存在棧中的數據大小與生存期必須確定

  2. Heap堆內存
       大小動態變化,對象的生命週期不必事先告訴編譯器JVM。

兩種內存使用

  1. Stack棧內存 
    基本數據類型,Java  指令代碼,常量 
    對象實例的引用 對象的方法代碼

  2. Heap堆內存
       對象實例的屬性數據和數組。堆內存由Java虛擬機的自動垃圾回收器來管理。


對象如何保存在內存中?

  1. 對象的屬性Attribute Property

    屬性值作爲數據,保存在數據區heap 中,包括屬性的類型Classtype和對象本身的類型

  1. 方法method

   方法本身是指令的操作碼,保存在stack中。 
   方法內部變量作爲指令的操作數也是在Stack中, 
    包括基本類型和其他對象的引用。

  1. 對象實例在heap 中分配好內存以後,需要在stack中保存一個4字節的heap內存地址,用來定位該對象實例在heap 中的位置,便於找到該對象實例。


靜態屬性和方法的特點

  1. 靜態屬性和方法都是保存在Stack中,

  2. Stack內存是共享的,其他線程都可以訪問靜態屬性實際是全局變量。

  3. 靜態方法在Stack,就無法訪問Heap中的數據。靜態方法無法訪問普通對象中數據。

  1. 靜態屬性意味着全局變量,生命週期和JVM一致。JVM屬於技術邊界,靜態只能用於技術邊界內工具性質使用,不能用作業務。


內存管理:垃圾回收機制

  1. 每一種垃圾收集的算法(引用計數、複製、標記-清除和標記-整理等)在特定條件下都有其優點和缺點。

  2. 當有很多對象成爲垃圾時,複製可以做得很好,但是複製許多生命週期長的對象時它就變得很糟(要反覆複製它們)。

  3. 標記-整理適合生命週期長對象可以做得很好(只複製一次),但是不適合短生命的對象。

  4. Sun JVM 1.2 及以後版本使用的技術稱爲 分代垃圾收集(generational garbage collection),它結合了這兩種技術以結合二者的長處。


可選用的GC類型

jvm參數


JVM性能優化

  1. 內存微調優化

  1. 鎖爭奪微調:

  多線程 不變性 單寫原則 Actor Disrupotor

  1. CPU使用率微調

  1. I/O 微調


內存微調優化

  1. 內存分配:

   新生代 Eden和survior  舊生代內存大小分配。 
   內存越大,吞吐量越大,但是需要內存整理的時間就越長,響應時間有延遲。

  1. 垃圾回收機制

   垃圾回收啓動整個應用都暫停,暫停時間造成響應時間有延遲。




內存微調目標


  1. 在延遲性(響應時間)和吞吐量上取得一個平衡。

  1. 內存大小影響吞吐量和延遲性。需要在內存大小和響應時間之間取得一個平衡。

  1. 垃圾回收機制是延遲的最大問題。目標儘量不啓動,少啓動。

jvm微調目標


 

內存模型

jvm

新生代Eden內存分配

  • 新生代(New Generation ):Eden + 1 Survivor。所有新創建的對象在Eden。

  • 當Eden滿了,啓動Stop-The-World的GC,或爲minor gc,採取數次複製Copy-Collection到Survivor。

  • 經過幾次收集,壽命不斷延長的對象從Survivor 進入老生代,也稱爲進入保有Tenuring,類似普通緩存LRU算法。


survivor設計要旨

  1. 足夠大到能容納所有請求響應中涉及的對象數據。

  1. 每個survivor空間也要足夠大到能夠容納活躍的請求對象和保有對象。

  1. Survivor大小決定了保有Tenuring閥值,閥值如果能大到容納所有常住對象,那麼保有遷移過程就越快。

老生代Old

  1. 老生代的gc稱爲major gc,就是通常說的full gc。

  2. 採用標記-整理算法。由於老年區域比較大,而且通常對象生命週期都比較長,標記-整理需要一定時間。所以這部分的gc時間比較長。

  3. minor gc可能引發full gc。當eden+from space的空間大於老生代的剩餘空間時,會引發full gc。這是悲觀算法,要確保eden+from space的對象如果都存活,必須有足夠的老生代空間存放這些對象。

  4. 這些都根據情況調整啓動JVM的設置。

  5. 使用 Adaptive讓JVM自動劃分新生代和老生代。

Permanent Generation 永久代

  1. 該區域比較穩定,主要用於存放classloader信息,比如類信息和method信息。

  2. 缺省是 64M ,如果你的代碼量很大,容易出現OutOfMemoryError: PermGen space 。

  3. 2G以上內存設置MaxPermSize爲160M

  4. -XX:PermSize=128m -XX:MaxPermSize=160m


降低Full GC發生概率

  1. 爲了降低Full GC發生概率,如果降低了老生代大小,那麼 OutOfMemoryError 發生,Full GC概率反而會上升。

  2. 如果爲了降低Full GC,增加老生代大小,執行時間可能會被延長。

  3. 必須尋找合適大小的老生代。

  4. 避免大的對象遷移到老生代。

  5. 減少遷移到老生代的對象數目


java.lang.OutOfMemoryError

  1. (1)在高負荷的情況下的卻需要很大的內存,因此可以通過修改JVM參數來增加Java Heap Memory。

  2. (2)應用程序使用對象或者資源沒有釋放,導致內存消耗持續增加,關鍵採取OO封裝邊界方式,樹立對象都有生命週期的基本習慣。

  3. (3)再一種也可能是對於第三方開源項目中資源釋放了解不夠導致使用以後資源沒有釋放(例如JDBC的ResultSet等)。


JVM參數

  1. -Xms, -Xmx—定義JVM的heap大小最小和最大值。

  1. -XX:NewSize— 定義年輕態的最小大小,Eden越大越好,但是越大響應有延遲。

 

  1. -Xmx2G -Xms1G -XX:NewSIze=512M (OldGen at least 1G)

  2. -Xmx3G -Xms1G -XX:NewSize=512M (OldGen at least 2G)

  3. Xmx4G -Xms2G -XX:NewSize=1G (OldGen at least 2.5G)

  4. -Xmx6G -Xms3G -XX:NewSize=2G (OldGen at least 3.5G)

  5. -Xmx8G -Xms4G -XX:NewSize=3G (OldGen at least 4.5G)

參數調整示意

  1. JAVA_OPTS="$JAVA_OPTS -server -Xss1280K -Xms1664m -Xmx1664m  -XX:MaxPermSize=128m -XX:SurvivorRatio=16  -XX:NewSize=1280m  -XX:MaxNewSize=1280m -XX:+DisableExplicitGC -XX:GCTimeRatio=2 -XX:ParallelGCThreads=4 -XX:+UseParNewGC -XX:MaxGCPauseMillis=2000 -XX:+UseConcMarkSweepGC  -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSClassUnloadingEnabled

jvm參數


Survivor大小

  1. NewSize / ( SurvivorRatio + 2)

  2. 如果SurvivorRatio =16, NewSize =1280m,那麼S大小是70M。

  3. 太小,溢出的複製Collection進入老生代。

  4. 太大,閒置無用 浪費內存。

  5. 使用XX:+PrintTenuringDistribution  和-XX:+PrintGCDetails, -XX:+PrintHeapAtGC觀察:

  6. 與 -XX:+UseAdaptiveSizePolicy 衝突

 


垃圾回收機制啓動

  1. 垃圾回收機制不會頻繁啓動,因爲機制一旦啓動,造成應用程序停頓。

  2. 機制一般內存剩餘5%左右啓動,所以有現象:啓動服務器,內存不斷消耗,有多大內存消耗多大。

  3. 問題:如果服務器程序頻繁觸及5%底線,機制頻繁啓動,造成服務器慢..甚至死機。

  4. 根源:應用程序無限制頻繁大量創建對象,消耗內存。


控制垃圾回收

  1. 帶CMS參數的都是和併發回收相關的

  2. -XX:+UseParNewGC,對新生代採用多線程並行回收。

  3. CMSInitiatingOccupancyFraction=90說明年老代到90%滿的時候開始執行對年老代的併發垃圾回收(CMS)

  4. 用jmap和jstack查看


串行 並行回收的區別

  1. 新生代 高吞吐量:

  2. -XX:+UseSerialGC
    -XX:+UseParallelGC
    -XX:+UseParNewGC

  3. 老生代 低暫停:

  4. -XX:+UseParallelOldGC
    -XX:+UseConcMarkSweepGC

  5. 相同點:GC時都暫停一切。

  6. 不同點:一個線程和多個線程同時GC

並行和CMS(Concurrent-Mark-Sweep)區別

  1. CMS步驟:

  2. - initial mark
    - concurrent marking
    - remark
    - concurrent sweeping

  3. 區別:CMS一個線程,並行多個線程

  4. CMS只是在1 3階段暫停,而並行全部暫停。

 Parallel GC 和 CMS GC

  1. 壓實compaction是移除內存碎片,也就是移除已經分配的內存之間的空白空間。

  2. 在Parallel GC中,無論Full GC是否執行,壓實總是被執行,會花費更多時間,不過在執行完Full GC後,內存也許再被使用時,會分配得快些,能夠順序分配了。

  3. CMS GC 並不執行壓實,所以更快,碎片太多,沒有空間放置大的需要連續空間的對象,“Concurrent mode failure”會發生。


並行和CMS配置

  1. -XX:UserParNewGC 適合於

  新生代 (multiple GC threads)
-XX:+UseConcMarkSweepGC  適合於
  老生代 (one GC thread, freezes the JVM only during the initial mark and remark phases) 
  -XX:InitiatingOccupancyFraction 80是表示CMS是在老生代接近滿80%啓動,如CPU空閒,可設定點一些。

  -XX:+CMSIncrementalMode 用於CMS,不會讓處理器Hold住整個併發phases  。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章