天天看點

JVM

jvm記憶體模型優點

内置基于記憶體的并發模型:      多線程機制

同步鎖synchronization

大量線程安全型庫包支援

基于記憶體的并發機制,粒度靈活控制,靈活度高于資料庫鎖。

多核并行計算模型

基于線程的異步模型。

jvm性能的人為問題

關鍵原因是:沒有正确處理好對象的生命周期。

需要從需求中找出存在自然邊界的業務對象,将其對應落實到記憶體中,成為記憶體模型in-memory domain model。

有大小邊界限制的記憶體是緩存,沒有永遠使用不完的記憶體,緩存=“有邊界的”記憶體。

緩存是domain model對象緩存,不同于傳統意義上資料庫緩存的定義。

分布式緩存可以提高巨量資料處理計算能力。

java記憶體種類

stack棧記憶體

存取速度快,資料可多線程間共享。 

存在棧中的資料大小與生存期必須确定

heap堆記憶體

   大小動态變化,對象的生命周期不必事先告訴編譯器jvm。

兩種記憶體使用

stack棧記憶體 

基本資料類型,java  指令代碼,常量 

對象執行個體的引用 對象的方法代碼

   對象執行個體的屬性資料和數組。堆記憶體由java虛拟機的自動垃圾回收器來管理。

對象如何儲存在記憶體中?

對象的屬性attribute property

    屬性值作為資料,儲存在資料區heap 中,包括屬性的類型classtype和對象本身的類型

方法method

   方法本身是指令的操作碼,儲存在stack中。 

   方法内部變量作為指令的操作數也是在stack中, 

    包括基本類型和其他對象的引用。

對象執行個體在heap 中配置設定好記憶體以後,需要在stack中儲存一個4位元組的heap記憶體位址,用來定位該對象執行個體在heap 中的位置,便于找到該對象執行個體。

靜态屬性和方法的特點

靜态屬性和方法都是儲存在stack中,

stack記憶體是共享的,其他線程都可以通路靜态屬性實際是全局變量。

靜态方法在stack,就無法通路heap中的資料。靜态方法無法通路普通對象中資料。

靜态屬性意味着全局變量,生命周期和jvm一緻。jvm屬于技術邊界,靜态隻能用于技術邊界内工具性質使用,不能用作業務。

記憶體管理:垃圾回收機制

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

當有很多對象成為垃圾時,複制可以做得很好,但是複制許多生命周期長的對象時它就變得很糟(要反複複制它們)。

标記-整理适合生命周期長對象可以做得很好(隻複制一次),但是不适合短生命的對象。

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

可選用的gc類型

JVM

jvm性能優化

記憶體微調優化

鎖争奪微調:

  多線程 不變性 單寫原則 actor disrupotor

cpu使用率微調

i/o 微調

記憶體配置設定:

   新生代 eden和survior  舊生代記憶體大小配置設定。 

   記憶體越大,吞吐量越大,但是需要記憶體整理的時間就越長,響應時間有延遲。

垃圾回收機制

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

記憶體微調目标

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

記憶體大小影響吞吐量和延遲性。需要在記憶體大小和響應時間之間取得一個平衡。

垃圾回收機制是延遲的最大問題。目标盡量不啟動,少啟動。

JVM

記憶體模型

JVM

新生代eden記憶體配置設定

新生代(new generation ):eden + 1 survivor。所有新建立的對象在eden。

當eden滿了,啟動stop-the-world的gc,或為minor gc,采取數次複制copy-collection到survivor。

經過幾次收集,壽命不斷延長的對象從survivor 進入老生代,也稱為進入保有tenuring,類似普通緩存lru算法。

survivor設計要旨

足夠大到能容納所有請求響應中涉及的對象資料。

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

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

老生代old

老生代的gc稱為major gc,就是通常說的full gc。

采用标記-整理算法。由于老年區域比較大,而且通常對象生命周期都比較長,标記-整理需要一定時間。是以這部分的gc時間比較長。

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

這些都根據情況調整啟動jvm的設定。

使用 adaptive讓jvm自動劃分新生代和老生代。

permanent generation 永久代

該區域比較穩定,主要用于存放classloader資訊,比如類資訊和method資訊。

預設是 64m ,如果你的代碼量很大,容易出現outofmemoryerror: permgen space 。

2g以上記憶體設定maxpermsize為160m

-xx:permsize=128m -xx:maxpermsize=160m

降低full gc發生機率

為了降低full gc發生機率,如果降低了老生代大小,那麼 outofmemoryerror 發生,full gc機率反而會上升。

如果為了降低full gc,增加老生代大小,執行時間可能會被延長。

必須尋找合适大小的老生代。

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

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

java.lang.outofmemoryerror

(1)在高負荷的情況下的卻需要很大的記憶體,是以可以通過修改jvm參數來增加java heap memory。

(2)應用程式使用對象或者資源沒有釋放,導緻記憶體消耗持續增加,關鍵采取oo封裝邊界方式,樹立對象都有生命周期的基本習慣。

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

jvm參數

-xms, -xmx—定義jvm的heap大小最小和最大值。

-xx:newsize— 定義年輕态的最小大小,eden越大越好,但是越大響應有延遲。

-xmx2g -xms1g -xx:newsize=512m (oldgen at least 1g)

-xmx3g -xms1g -xx:newsize=512m (oldgen at least 2g)

xmx4g -xms2g -xx:newsize=1g (oldgen at least 2.5g)

-xmx6g -xms3g -xx:newsize=2g (oldgen at least 3.5g)

-xmx8g -xms4g -xx:newsize=3g (oldgen at least 4.5g)

參數調整示意

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大小

newsize / ( survivorratio + 2)

如果survivorratio =16, newsize =1280m,那麼s大小是70m。

太小,溢出的複制collection進入老生代。

太大,閑置無用 浪費記憶體。

使用xx:+printtenuringdistribution  和-xx:+printgcdetails, -xx:+printheapatgc觀察:

與 -xx:+useadaptivesizepolicy 沖突

垃圾回收機制啟動

垃圾回收機制不會頻繁啟動,因為機制一旦啟動,造成應用程式停頓。

機制一般記憶體剩餘5%左右啟動,是以有現象:啟動伺服器,記憶體不斷消耗,有多大記憶體消耗多大。

問題:如果伺服器程式頻繁觸及5%底線,機制頻繁啟動,造成伺服器慢..甚至當機。

根源:應用程式無限制頻繁大量建立對象,消耗記憶體。

控制垃圾回收

帶cms參數的都是和并發回收相關的

-xx:+useparnewgc,對新生代采用多線程并行回收。

cmsinitiatingoccupancyfraction=90說明年老代到90%滿的時候開始執行對年老代的并發垃圾回收(cms)

用jmap和jstack檢視

串行 并行回收的差別

新生代 高吞吐量:

-xx:+useserialgc

-xx:+useparallelgc

-xx:+useparnewgc

老生代 低暫停:

-xx:+useparalleloldgc

-xx:+useconcmarksweepgc

相同點:gc時都暫停一切。

不同點:一個線程和多個線程同時gc

并行和cms(concurrent-mark-sweep)差別

cms步驟:

- initial mark

- concurrent marking

- remark

- concurrent sweeping

差別:cms一個線程,并行多個線程

cms隻是在1 3階段暫停,而并行全部暫停。

 parallel gc 和 cms gc

壓實compaction是移除記憶體碎片,也就是移除已經配置設定的記憶體之間的空白空間。

在parallel gc中,無論full gc是否執行,壓實總是被執行,會花費更多時間,不過在執行完full gc後,記憶體也許再被使用時,會配置設定得快些,能夠順序配置設定了。

cms gc 并不執行壓實,是以更快,碎片太多,沒有空間放置大的需要連續空間的對象,“concurrent mode failure”會發生。

并行和cms配置

-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  。

jvm