導航:首頁 > 萬維百科 > jvmcms過程

jvmcms過程

發布時間:2020-10-15 22:16:08

1、jvm的內存模型中運行時常量池與Class文件結構中說的常量池是否是同一個?

老年代調整:
標記-清除(Mark - Sweep)演算法
標記-壓縮(Mark - Compact)演算法

老年代調整參數:
-XX:PretenureSizeThreshold 控制直接升入老年代的對象大小,大於這個值的對象會直接分配在老年代中

FULL GC發生的概率比較低,發生的話操作時間特別長,嚴重影響程序的性能。

永久代(JDK1.8之後被廢除了)
該區域中的對象不會被回收。當時簡單的理解是方法區就是永久代,HotSpot虛擬機規范中是存在永久代概念的。
被元空間取代

JDK1.8之後,元空間取代永久代,元空間的本質就是本機的物理內存
OOM異常 可能分為
堆內存溢出(Java Heap space):往往出現在FUll GC失敗之後
永久代溢出(PermGen space):一個方法中出現的內存溢出
元空間溢出(Metaspace):分配的物理內存不足,或者數據量高於物理內存

垃圾的收集一定要分兩個空間考慮:年輕代、老年代,老年代的內存空間要大於年輕待

可用GC方式:
新生代可用GC策略:
串列GC(Serial Copying)
並行回收GC(Parallel Scavenge):
復制(Copying)清理演算法;
操作步驟:
在掃描和復制時均採用多線程方式處理,並行回收GC為空間較大的年輕代回收提供許多優化
優勢:
在多CPU的機器上其GC耗時會比串列方式短,適合多CPU、對暫停時間要求較短的應用
並行GC(ParNew):
復制(Copying)清理演算法
操作步驟:
並行GC(ParNew)必須結合老年代,"cms GC"一起使用。因為年輕代如果發生了"Minor GC"時,老年代也需要使用"CMS GC"同時處理,
(並行回收GC並不會做這些)

CMS(Concurrent Mark-Sweep):是以犧牲吞吐量為代價來獲得最短回收停頓時間的垃圾回收器。對於要求伺服器響應速度的應用上,這種垃圾回收器非常適用
老年代可用GC策略:
串列GC(Serial MSC)
演算法:標記-清除-壓縮(Mark-Sweep-Compact)
操作步驟:
掃描老年代中還存活的對象,並且對這些對象進行標記
遍歷整個老年代的內存空間,回收所有未標記的對象內存
將所有存活對象都集中在一端,而後將所有回收對象的內存空間變為一塊連續的內存空間
優缺點:串列執行的過程中為單線程,需要暫停應用並耗時較長
並行GC(Parallel MSC)
演算法:標記-壓縮(Mark-Compact)
操作步驟:
將老年代內存空間按照線程個數劃分若乾子區域;
多個線程並行對各自子區域內的存活對象進行標記
多個線程並行清除所有未標記的對象
多個線程並行將多個存活對象整理在一起,並將所有被回收的對象空間整合為一體
優缺點:
多個線程同時進行垃圾回收可以縮短應用的暫停時間,但是由於老年代的空間一般較大,所以在掃描和標記存活對象上需要花費較長時間。

與串列相比,就是多了一個多線程的支持,但是這樣的暫停時間就會減少
並行GC(CMS)
演算法:標記-清除(Mark-Sweep)
操作步驟:
初始標記(STW【Stop-The World】 Initial Mark):虛擬機暫停正在執行的任務(STW),由根對象掃描出所有的關聯對象,並做出標記。此過程只會導致短暫的JVM暫停
並發標記(Concurrent Marking):恢復所有暫停的線程對象,並且對之前標記過的對象進行掃描,取得所有跟標記對象有關聯的對象
並發預處理(Concurrent Precleaning):查找所有在並發標記階段新進入老年代的對象(一些對象可能從新生代晉升到老年代,或者有一些對象被分配到老年代),通過重新掃描,減少下一階段的工作
重新標記(STW Remark):此階段會暫停虛擬機,對在「並發標記」階段被改變引用或新創建的對象進行標記
並發清理(Concurrent Sweeping):恢復所有暫停的應用線程,對所有未標記的垃圾對象進行清理,並且會盡量將已回收對象的空間重新拼湊為一個整體。在此階段收集器線程和應用程序線程並發執行
並發重置(Concurrent Reset):重置CMS收集器的數據結構,等待下一次垃圾回收。
優缺點:
只有在第一次和重新標記階段才會暫停整個應用,這樣對應用程序所帶來的影響非常的小、缺點是並發標記與回收線程會與應用線程爭搶CPU資源,並且容易產生內存碎片。

常用GC策略:
運行環境 年輕代GC 老年代
單機程序(client) 串列GC(Serial Copying) 串列GC(Serial MSC)
伺服器程序(Server) 並行回收GC(Parallel Scavenge) 並行GC(Parallel Mark Sweep、Parallel Compacting)

如果要向確認使用的GC處理,首先需要知道當前的主機上可以支持的處理進程數量
調參:
使用串列GC策略:-XX:+UseSerialGC
使用並行GC策略:-XX:+UseParNewGC
使用CMS:-XX:+UseConcMarkSweepGC
CMS會經歷如下幾個步驟:
"CMS-concurrent-mark-start":CMS標記開始
"CMS-concurrent-mark":表示開始進行標記,進入到了STW狀態(暫停)
"CMS-concurrent-preclean-start":預清理開始
"CMS-concurrent-sweep-start":開始進行無用對象清理
CMS的處理適當性能會好一些,但是這所有的GC策略都是現在正在常用的策略,不過似乎都有缺陷。

總結:
實際開發之中對於GC的策略不建議手動修改,默認的一版比較好

G1收集器:
對Java伺服器而言,如何去選擇一個合適的配置呢? 默認情況下,Java會為每一個線程分配1M的內存空間。如果現在電腦有32G的內存,最大可以分配30G的內存空間(理論)
理論上可以處理 30* 1024 個用戶請求,所以一般的伺服器處理個5000——10000基本上也就夠了

G1的實現方案相當於將所有的子內存區域合並在一起,也不在進行任何的區分,這樣就相當於所有的內存的區域都可以按照統一的方式進行統一規劃處理
G1的最大特點就是避免了全內存掃描

G1在標記和清理的時候是按照區域完成的,這樣不影響其他的區域的執行,除此之外,使用的形式和之前的CMS都是類似的操作方式。
調參使用G1收集器:-Xmx10m -Xms10m -XX:+PrintGCDetails -XX:+UseG1GC

G1的垃圾收集比傳統的GC要快一些

引用類型概述:
對於垃圾的產生與回收的處理之中,要想進行更好的控制,就必須清楚的掌握JAVA中的四種引用方式:
1.強引用(Strong Reference):即使進行了多次GC的回收,即使JVM的內存真的已經不夠用了,最終不得以拋出了OOM錯誤,那麼該引用繼續搶占
2.軟引用(Soft Reference):當內存空間不足時,可以回收此內存空間。如果充足則不回收,可以用其完成一些緩存的處理操作開發
3.弱引用(Weak Reference):不管內存是否緊張,只要一出現GC處理,則立即回收
4.幽靈引用(Phantom Reference):和沒有引用是一樣的

強引用:
即使出現GC,即使內存不足,該引用的數據也不會被回收
強引用不是造成OOM的關鍵因素,正常來講,你每一個用戶(線程)操作完成後該對象都很容易進行回收。
軟引用:
當內存空間不足時才進行GC的空間釋放,但是如果想使用軟引用必須單獨使用特殊的處理類:
import java.lang.ref.SoftReference;
public class SoftReferenceTest {
public static void main(String[] args) {
String str = "www.cwnu.com";
//加入到軟引用之中
SoftReference<String> stringReference = new SoftReference<String>(str);
//類似強引用埠連接
str = null;
//因為軟引用空間還很富裕,所以不會釋放
System.gc();
System.out.println(stringReference.get());

}
}
在開發中,可以利用軟引用實現高速緩存組件
弱引用:
最大的特點:一旦發生GC操作,則保存的內容則立即釋放
import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static void main(String[] args) {
String a = new String("www.cwnu.com");
WeakReference reference = new WeakReference<String>(a);
a = null;
System.gc();
System.out.println(reference.get());
}
}
在我們類集裡面有一個與弱引用功能相似的Map集合,WeakHashMap<K,V>,它屬於弱引用的一個實現
import java.util.*;
public class WeakReferenceTest {
public static void main(String[] args) {
Map<Integer,String> map = new WeakHashMap<Integer,String>();
map.put(new Integer(1),new String("a"));
map.put(new Integer(2),new String("b"));
System.gc();
System.out.println(map);
}
}
使用WeakHashMap好處是保存一些共享數據,如果長時間不使用則可以清空

引用隊列:
如果要想清楚引用隊列,則首先必須知道對象的引用的強度,如果說按照現在的理解來講: 強引用 > 軟引用 > 弱引用。

引用隊列裡面所保存的就是一個要准備被回收的對象的信息
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class ReferenceQueueTest {
public static void main(String[] args) throws Exception{
String str = new String("www.cwnu.com");

ReferenceQueue<String> queue = new ReferenceQueue<String>();
WeakReference<String> weak = new WeakReference<String>(str,queue);
str = null;
System.out.println(weak.isEnqueued());
System.gc();
Thread.sleep(500);
System.out.println(weak.isEnqueued());
//觀察隊列是否有內容
System.out.println(queue.poll());
}
}
幽靈引用(虛引用):
幽靈引用指的是什麼都不保存,但是又看起來保存了似得。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class PhantomReferenceTest {
public static void main(String[] args) throws Exception{
String str = new String("www.cwnu.com");

ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> ps = new PhantomReference<String>(str,queue);
str = null;
System.out.println(ps.isEnqueued());
System.gc();
Thread.sleep(500);
System.out.println(ps.isEnqueued());
//觀察隊列是否有內容
System.out.println(queue.poll());
}
}

幽靈引用直接把保存的內容保存在引用隊列之中

關於逃逸分析:
在平時開發過程中就要可盡可能的控制變數的作用范圍了,變數范圍越小越好

建議去刷題,我運氣好,簡單的演算法讓我碰到了,一些快排,堆排,二叉樹相關的,鏈表反轉,成環,環節點,跳樓梯等常規的簡單演算法建議刷刷,雙指針,dp,遞歸這些還是多找找感覺,大數據內存有限的場景的統計,有時間一些middle可以去試試,手寫紅黑樹你要是可以,那我估計演算法你穩了

堆棧相關:
1.寄存器:最快的存儲區, 由編譯器根據需求進行分配,我們在程序中無法控制.
2. 棧:存放基本類型的變數數據和對象的引用,但對象本身不存放在棧中,而是存放在堆(new 出來的對象)或者常量池中(字元串常量對象存放在常量池中。)
3. 堆:存放所有new出來的對象。
4. 靜態域:存放靜態成員(static定義的)
5. 常量池:存放字元串常量和基本類型常量(public static final)。
6. 非RAM存儲:硬碟等永久存儲空間

2、jvm cms stop the world進程什麼意思

JVM有個叫做「安全點」和「安全區域」的東西,在發生GC時,所有的線程都會執行到「安全點」停下來。

在需要GC的時候,JVM會設置一個標志,當線程執行到安全點的時候會輪詢檢測這個標志,如果發現需要GC,則線程會自己掛起,直到GC結束才恢復運行。

體系結構:

每個Java程序都離不開Java虛擬機,Java程序的運行依靠具體的Java虛擬機實例。在Java虛擬機規范中,分別用子系統、內存區、數據類型以及指令這幾個術語來描述的。這些組成部分一起展示出一個抽象化的虛擬機內部的抽象體系結構。

(2)jvmcms過程擴展資料:

內存管理:

對於Java運行時涉及到的存儲區域主要包括程序計數器、Java虛擬機棧、本地方法棧、java堆、方法區以及直接內存等等。對於每個部分,都有其使用的條件。程序計數器主要是取下一條指令,在Java裡面主要是取下一條指令的位元組碼文件。

Java虛擬機棧主要是利用棧先進後出的特性存儲局部變數表,動態鏈接等,主要包括堆內存和棧內存,對於程序員內存分析而言是特別重要的。

本地方法棧與上邊的棧基本作用差不多,只不過這里是為Java方法而服務。Java堆是內存管理中最大的一塊,所有的線程共享這一塊內容,同時該部分也是垃圾收集器的主要區域。

3、如何設置jvm啟動參數

不管是YGC還是Full GC,GC過程中都會對導致程序運行中中斷,正確的選擇不同的GC策略,調整JVM、GC的參數,可以極大的減少由於GC工作,而導致的程序運行中斷方面的問題,進而適當的提高Java程序的工作效率。但是調整GC是以個極為復雜的過程,由於各個程序具備不同的特點,如:web和GUI程序就有很大區別(Web可以適當的停頓,但GUI停頓是客戶無法接受的),而且由於跑在各個機器上的配置不同(主要cup個數,內存不同),所以使用的GC種類也會不同(如何選擇見GC種類及如何選擇)。本文將注重介紹JVM、GC的一些重要參數的設置來提高系統的性能。
GC性能方面的考慮
對於GC的性能主要有2個方面的指標:吞吐量throughput(工作時間不算gc的時間占總的時間比)和暫停pause(gc發生時app對外顯示的無法響應)。
1. Total Heap
默認情況下,vm會增加/減少heap大小以維持free space在整個vm中占的比例,這個比例由MinHeapFreeRatio和MaxHeapFreeRatio指定。
一般而言,server端的app會有以下規則:
對vm分配盡可能多的memory;
將Xms和Xmx設為一樣的值。如果虛擬機啟動時設置使用的內存比較小,這個時候又需要初始化很多對象,虛擬機就必須重復地增加內存。
處理器核數增加,內存也跟著增大。
2. The Young Generation
另外一個對於app流暢性運行影響的因素是young generation的大小。young generation越大,minor collection越少;但是在固定heap size情況下,更大的young generation就意味著小的tenured generation,就意味著更多的major collection(major collection會引發minor collection)。
NewRatio反映的是young和tenured generation的大小比例。NewSize和MaxNewSize反映的是young generation大小的下限和上限,將這兩個值設為一樣就固定了young generation的大小(同Xms和Xmx設為一樣)。
如果希望,SurvivorRatio也可以優化survivor的大小,不過這對於性能的影響不是很大。SurvivorRatio是eden和survior大小比例。
一般而言,server端的app會有以下規則:
首先決定能分配給vm的最大的heap size,然後設定最佳的young generation的大小;
如果heap size固定後,增加young generation的大小意味著減小tenured generation大小。讓tenured generation在任何時候夠大,能夠容納所有live的data(留10%-20%的空餘)。
經驗&&規則
年輕代大小選擇
響應時間優先的應用:盡可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇).在此種情況下,年輕代收集發生的頻率也是最小的.同時,減少到達年老代的對象.
吞吐量優先的應用:盡可能的設置大,可能到達Gbit的程度.因為對響應時間沒有要求,垃圾收集可以並行進行,一般適合8CPU以上的應用.
避免設置過小.當新生代設置過小時會導致:1.YGC次數更加頻繁 2.可能導致YGC對象直接進入舊生代,如果此時舊生代滿了,會觸發FGC.
年老代大小選擇
響應時間優先的應用:年老代使用並發收集器,所以其大小需要小心設置,一般要考慮並發會話率和會話持續時間等一些參數.如果堆設置小了,可以會造成內存碎 片,高回收頻率以及應用暫停而使用傳統的標記清除方式;如果堆大了,則需要較長的收集時間.最優化的方案,一般需要參考以下數據獲得:
並發垃圾收集信息、持久代並發收集次數、傳統GC信息、花在年輕代和年老代回收上的時間比例。
吞吐量優先的應用:一般吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代.原因是,這樣可以盡可能回收掉大部分短期對象,減少中期的對象,而年老代盡存放長期存活對象.
較小堆引起的碎片問題
因為年老代的並發收集器使用標記,清除演算法,所以不會對堆進行壓縮.當收集器回收時,他會把相鄰的空間進行合並,這樣可以分配給較大的對象.但是,當堆空間較小時,運行一段時間以後,就會出現"碎片",如果並發收集器找不到足夠的空間,那麼並發收集器將會停止,然後使用傳統的標記,清除方式進行回收.如果出現"碎片",可能需要進行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用並發收集器時,開啟對年老代的壓縮.
-XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這里設置多少次Full GC後,對年老代進行壓縮
用64位操作系統,Linux下64位的jdk比32位jdk要慢一些,但是吃得內存更多,吞吐量更大
XMX和XMS設置一樣大,MaxPermSize和MinPermSize設置一樣大,這樣可以減輕伸縮堆大小帶來的壓力
使用CMS的好處是用盡量少的新生代,經驗值是128M-256M, 然後老生代利用CMS並行收集, 這樣能保證系統低延遲的吞吐效率。 實際上cms的收集停頓時間非常的短,2G的內存, 大約20-80ms的應用程序停頓時間
系統停頓的時候可能是GC的問題也可能是程序的問題,多用jmap和jstack查看,或者killall -3 java,然後查看java控制台日誌,能看出很多問題。(相關工具的使用方法將在後面的blog中介紹)
仔細了解自己的應用,如果用了緩存,那麼年老代應該大一些,緩存的HashMap不應該無限制長,建議採用LRU演算法的Map做緩存,LRUMap的最大長度也要根據實際情況設定。
採用並發回收時,年輕代小一點,年老代要大,因為年老大用的是並發回收,即使時間長點也不會影響其他程序繼續運行,網站不會停頓
JVM參數的設置(特別是 –Xmx –Xms –Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold等參數的設置沒有一個固定的公式,需要根據PV old區實際數據 YGC次數等多方面來衡量。為了避免promotion faild可能會導致xmn設置偏小,也意味著YGC的次數會增多,處理並發訪問的能力下降等問題。每個參數的調整都需要經過詳細的性能測試,才能找到特定應用的最佳配置。
promotion failed:
垃圾回收時promotion failed是個很頭痛的問題,一般可能是兩種原因產生,第一個原因是救助空間不夠,救助空間里的對象還不應該被移動到年老代,但年輕代又有很多對象需要放入救助空間;第二個原因是年老代沒有足夠的空間接納來自年輕代的對象;這兩種情況都會轉向Full GC,網站停頓時間較長。
解決方方案一:
第一個原因我的最終解決辦法是去掉救助空間,設置-XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0即可,第二個原因我的解決辦法是設置為某個值(假設70),這樣年老代空間到70%時就開始執行CMS,年老代有足夠的空間接納來自年輕代的對象。
解決方案一的改進方案:
又有改進了,上面方法不太好,因為沒有用到救助空間,所以年老代容易滿,CMS執行會比較頻繁。我改善了一下,還是用救助空間,但是把救助空間加大,這樣也不會有promotion failed。具體操作上,32位Linux和64位Linux好像不一樣,64位系統似乎只要配置MaxTenuringThreshold參數,CMS還是有暫停。為了解決暫停問題和promotion failed問題,最後我設置-XX:SurvivorRatio=1 ,並把MaxTenuringThreshold去掉,這樣即沒有暫停又不會有promotoin failed,而且更重要的是,年老代和永久代上升非常慢(因為好多對象到不了年老代就被回收了),所以CMS執行頻率非常低,好幾個小時才執行一次,這樣,伺服器都不用重啟了。
-Xmx4000M -Xms4000M -Xmn600M -XX:PermSize=500M -XX:MaxPermSize=500M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log

值與Xmn的關系公式
上面介紹了promontion faild產生的原因是EDEN空間不足的情況下將EDEN與From survivor中的存活對象存入To survivor區時,To survivor區的空間不足,再次晉升到old gen區,而old gen區內存也不夠的情況下產生了promontion faild從而導致full gc.那可以推斷出:eden+from survivor < old gen區剩餘內存時,不會出現promontion faild的情況,即:
(Xmx-Xmn)*(1-/100)>=(Xmn-Xmn/(SurvivorRatior+2)) 進而推斷出:
<=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100
例如:
當xmx=128 xmn=36 SurvivorRatior=1時 <=((128.0-36)-(36-36/(1+2)))/(128-36)*100 =73.913
當xmx=128 xmn=24 SurvivorRatior=1時 <=((128.0-24)-(24-24/(1+2)))/(128-24)*100=84.615…
當xmx=3000 xmn=600 SurvivorRatior=1時 <=((3000.0-600)-(600-600/(1+2)))/(3000-600)*100=83.33
低於70% 需要調整xmn或SurvivorRatior值。

與jvmcms過程相關的知識