JVM 理解性學習(一)

2022-05-04 09:33:07 字數 3653 閱讀 5973

重新學習,重新理解

驗證:.class 檔案載入到 jvm 裡的時候,會驗證下該檔案是否符合 jvm 規範。

準備:給實體類分配記憶體空間,以及給類變數(static 修飾)分配"預設值"。

解析:將符號引用替換為直接引用。

初始化:將類初始化,如果有父類且父類未初始化,會先初始化父類,再初始化此類。然後再對各個變數賦值。

乙個執行緒乙個虛擬機器棧,乙個方法對應乙個棧幀,棧幀裡存了區域性變數等資訊。

程式計數器主要是記錄當先執行緒執行到的位元組碼位置。

堆記憶體和最大記憶體512m,新生代256m,棧記憶體1m,元資料記憶體和最大記憶體128m。

引用:乙個引用指向乙個堆中的物件。

強引用:還有區域性變數或者靜態類變數等指向物件,此時該物件不可被**(任意情況)。

軟引用:如果堆中新生代記憶體滿了,發生 minorgc,就會**該軟引用。

將新生代分為3個區 eden 區,2個 survivor 區。用到了複製演算法,而一般情況下,minorgc 後存活的物件只有垃圾**之前的1%。 所以三個區的比例一般是8:1:1。

當 eden 區物件佔滿了記憶體,觸發 minorgc ,就會把存活物件放在乙個 survivor 區,然後清除 eden 區。

當發生第二次 minorgc 時,就會把 eden 區和存了物件的 survivor 區的存活物件,放到第二個 survivor 區。然後清除 eden 區和第乙個survivor 區的死亡物件。

1)、躲過15次 minorgc 之後從新生代進入老年代;

2)、大物件直接進入老年代。有乙個 jvm 引數 '-xx:pretenuresizethreshold' 設定值為位元組數,建立超過該大小的物件直接進入老年代。

3)、動態年齡判斷:當前放物件的 survivor 區,相同年齡的一批物件(以及小於該年齡)的總記憶體大於該區的記憶體的50%,大於該年齡的其他老物件,就會進入老年代(例如1,2,3歲年齡的物件佔了 s 區的50%以上,就會把大於3歲的物件移動到老年代去。所以盡量讓 s 區中的物件,佔比盡量少於 50%);

4)、eden 區存活物件太多,超過了 survivor 的大小,就直接把這些物件都轉移到老年代去。(空間擔保機制)

3、4點是可能造成頻繁 fullgc 的原因。

老年代記憶體分配擔保規則:在執行一次 minorgc 之前,jvm 會先檢查一下老年代可用的記憶體空間,是否大於新生代所有物件的總大小。小於也沒有關係,只要開啟了擔保規則,就不會關注於新生代所有物件的大小,而會關注新生代多次 minorgc 的**垃圾物件的平均大小。

觸發 fullgc:

survivor 區域放不下每次 minorgc 後存活的物件,所以直接放入了老年代,以至於沒發生幾次 minnogc 後,就會觸發 fullgc。

假如老年代可用記憶體小於新生代所有物件的大小,就會檢查乙個引數是否設定。設定後,遵循老年代空間分配擔保原則,就會檢查老年代的記憶體大小,是否大於之前 minorgc 後進入老年代的物件的平均大小。

如果判斷失敗,或者沒有設定引數,就會觸發 fullgc。

盡量少 fullgc 思路:

1)、將物件在新生代存活年齡又15變大,改為30或更大;

2)、禁止物件動態年齡,防止物件還未活到30次 minorgc 就移到老年代;

3)、將新生代佔的記憶體空間調大,並且將 surivivor 區域調大。保證每次 minorgc 後存活的物件能放在 surivvor 區域,不會因為 survivor 區域過小,直接將物件放老年代。

多執行緒併發的機制,效能更好,一般是線上生產系統的標配。

jvm 優化,指 減少垃圾**的頻率,降低垃圾**的時間,減小垃圾**對系統執行的影響。

stop th world :在垃圾**的時候,停止系統的執行,停止物件的建立。

parnew:多執行緒垃圾**機制。在合適的時機執行 minorgc,停止程式執行,禁止程式繼續建立物件,用多個垃圾**執行緒去**垃圾物件。(多執行緒是為了適應 多核 cpu)

指定引數: xx:+usepernewgc ,jvm 啟動,新生代就是用 pernew 垃圾**器了。

cms:標記清理演算法,採取的是垃圾**執行緒和系統工作執行緒,盡量同時執行的模式來處理的。

分為四個階段:初始標記、併發標記、重新標記、併發清理。

初始標記:讓系統的工作執行緒全部停止,進入 "stop the world" 狀態。去標記 gc roots,方法的區域性變數和類的靜態變數是 gc roots,類的例項變數不是 gc roots。(暫停工作執行緒的速度很快,只是為了標記 gc roots 直接引用的變數) (標記活著的---有引用的物件

併發標記:對老年代所有的物件都進行 gc roots 追蹤,是最耗時的。追蹤所有物件是否從根源上被 gc roots 引用了。但是最耗時的垃圾**執行緒,是和系統執行的執行緒併發進行,所以不影響系統。(因為老年代大多數物件都是存活的)

重新標記:讓系統停下來,進入 "stop the world" 階段。重新標記下第二階段新建立的物件,還有一些已有物件可能失去引用,變成垃圾物件。(再找到活著的少部分,其他都是垃圾物件

併發清理:很耗時,清理之前標記為垃圾的物件即可。(之前標記了存活和垃圾)垃圾**執行緒,也是和系統執行執行緒併發執行。

真正耗時的第二和第四階段,是和工作執行緒併發進行。第一和第三階段,雖然停止了工作執行緒,但是工作記憶體不多,不太耗時。

cms 併發垃圾**機制會有幾個問題:

第乙個問題:消耗 cpu 資源。

第二個問題:在垃圾**的時候,會新產生浮動垃圾物件,如果該物件佔記憶體超過了老年代預留的記憶體,就會發生 concurrent mode failure ,併發失敗,執行 serial old 垃圾**器,強行 stop th world。

第三個問題:記憶體碎片問題。full gc 後再次 stop the world ,停止工作執行緒,整理碎片。把存活物件移到一起。

所以為什麼 fullgc 比 minorgc 要慢10倍還多,主要就是演算法的不同,以及 fullgc 後還進行記憶體整理。

-xx:cmsinitiatingoccupancyfaction 引數可以設定老年代中物件佔多少比例的時候,觸發 cms 垃圾**器的**機制,預設92%。

1、如果新生代中物件太多,並且老年代可用記憶體不是很大(新生代物件記憶體 > 老年代可用記憶體),沒有空間擔保規則,那麼就會頻繁出發 fullgc。

2、minorgc 很快的原因:採用複製演算法,一次 minorgc 存活的物件少,遷移的存活物件就少,遷移就快。遷移完存活物件後,就直接清理垃圾物件,速度就快。

3、併發標記:怎麼一邊標記存活物件,一邊標記垃圾物件。就是在垃圾**執行緒工作的同時,讓系統執行緒也執行。這樣有引用的就是存活物件,沒有引用的就是垃圾物件。(主要是標記垃圾物件,所以會產生浮動垃圾)

4、為什麼優化 jvm:記憶體分配、引數設定不合理,導致物件頻繁進入老年代,頻繁觸發老年代 fullgc,導致系統每個幾分鐘就要停頓幾秒。

5、ygc 就算頻繁一些,也不會對系統產生很大影響。主要還是 fullgc 對系統的停頓時間太長。

JVM基礎學習(一) JVM記憶體模型

在j a高階知識的學習中,jvm都是避不過去的一關,我個人對於jvm的理解其實就是相當於在作業系統的外層再加了一層中間層,從來遮蔽了具體硬體之間的不同實現,使得j a實現了最重要的特性 一次編譯,處處執行。本分類中的內容都是基於 深入理解j a虛擬機器 中的內容以及網路上面部落格整理,外加自己的理解...

jvm可觸及性

可觸及性 可觸及的 從根節點可以觸及到這個物件 可復活的 一旦所有引用被釋放,就是可復活狀態 因為在finalize 中可能復活該物件 不可觸及的 在finalize 後,可能會進入不可觸及狀態 不可觸及的物件不可能復活 可以 public class canreliveobj override p...

JVM指令集理解

1.凡是帶const的表示將什麼資料壓運算元棧。如 iconst 2 將int型資料2壓入到運算元棧 aconst null 將null值壓入棧。2.bipush和sipush 表示將單位元組或者短整形的常量值壓入運算元棧。3.帶ldc的表示將什麼型別資料從常量池中壓入到運算元棧。如 ldc w 將...