1. 程式人生 > 實用技巧 >動態開點

動態開點

虛擬機器(JVM)

在這裡插入圖片描述

在這裡插入圖片描述

工作原理

一個java檔案通過編譯成位元組碼(也就是類檔案),通過類裝載子系統到記憶體區中執行(執行時資料區),記憶體區都是通過位元組碼引擎執行的。其中記憶體區主要由堆,棧,本地方法棧,方法區,程式計數器。其中呀,堆跟方法區是共有資料,其他都是私有資料。

程式計數器的作用可以看做是當前執行緒所執行的位元組碼的行號指示器。

先說棧,棧也可以理解為是一個執行緒棧,它是主管java程式的執行,我們這個執行緒中的一些變數都是在棧記憶體中分配的。棧的記憶體結構是先進後出,在這個棧中,執行一個方法的同時都會建立一個棧幀。每個棧幀用於儲存區域性變量表,運算元棧,動態連結,方法出口等資訊。區域性變量表就是存放了編譯期可知的各種基本資料型別,物件引用等等,運算元棧就是存放計算機一些臨時的操作資料。(從區域性變量表或物件例項的欄位中複製常量或變數寫入到運算元棧,再隨著計算的進行將棧中元素出棧到區域性變量表或者返回給方法呼叫者,也就是出棧/入棧操作。)

方法出口就是根據當前位元組碼指令,返回到方法被呼叫的位置,方法返回時可能需要在棧幀中儲存一些資訊。如果異常退出,則不會保留資訊。

堆主要用於存放各種類的例項物件對應的記憶體地址。

方法區儲存載入進來的每一個類的結構資訊,可以看做是將類(Class)的模板資訊,儲存在方法區裡

本地方法棧與棧原理差不多,棧也就是java棧,為執行java方法服務,本地方法棧是為執行本地方法服務的。對本地實現方法及資料結構沒有約束。如果說這個方法是用native(內t無)修飾的,他就是本地方法棧。

垃圾回收機制

在這裡插入圖片描述

在這裡插入圖片描述

堆中分為年輕代(1/3)和老年代(2/3),年輕代中有包含有eden(伊甸園),survivor(讀音:色外喔,翻譯:倖存區)。

一般新的物件都會儲存到eden中,當eden中滿的話,會觸發Minor GC(賣訥)。

當eden滿的話,如何檢視那個是垃圾,那些不是?

通過使用可達性分析演算法,將“gc roots”作為物件,向下搜尋,找到的物件都是非垃圾物件。都會通過複製演算法複製到survivor 0區中,然後將eden中的全部銷燬。

當survivor 0區也存滿時候,將eden和servivor 0區中存活的物件存放到servivor 1區中,然後清空eden和survivor0區清除,此時survivor0 區是空的,然後將survivor 0區和survivor 1區進行交換,一直保持survivor 1區為空。

當survivo 1區不能存放eden和survivor 0中的存活,就將存活物件存到老年代。要是老年代也滿的話就會觸發full gc,也就是年輕代和老年代都進行回收。

為什麼要對jvm進行優化呢,就是當新生代和老年代都滿的話,會觸發full gc 垃圾回收,full gc 進行垃圾回收時,會對程式進行stw(stop the world-關閉),這樣就會導致程式停止,日誌不輸出,並且佔用需要系統資源(比如,cpu),影響系統吞吐量。

記憶體調優

調優的目的就是為了減少gc的頻率和full gc的次數,因為full gc執行時,會產生stw(stop the world),會是佔用較多的系統資源(cpu),影響程式正常執行。調優說明白點就是調整堆中記憶體佔用比例。我們可以使用jdk提供的記憶體檢視工具,如:jconsole或者java visualVM

監控gc狀態,檢視日誌,根據當前堆記憶體快照和gc日子判斷是否進行優化。

如果日誌正常,則不需要優化,如果頻繁使用full gc 則要進行調優,調優就是調整其記憶體分配大小比例。

full gc 頻繁使用的原因

  1. 年輕代設定過小 由於年輕代設定過小,造成頻繁使用minor gc,讓系統一直處於垃圾回收中無法執行其他程式,再者就是年輕代設定過小會使一個大物件直接進入老年代,就是有些物件當佔有survivor 0區的50%以上,會直接進入老年代,佔用老年代記憶體,從而頻繁呼叫full gc
  2. 年輕代設定過大 會導致老年代設定過小,從而頻繁使用full gc ,再者年輕代gc消耗能力增加。
  3. survivor設定過小 導致一些物件直接進入老年代
  4. sruvivor設定過大 導致eden設定過小,頻繁使用minor gc