国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

揪出一個導致GC慢慢變長的JVM設計缺陷

開發 開發工具
Java 堆分為新生代和老生代,YGC 其實就是針對新生代的垃圾回收,對象都是優先在新生代分配的,因此當新生代內存不夠分配的時候就會觸發垃圾回收,正常情況下可能觸發一次 YGC 就可以解決問題并正常分配的,當然也有極端情況可能要進行大掃除,對整個堆進行回收,也就是我們說的 Full GC,這種情況發生就會比較悲劇了。

今天要給大家分享的內容和 YGC(Young GC)有關,是我最近碰到的一個案例,希望將排查思路分享給大家,如果大家后面碰到類似的問題,可以直接作為一個經驗來排查。

我之前里寫過幾篇 YGC 的文章,也許其中有人已經看過了,沒看過的可以去看看,那兩個坑在這里就不再描述,大家可以直接當經驗使用。

Java 堆分為新生代和老生代,YGC 其實就是針對新生代的垃圾回收,對象都是優先在新生代分配的,因此當新生代內存不夠分配的時候就會觸發垃圾回收,正常情況下可能觸發一次 YGC 就可以解決問題并正常分配的,當然也有極端情況可能要進行大掃除,對整個堆進行回收,也就是我們說的 Full GC,這種情況發生就會比較悲劇了。

這里再提一下,YGC 也是會 STW(stop the world) 的,也就是會暫停整個應用,不要覺得 YGC 發生頻繁不是問題。

說實話我比較不喜歡排查 YGC 的問題,因為 YGC 的日志太簡單了,正常情況下只能知道新生代內存從多少變到了多少,花了多長時間,再無其它信息了。

所以當有人來咨詢為什么我的程序 YGC 越來越長的問題的時候,我其實是抗拒的,不過也無奈,總得嘗試去幫人家解決,包括前面說的那兩個問題,也是費了不少精力查出來的,希望大家珍惜。。。

有些時候你越想逃避,偏偏就會找上你,YGC 的問題最近說實話找我的挺多的,不過有好些都是踩過的坑,所以能順利幫人家解決,但是今天要說的這個問題是之前從未碰到過的,是一個全新的問題,所以也費了我不少精力,也因為其他問題要查被拖延了幾天。

這個問題最終排查下來其實是 JVM 本身設計上面的一個缺陷,我改天也會提到 openjdk 社區去和大家一起討論下這個設計,希望能徹底***這個問題。

這個問題現象也很明顯,就是發現 YGC 的時間越來越長,從 20ms 慢慢增加到100ms+,甚至還一直在漲。

不過這個增長過程還是挺緩慢的,其實 YGC 時間在幾十毫秒我個人認為算正常現象,沒必要去深究,再說了還是經過壓測了一個晚上才漲上來的,所以平時應該也不是啥問題吧,不過這次正巧趕上年中大促,所以大家對時間也比較敏感,便接手來排查這個案例了。

首先排除了之前碰到的幾種情況,然后我要同事加了一個我們 alijdk 特定的參數,可以打印 YGC 過程里具體各個階段的耗時情況,可惜的是并沒有找出問題,因為我們漏掉了一些點,導致沒有直接定位出來。

于是我懷疑那些沒跟蹤到的邏輯,首先懷疑的就是引用這塊的處理,所以叫同事加上了 -XX:+PrintReferenceGC 這個參數,這個參數會打印各種引用的處理時間,大概如下:

點擊下面圖片進入小程序查看PrintReferenceGC參數詳情

從當時的那個日志里,我發現了一個現象,就是隨著 YGC 時間的增長,JNI Weak Reference 的處理耗時也在不斷增長,所以基本就定位到了 YGC 增長的直接原因。

JNI Weak Reference 到底是什么呢?大家都知道 Java 層面有各種引用,包括 SoftReference,WeakReference 等,其中 WeakReference 可以保證在 GC 的時候不會阻礙其引用對象的回收,同樣的在 native 代碼里,我們也可以做類似的事情,有個叫做 JNIHandles::make_weak_global 的方法來達到這樣的效果。

于是我開始修改 JVM,嘗試打印一些信息出來,不知道大家注意過,我們 dump 線程的時候,使用 jstack 命令,***一條輸出里會看到類似 JNI global references: 328 的日志,這里其實就是打印了 JNI 里的兩種全局引用總數,分別是 _global_handles 和 _weak_global_handles。

于是嘗試將這兩種情況分開來,看具體哪種有多少個,于是改了***個版本,從修改之后的輸出來看,_global_handles 的引用個數基本穩定不變,但是 _weak_global_handles 的變化卻比較明顯。

至此也算佐證了 JNI Weak Reference的問題,于是我想再次修改 JVM,打印了這些 JNI Weak Reference 引用的具體對象是什么對象。

在每次我執行 jstack 時,就會順帶把那些對象都打印出來,當然那個時候是為了性能,畢竟程序還跑在線上,不敢動太大,比如要是大量輸出日志不可控,那就麻煩了,所以就借助 jstack 來手動觸發這個邏輯。

從輸出來看,看到了大量的下面的內容:

于是詢問同事是不是存在大量的 Java 對 JavaScript 的調用,被告知確實有使用,那問題點基本算定位到了,我馬上要同事針對他們的用法寫一個簡單的 demo 出來復現下問題。

沒想到很快就寫好,而且真的很容易復現,大概邏輯如下:

于是我開始 debug,最終確認和上面的 demo 完全等價于下面的 demo。

所以大家直接運行上面的 demo 就能復現問題,JVM 參數如下:

  1. -Xmx300M -Xms300M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintReferenceGC 

對了,運行平臺是 JDK 8,JDK 6 是不存在這個問題的,因為 invokedynamic 指令以及 nashorn 是在 JDK 6 里不存在的。

上面的 demo 看起來是不是沒毛病,但是卻真的會讓你的 GC 越來越慢,通過對 JVM 進行 debug 的方式抓出了下面的類似堆棧。

在 JDK 層面的棧如下:

最上面的 resolve 方法是一個 native 方法,這個方法發現可以直接調用到上面提到的 JNIHandles::make_weak_global 方法。

JNIHandles::make_weak_global 方法其實就是創建了一個 JNI Weak Reference。

在這里我要稍微描述下了,因為太繁瑣就不準備貼代碼了。

JVM 里有個數據結構叫做 JNIHandleBlock,之前提到了 global_handles 和 _weak_global_handles,其實他們都是一個 JNIHandleBlock 鏈表。

可以想象下里面有個 next 字段鏈到下一個 JNIHandleBlock,同時里面還有一個數組 _handle[],長度是 32,當我們要分配一個 JNI Weak Reference 的時候,就相當于在這個 JNIHandleBlock 鏈表里找一個空閑的位置(就是那些 _handle 數組),如果發現每個 JNIHandleBlock 的 _handle 數組都滿了,就會創建一個新的 JNIHandleBlock,然后加到鏈里,注意這個鏈可以***長,所以問題就來了,假如我們上層代碼不斷觸發底層調用 JNIHandles::make_weak_global 來創建一個 JNI Weak Reference,那是不是意味著這個 JNIHandleBlock 鏈會不斷增長,那會不會無窮增長呢,答案是肯定的,既然有創建 JNI Weak Reference 的 API,是不是也存在銷毀 JNI Weak Reference 的 API?

當然是存在的,可以看到有 JNIHandles::destroy_weak_global 方法,這個實現其實很簡單,就是相當于設計一個標記,表示這個數組里的這個位置是可以重用的了,在 GC 發生的時候,如果發現這個坑被標記了,于是就將這個坑加入到一個 free_list 里,當我們下面再想要分配一個 JNI Weak Reference 的時候,就可以有機會從 free_list 里去分配一個重用了。

但是這個 api 是在什么情況下才能調用的呢,其實只有在類卸載的時候才會去調用這個 api,那到底是什么類被卸載了,那就是調用了 MethodHandles.lookup() 這個方法的那個類,從我們上面的 demo 來看,就是 MHTest 這個主類本身,從同事給我的 demo 來看,其實是 jdk.nashorn.internal.runtime.Context 這個類,但是這個類其實是被 ext_classloader 加載的,也就是說這個類根本就不會被卸載,不能卸載那問題就嚴重了,意味著 GC 發生的時候并不能將那些引用對象已經死掉的坑置空,這樣在我們需要再次分配 JNI Weak Reference 的時候,沒有機會來重用那些坑,最終的結果就是不斷地創建新的 JNIHandleBlock 加到鏈表里,導致鏈表越來越長,然而 GC 的時候是會去不斷掃描這個鏈表的,因此看到 GC 的時候也會越來越長。

那還有一個問題,假如說調用 MethodHandles.lookup() 的類真的被卸載了還存在這個問題嗎,答案是 GC 時間不會再惡化了,但是之前已經達到的惡化結果已經無法再修復了。

所以,這算是一個 JVM 設計上的缺陷吧,只要 Java 層面能觸發不斷調用到JNIHandles::make_weak_global,那這個問題將會立馬重現。

其實解決方案我也想了一個,就是在遍歷這些 JNIHandleBlock 的時候,如果發現對應的_handle數組全是空的話,那就直接將 JNIHandleBlock 回收掉,這樣在 GC 發生的過程中并不會掃描到很多的 JNIHandleBlock 而耗時掉。

至于同事的那個問題的解決方案,其實也簡單,對于同一個 JavaScript 腳本,不要每次都去調用 eval 方法,可以緩存起來,這樣就減少了不斷去觸發調用 JNIHandles::make_weak_global 的動作從而可以避免 JNIHandleBlock 不斷增長的問題。

【本文是51CTO專欄作者李嘉鵬的原創文章,轉載請通過微信公眾號(你假笨,id:lovestblog)聯系作者本人獲取授權】

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2017-04-17 11:07:19

GC數組動態擴容

2015-06-15 12:30:10

Hadooplong編碼剖析

2009-10-27 09:05:44

Windows 7進程查看

2011-06-30 16:10:01

JavaScript

2021-11-12 08:07:31

SQL緩存RabbitMQ

2020-07-29 15:01:50

JVMGCJDK

2023-03-29 16:31:09

2015-07-29 10:28:59

JVM參數配置參數

2012-08-16 10:43:10

GC

2021-09-11 19:00:54

Intro元素MemoryCache

2018-11-22 14:09:45

iOS架構組件開發

2013-07-01 11:01:22

API設計API

2011-11-01 16:57:22

iOS 5蘋果iPhone4s

2017-09-26 16:32:03

JavaGC分析

2021-04-12 09:36:14

JVM生產問題JVM FULL GC

2024-04-24 10:38:22

2020-09-22 07:50:23

API接口業務

2021-05-28 18:12:51

C++設計

2024-11-20 13:18:21

2024-09-14 14:14:26

Dubbo框架微服務
點贊
收藏

51CTO技術棧公眾號

精品视频中文字幕| 夜夜嗨av一区二区三区网页 | 91在线三级| 亚洲最大成人网4388xx| 欧美视频不卡中文| 日本女优北野望在线电影| 国产精品视频一区二区三区四蜜臂| 91看片淫黄大片一级| 国产精品九九九| av影片在线| 欧美丝袜一区二区| 成人在线电影网| 免费成人美女在线观看| 国产欧美 在线欧美| 91麻豆精品激情在线观看最新| 亚洲欧洲一区二区三区在线观看| 秋霞午夜理伦电影在线观看| 岛国av在线不卡| 波多野结衣av在线| 亚洲美女屁股眼交3| 超碰在线播放91| 久久久精品免费网站| 韩国理伦片一区二区三区在线播放| 欧美另类在线播放| 国产成人毛片| 亚洲视频自拍偷拍| 不卡福利视频| 亚洲男女性事视频| 欧美va视频| 综合久久五月天| 色综合.com| 欧美理论电影在线播放| 欧美高清一级片| 2019中文字幕在线免费观看| 一区二区三区韩国免费中文网站| 日本精品在线视频| 五月天久久777| 精品国产日韩欧美| 热99在线视频| 国产九一精品| 动漫一区二区在线| 国产成人一区在线| 男人的天堂久久| 久久久综合免费视频| 免费看污污视频| 日韩精品欧美精品| 日韩三级电影免费观看| 国产一区二区三区在线观看免费视频| 亚洲国产精品影视| 91丨国产丨九色丨pron| 91欧美视频在线| 偷拍亚洲欧洲综合| 黄色网页网址在线免费| 精品视频在线播放色网色视频| julia一区二区三区中文字幕| 欧美激情喷水视频| 国产精品x453.com| 麻豆成人在线播放| 国产成人免费高清| 成人www视频网站免费观看| 亚洲一区二区成人在线观看| 亚洲欧美视频一区二区| 亚洲乱码国产乱码精品精天堂| 精品国产亚洲日本| 国产在线拍偷自揄拍精品| 手机精品视频在线观看| 日本老熟妇毛茸茸| 欧美色视频在线| 成人精品电影在线| 日本久久久久久久久| 国产精品免费看| 亚洲精品美女在线| 国产精品一区二区美女视频免费看| 亚洲欧美日韩精品| 国产另类在线| 国产精品免费一区二区三区| 久久国产精品99久久人人澡| 手机在线看福利| 在线视频你懂得一区二区三区| 密臀av在线播放| 欧美综合一区第一页| 日韩黄色免费网站| 成人h动漫在线| 91精品国产麻豆国产自产在线| 久久精品超碰| 超碰97在线资源| 久久婷婷色综合| 1024免费在线视频| 久久成人一区二区| 欧美体内she精视频在线观看| 成人性生活视频免费看| 色哟哟国产精品免费观看| 成人在线免费| 精品国产一区二区三区麻豆免费观看完整版| av亚洲精华国产精华精华| 蝌蚪视频在线播放| 欧美日韩国产第一页| 爽好多水快深点欧美视频| 特黄特色特刺激视频免费播放 | 最新在线地址| 在线观看精品自拍私拍| 中文精品久久| 国产精品一区二区羞羞答答| 日韩亚洲欧美综合| 日韩精品一区二区久久| 欧美性大战久久久久xxx| 欧美一区二区三区四区久久| 日韩av三区| 蜜臀av色欲a片无码精品一区| 欧美男人的天堂一二区| 精品国产视频| 天天操天天爽天天射| 日韩毛片在线看| 99精品久久| 久草在线资源视频| 午夜精品蜜臀一区二区三区免费 | 波多野结衣xxxx| 亚洲欧洲日产国产网站| 尹人成人综合网| 免费观看的av网站| 久久久爽爽爽美女图片| www.日韩av| 天天免费亚洲黑人免费| 日韩精品久久一区二区三区| 在线免费亚洲电影| 欧州一区二区| 欧美精品一区二区在线观看| 精品不卡一区| 四虎4hutv紧急入口| 欧美xxxx18性欧美| 成人激情文学综合网| 小草在线视频免费播放| 亚洲国产欧美不卡在线观看| 欧美情侣在线播放| 亚洲国产影院| 激情婷婷欧美| wwwav91| 最好看的2019的中文字幕视频| 91精品推荐| 999大胆视频| 欧美激情国内偷拍| 波多野结衣精品在线| 校园春色亚洲| 视频一区视频二区视频三区视频四区国产 | 欧美亚洲国产激情| 高清hd写真福利在线播放| 97国产suv精品一区二区62| 欧美激情综合在线| 久久大胆人体视频| 三年片观看免费观看大全视频下载| 久久久久成人网| 最好看的中文字幕久久| 国产乱码精品一区二区三区四区| 簧片在线观看| 国产日韩欧美亚洲一区| 精品久久久三级丝袜| 国内不卡的二区三区中文字幕| 四虎4545www国产精品| 成人在线免费在线观看| 91tv亚洲精品香蕉国产一区7ujn| 亚洲精选视频在线| 欧美不卡视频| 亚洲v.com| 成人xxx免费视频播放| 91亚洲精品一区| 亚洲福利视频二区| 国产欧美一区二区三区鸳鸯浴| 日本道不卡免费一区| 性欧美ⅴideo另类hd| 免费无码国产v片在线观看| 国产精品免费视频久久久| 欧美一区二区三区电影| 99久久免费视频.com| 日本a口亚洲| 操喷在线视频| 国产精品久久久久永久免费看| 国内不卡一区二区三区| 日韩天堂在线视频| 亚洲成人黄色影院| 久久99国产精品免费网站| 9l视频自拍蝌蚪9l视频成人| 人猿泰山h版在线观看| 免费在线一区二区| 久久人体大胆视频| 精品久久久一区| 亚洲精品影视| 国产在线不卡一区二区三区| 爱爱永久免费视频| 亚洲专区在线视频| 中文字幕日韩欧美在线| 亚洲欧洲美洲综合色网| 91精品国产自产在线观看永久∴| 欧美成人高清电影在线| aaa毛片在线观看| avlululu| 一女被多男玩喷潮视频| 免费裸体美女网站| mm131亚洲精品| 中文字幕av久久| 国产91在线亚洲|