java 程序執行后 強制gc_GC 設計與停頓

(給ImportNew加星標,提高Java技能)

編譯:唐尤華

鏈接:shipilev.net/jvm/anatomy-quarks/3-gc-design-and-pauses/

1. 寫在前面

“[JVM 解剖公園][1]”是一個持續更新的系列迷你博客,閱讀每篇文章一般需要5到10分鐘。限于篇幅,僅對某個主題按照問題、測試、基準程序、觀察結果深入講解。因此,這里的數據和討論可以當軼事看,不做寫作風格、句法和語義錯誤、重復或一致性檢查。如果選擇采信文中內容,風險自負。

Aleksey Shipilёv,JVM 性能極客?

推特 [@shipilev][2]??

問題、評論、建議發送到 [aleksey@shipilev.net][3]

[1]:https://shipilev.net/jvm-anatomy-park

[2]:http://twitter.com/shipilev

[3]:aleksey@shipilev.net

?2. 問題

如果說垃圾回收是敵人,那么絕不能害怕,因為恐懼會讓人逐步死去直至徹底消亡。等等,這里究竟要討論什么問題?好吧,這里要討論的是,“在 `ArrayList` 中分配1億個對象會讓 Java ‘打嗝’“ 是真的嗎?

?3. 全貌圖

可以簡單地把性能問題歸罪于通用 GC,而真正的問題是對于實際工作負載 GC 的表現沒有達到預期。很多時候是工作負載本身有問題,其他情況則是使用了不匹配的 GC。請注意大多數回收器在其 GC 周期中是如何停頓的。

4. 實驗

對于“向 `ArrayList` 加入1億個對象”這個實驗,雖然不切實際且略顯搞笑,但在還是可以運行一下看看效果。下面是實驗代碼:

```java
import java.util.*;

public class AL {
static List l;public static void main(String... args) {
l = new ArrayList<>();for (int c = 0; c < 100_000_000; c++) {
l.add(new Object());
}
}
}
```

下面是來自奶牛的評論:

```shell
$ cowsay ...
________________________________________
/ 順便說一下,這是一個糟糕的 GC 基準測試 \
| 即使我是一頭奶牛,也能清楚地知道 |
\ 這一點。 /
----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```

盡管如此,即使一個糟糕的基準測試,只要仔細分析還是可以從運行結果中了解一些測試系統的有用信息。事實證明,在 OpenJDK 中選擇不同的回收器及其對應的 GC 設計,在這樣的負載下運行更能凸顯彼此之間的差異。

下面使用 JDK 9 + Shenandoah 垃圾回收器享受 GC 所有最新改進。在配置較低的 1.7 GHz i5 超極本運行 Linux x86_64 進行測試。要分配1億個16字節大小的對象,這里 heap 設為靜態 4GB 以消除不同回收器之間的自由度差異。

4.1 G1(JDK9 默認 GC)

```shell
$ time java -Xms4G -Xmx4G -Xlog:gc AL
[0.030s][info][gc] Using G1
[1.525s][info][gc] GC(0) Pause Young (G1 Evacuation Pause) 370M->367M(4096M) 991.610ms
[2.808s][info][gc] GC(1) Pause Young (G1 Evacuation Pause) 745M->747M(4096M) 928.510ms
[3.918s][info][gc] GC(2) Pause Young (G1 Evacuation Pause) 1105M->1107M(4096M) 764.967ms
[5.061s][info][gc] GC(3) Pause Young (G1 Evacuation Pause) 1553M->1555M(4096M) 601.680ms
[5.835s][info][gc] GC(4) Pause Young (G1 Evacuation Pause) 1733M->1735M(4096M) 465.216ms
[6.459s][info][gc] GC(5) Pause Initial Mark (G1 Humongous Allocation) 1894M->1897M(4096M) 398.453ms
[6.459s][info][gc] GC(6) Concurrent Cycle
[7.790s][info][gc] GC(7) Pause Young (G1 Evacuation Pause) 2477M->2478M(4096M) 472.079ms
[8.524s][info][gc] GC(8) Pause Young (G1 Evacuation Pause) 2656M->2659M(4096M) 434.435ms
[11.104s][info][gc] GC(6) Pause Remark 2761M->2761M(4096M) 1.020ms
[11.979s][info][gc] GC(6) Pause Cleanup 2761M->2215M(4096M) 2.446ms
[11.988s][info][gc] GC(6) Concurrent Cycle 5529.427ms
real 0m12.016s
user 0m34.588s
sys 0m0.964s
```

從 G1 運行結果中能觀察到什么?年輕代的停頓時間從500至1000毫秒不等。到達穩定狀態后停頓開始減少,啟發式方法給出了結束停頓需回收多少內存。一段時間后,會進入并發 GC 階段直到結束(請注意年輕代與并發階段重疊)。接下來應該還有“混合停頓”,但是 VM 已經提前退出。這些不確定的停頓是運行時間過長的罪魁禍首。

另外,可以注意到“user”時間比“real”(時鐘時間)要長。由于 GC 并行執行,而應用程序是單線程執行,因此 GC 會利用所有可用的并行機制從而讓收集時間變得比時鐘時間短。

4.2 Parallel

```shell
$ time java -XX:+UseParallelOldGC -Xms4G -Xmx4G -Xlog:gc AL
[0.023s][info][gc] Using Parallel
[1.579s][info][gc] GC(0) Pause Young (Allocation Failure) 878M->714M(3925M) 1144.518ms
[3.619s][info][gc] GC(1) Pause Young (Allocation Failure) 1738M->1442M(3925M) 1739.009ms
real 0m3.882s
user 0m11.032s
sys 0m1.516s
```

從 Parallel 結果中,可以看到類似的年輕代停頓。原因可能是調整 Eden 區或 Survivor 區的大小以容納更多臨時分配的內存。這里有兩次長停頓,完成任務總用時很短。當處于穩定狀態,回收器會保持相同頻率的長停頓。“user”時間同樣遠大于“real”時間,并發隱藏了一些 GC 開銷。

4.3 CMS(并發標記-清掃)

```shell
$ time java -XX:+UseConcMarkSweepGC -Xms4G -Xmx4G -Xlog:gc AL
[0.012s][info][gc] Using Concurrent Mark Sweep
[1.984s][info][gc] GC(0) Pause Young (Allocation Failure) 259M->231M(4062M) 1788.983ms
[2.938s][info][gc] GC(1) Pause Young (Allocation Failure) 497M->511M(4062M) 871.435ms
[3.970s][info][gc] GC(2) Pause Young (Allocation Failure) 777M->850M(4062M) 949.590ms
[4.779s][info][gc] GC(3) Pause Young (Allocation Failure) 1117M->1161M(4062M) 732.888ms
[6.604s][info][gc] GC(4) Pause Young (Allocation Failure) 1694M->1964M(4062M) 1662.255ms
[6.619s][info][gc] GC(5) Pause Initial Mark 1969M->1969M(4062M) 14.831ms
[6.619s][info][gc] GC(5) Concurrent Mark
[8.373s][info][gc] GC(6) Pause Young (Allocation Failure) 2230M->2365M(4062M) 1656.866ms
[10.397s][info][gc] GC(7) Pause Young (Allocation Failure) 3032M->3167M(4062M) 1761.868ms
[16.323s][info][gc] GC(5) Concurrent Mark 9704.075ms
[16.323s][info][gc] GC(5) Concurrent Preclean
[16.365s][info][gc] GC(5) Concurrent Preclean 41.998ms
[16.365s][info][gc] GC(5) Concurrent Abortable Preclean
[16.365s][info][gc] GC(5) Concurrent Abortable Preclean 0.022ms
[16.478s][info][gc] GC(5) Pause Remark 3390M->3390M(4062M) 113.598ms
[16.479s][info][gc] GC(5) Concurrent Sweep
[17.696s][info][gc] GC(5) Concurrent Sweep 1217.415ms
[17.696s][info][gc] GC(5) Concurrent Reset
[17.701s][info][gc] GC(5) Concurrent Reset 5.439ms
real 0m17.719s
user 0m45.692s
sys 0m0.588s
```

與一般看法相反,CMS 中的 “Concurrent”指年老代并發回收。正如結果中看到的,年輕代還是處于萬物靜止狀態。GC 日志看起來與 G1 類似:年輕代暫停,循環進行并發收集。區別在于,與 G1 “混合停頓”相比,“并發清掃”可以不間斷清掃不會造成應用停止。年輕代 GC 停頓時間越長影響了任務的執行性能。

4.4 Shenandoah

```shell
$ time java -XX:+UseShenandoahGC -Xms4G -Xmx4G -Xlog:gc AL
[0.026s][info][gc] Using Shenandoah
[0.808s][info][gc] GC(0) Pause Init Mark 0.839ms
[1.883s][info][gc] GC(0) Concurrent marking 2076M->3326M(4096M) 1074.924ms
[1.893s][info][gc] GC(0) Pause Final Mark 3326M->2784M(4096M) 10.240ms
[1.894s][info][gc] GC(0) Concurrent evacuation 2786M->2792M(4096M) 0.759ms
[1.894s][info][gc] GC(0) Concurrent reset bitmaps 0.153ms
[1.895s][info][gc] GC(1) Pause Init Mark 0.920ms
[1.998s][info][gc] Cancelling concurrent GC: Stopping VM
[2.000s][info][gc] GC(1) Concurrent marking 2794M->2982M(4096M) 104.697ms

real 0m2.021s
user 0m5.172s
sys 0m0.420s
```

[Shenandoah][4] 回收器中沒有年輕代,至少今天如此。也有一些不引入分代進行部分回收的設想,但幾乎不可能避免萬物靜止的情況。并發 GC 與應用同步啟動,初始化標記和結束并發標記引發了兩次小停頓。因為所有內容都處于活躍狀態沒有碎片化,所以并發拷貝不會引發停頓。第二次 GC 由于 VM 關閉過早結束了。由于沒有其它回收器那樣的長停頓,任務很快執行結束。

[4]:https://wiki.openjdk.java.net/display/shenandoah/Main

4.5 Epsilon

```shell
$ time java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms4G -Xmx4G -Xlog:gc AL
[0.031s][info][gc] Initialized with 4096M non-resizable heap.
[0.031s][info][gc] Using Epsilon GC
[1.361s][info][gc] Total allocated: 2834042 KB.
[1.361s][info][gc] Average allocation rate: 2081990 KB/sec

real 0m1.415s
user 0m1.240s
sys 0m0.304s
```

使用實驗性“no-op” [Epsilon GC][5] 不會運行任何回收器,有助于評估 GC 開銷。 我們可以準確地放入預先設定好的 4GB 堆,應用運行過程中沒有任何停頓。不過,發生任何突然的變化都導致程序結束。注意,“real”和“user” + “sys”的時間幾乎相等,這證實了應用只有一個線程。

*譯注:Epsilon GC 處理內存分配,但不實現任何實際的內存回收機制。一旦可用的Java堆耗盡,JVM就會關閉。*

[5]:http://openjdk.java.net/jeps/318

5. 觀察

不同的 GC 實現有著各自的設計權衡,取消 GC 可看作一種延伸的“壞主意”。通過了解工作負載、性能要求以及可用的 GC 實現,才能根據實際情況選擇合適的回收器。即使選擇不使用 GC 的目標平臺,仍然需要知道并選擇本機內存分配器。當運行實驗負載時,請試著理解運行結果并從中學習。祝你好運!

推薦閱讀

(點擊標題可跳轉閱讀)

JDK 11 將引入低延遲 GC,大幅度縮短 GC 暫停時長

減少 GC 開銷的 5 個編碼技巧

雜談 GC

看完本文有收獲?請轉發分享給更多人

關注「ImportNew」,提升Java技能

4f9e81d417368eb565310032199bd7fc.png

喜歡就點一下「好看」唄~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/259157.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/259157.shtml
英文地址,請注明出處:http://en.pswp.cn/news/259157.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

除BUG記

我負責一個模塊&#xff0c;功能比較簡單&#xff0c;就是測量環境溫、濕度&#xff0c;外加控制空調開/關、溫度設定。就是這么幾個功能&#xff0c;就反復試驗、修改&#xff0c;才達到穩定。在調試時&#xff0c;出現各種各樣的BUG&#xff0c;一些是編程時候出現的語法錯誤…

正則表達式語法(轉)

正則表達式是一種文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之間的字母&#xff09;和特殊字符&#xff08;稱為“元字符”&#xff09;。模式描述在搜索文本時要匹配的一個或多個字符串。 正則表達式示例 表達式匹配 /^\s*$/ 匹配空行。 /\d{2}-…

迎戰校招訓練題

一、雙空 編譯器可以根據硬件特性選擇合適的類型長度&#xff0c;但要遵循如下限制&#xff1a;short與int類型至少為___C___位&#xff0c;long至少為__D____位&#xff0c;并且short類型不長于int類型&#xff0c;int類型不得長于long類型。 A. 4 B.8 C.16 D. 32 E. 64…

【ASP.NET Web API2】初識Web API

Web Api 是什么&#xff1f; MSDN&#xff1a;ASP.NET Web API 是一種框架&#xff0c;用于輕松構建可以訪問多種客戶端&#xff08;包括瀏覽器和移動設備&#xff09;的 HTTP 服務 百度百科&#xff1a;Web API是網絡應用程序接口。 個人理解&#xff1a;Web API 是提供給多種…

三星s8怎么分屏操作_三星手機該怎么玩?了解完這幾點用機技巧,可以輕車熟路了!...

其實對于三星這個手機品牌&#xff0c;我還是很佩服的。雖然近些年來&#xff0c;三星在國內的市場份額日漸變少&#xff0c;但是在國內的影響力依然尚存。畢竟三星手機在某些方面還是很有優勢的&#xff0c;特別是旗艦系列機型深受消費者喜愛。接下來&#xff0c;筆者就跟大家…

關于條件編譯的問題

這兩天來忙活ucos-II在PIC18fxxx系列上的移植。在編譯的時候老出現變量被多重定義的錯誤。花費了一天的功夫才成功編譯通過&#xff0c;錯誤何在&#xff1f;&#xff1f;就是因為沒有搞明白條件編譯的原理&#xff0c;二是對mcc18編譯器的特點無知。下面學習條件編譯方面的知識…

二維數組的指針復習

最近一次的考試都是指針&#xff0c;真是給我深深上了一課&#xff0c;所以我特此復習一下指針方面的知識。二維數組的指針 int a[3][4] {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; 下面通過一個表來做詳細的說明&#xff1a; 訪問二維數組&#xff0c;有兩種方法&#xff0c;一…

稱重的問題

給你8顆小石頭和一架托盤天平。有7顆石頭的重量是一樣的&#xff0c;另外一顆比其他石頭略重&#xff1b;除此之外&#xff0c;這些石頭完全沒有分別。你不得假設那顆重頭到底比其他的石頭重了多少。請問&#xff1a;最少要稱量幾次&#xff0c;你才能把那顆較重的石頭找出來&a…

TIF圖像文件的讀取(c++代碼)

一 TIF圖像介紹 TIFF是最復雜的一種位圖文件格式。TIFF是基于標記的文件格式&#xff0c;它廣泛地應用于對圖像質量要求較高的圖像的存儲與轉換。由于它的結構靈活和包容性大&#xff0c;它已成為圖像文件格式的一種標準&#xff0c;絕大多數圖像系統都支持這種格式。 TIFF 是一…

g menu i meun_長沙話讀“這里”,到底是閣(gó)里還是該(gái)里

“帶籠子”、“打抱秋”……這些地道的長沙話&#xff0c;長沙人&#xff0c;你有多久沒聽過了&#xff1f;/ 長沙人&#xff0c;你還記得長沙話嗎 / “去了很多地方&#xff0c;最后還是回到了長沙”“我聽見了一句長沙話&#xff0c;就想回長沙了。”逗霸妹聽過很多人回長沙的…

git使用---工作區和暫存區

轉載于:https://www.cnblogs.com/momo-unique/articles/4380551.html

UC/OS-II的學習

粗略的的看了邵貝貝老師的那本書&#xff0c;感覺有點眉目。UC/OS-II的全局變量繁多&#xff0c;剛接觸的時候容易弄混淆&#xff0c;現在總結下&#xff1a; OSRunning&#xff1a; 用于標識多任務環境是否已經開啟運行&#xff0c;在OSStart()函數里啟動任務后就置為True。 …

偶數哥德巴赫猜想

已知不小于6的偶數都可以分成兩個素數之和。請編寫6到100000的所有偶數的分解&#xff0c;若有一個偶數可以分解成多個素數之和&#xff0c;只需寫出一種即可。 #include <iostream> #include <algorithm> using namespace std;bool isprime(int n)//判斷素數{int …

[20170420]表達式加0或者減0不一樣.txt

[20170420]表達式加0或者減0不一樣.txt --//oracle 有時候避免某個索引采用字段0或者-0的方式&#xff0c;不使用索引&#xff0c;但是兩者存在一點點區別&#xff0c;通過例子說明。 1.環境&#xff1a; SCOTTbook> &r/ver1 PORT_STRING VERSION …

MAPLAP開發環境中release模式和debug模式燒寫.hex文件的不同之處

昨天看了齊工的報告才知道release模式和debug模式燒寫.hex文件的不同。 三&#xff1a;問題分析 1. PIC系列的仿真器和集成開發環境的情況&#xff1a; Release模式和Debug模式是有區別的&#xff1b;Release模式是只把代碼燒錄到單片機的flash區內&#xff0c;上電執行&am…

JavaWeb -- Session實例 -- 自動登錄 和 防止表單重復提交(令牌產生器) MD5碼

1、 自動登錄 http://blog.csdn.net/xj626852095/article/details/16825659 2. 防止表單重復提交 表單Servlet //負責產生表單 public class FormServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletEx…

電腦常見故障處理_彩超常見故障及維修

彩超是醫學檢測手段中重要的環節之一&#xff0c;是對產婦以及對病人進行內部組織和結構觀察的重要方式之一&#xff0c;彩超應用得當可以及早的診斷出病人的疾病&#xff0c;為患者解除疾病的困擾。彩超設備是一種極為先進的診斷系統&#xff0c;一般彩超系統包括以下幾個部分…

微軟歷史最高市值是多少?

有人說微軟在1999 年 12 月達到股價歷史最高點 $58.38并不準確。我1999年12月22日增加微軟&#xff0c;公司依照1999年12月27日的價格&#xff08;119.125&#xff0c;拆股后變為59.5625&#xff09;給了我一筆期權&#xff0c;這個價格&#xff0c;成為微軟股價空前絕后最高點…

京東2016校招編程題

記得有一個大題&#xff0c;說的是給定一個n*n的矩陣&#xff0c;要求從1開始填充矩陣&#xff0c;最后的矩陣是蛇形的。即如下&#xff1a; n3, 7 8 1 6 9 2 5 4 3 n4, 10 11 12 1 9 16 13 2 8 15 14 3 7 6 5 4 給出代碼&#xff1a; #incl…

leetcode21

/*** Definition for singly-linked list.* public class ListNode {* public int val;* public ListNode next;* public ListNode(int x) { val x; }* }*/ public class Solution {public ListNode MergeTwoLists(ListNode l1, ListNode l2) {//遞歸實現鏈表合并…