關于數組動態擴容導致頻繁GC的問題,我還有話說
概述
通過上篇關于數組動態擴容導致頻繁GC的文章假笨說-又抓了一個導致頻繁GC的鬼--數組動態擴容大家或許GET到了這么一些點。
- List里新數組在新生代分配
- 通過老生代使用率達到了閾值觸發的CMS GC,會把新生代里的對象作為GC ROOT的一部分,從而阻止了那些byte數組被回收
- 通過-XX:+CMSScavengeBeforeRemark這個參數可以解決這個問題
那是否還想過這么一些問題呢?
- List里新數組是否可以在老生代分配?
- -XX:+CMSScavengeBeforeRemark該參數是否一定會觸發YGC?
接下來主要圍繞這兩個問題展開,算是對上篇文章的一個補充
新數組在哪里分配
老實說,如果之前線上碰到的那個問題新數組是在老生代分配的話,那就不會有上篇文章,更不會有這篇文章,但是到底有沒有可能在老生代分配呢?其實是有可能的。
上面的代碼是慢速路徑分配的代碼,先判斷是否應該到新生代分配
其中_pretenure_size_threshold_words的值是jvm參數PretenureSizeThreshold指定的,如果我們指定了這個值,那意味著如果我們單次要求分配的超過了這個值就想到老生代去分配,當然這個值默認是0,表示不會對對象的大小做check,都優先到新生代分配。
如果不到新生代分配,或者新生代分配不了,然后有判斷是否會到老生代分配的條件。
- 如果要分配的內存超過了eden大小,那毫無疑問只能到老生代分配了
- 如果GC_locker正在起作用,有線程正在通過JNI操作臨界內存,并且操作完之后會觸發一次gc的話,那先到old分配解燃眉之急。
- 如果上一次YGC效果并不好,比如晉升失敗,或者因為預測到上一次YGC可能是一次失敗的YGC而沒做YGC了等,那就直接到老生代分配吧!
所以新的數組分配還是有各種可能在老生代分配的,因為隨著數組的不斷擴容,數組也會變得越來越大,當大到某個程度,或者到上面的某個條件成立的時候,還是可能在老生代直接分配的。
那如果新數組是在老生代分配的話,那經過CMS GC就會將老生代里不可達的那個新數組給回收了,那就不存在新生代指向老生代的跨代引用,因而其實并不會發生這樣的問題。
CMSScavengeBeforeRemark一定能觸發YGC嗎
CMSScavengeBeforeRemark這個參數本意是希望在CMS GC remark之前做一次YGC,正常情況下其實是會做一次YGC的,這個參數的好處是如果YGC比較有效果的話是能有效降低remark的時間長度,可以簡單理解為如果大部分新生代的對象被回收了,那作為根的部分少了,從而提高了remark的效率。
但是,但是這個YGC一定會發生嗎?下面對CMS GC remark之前你看到的現象分為三種情況:
- 你壓根看不到YGC的日志
- 你可以看到YGC日志,同時能看到內存被回收了
- 你可以看到YGC日志,但是發現內存根本沒被回收
對于看不到GC日志的情況,可以肯定是沒有發生YGC,這種情況通過是因為上面提到的GC_locker導致的,有線程正在訪問臨界區的內存,訪問這些內存的時候是不允許發生GC的,因為他們正在直接操作內存,而GC是會對對象做遷移的。另外你可能平時還會觀察到一個非常奇怪的現象,偶爾你會看到有連續的兩次YGC,其中后面那一次你會看到新生代使用的內存其實非常少但是也觸發了一次YGC,其實就是因為GC_locker有補償GC的邏輯。
對于第二種情況,你看到了YGC日志,同時也發現內存被回收了,這個毫無疑問,就是真的做了一次正常的YGC。
對于第三種情況,其實可能并沒有做YGC,當然也不排除確實做了YGC,但是確實效果不好的情況,那什么情況下會不做YGC呢,我們看看下面在做YGC之前的代碼。
如果這個判斷成立,那就直接return了。
而collection_attempt_is_safe在ParNew下的實現如下
***一條相對比較關鍵,具體實現如下:
如果老生代可用的空間足以容得下之前的新生代平均晉升的size,或者容的下新生代現在使用的size,那說明是可以正常做YGC的,那接下來就會準備做YGC,但是如果上面的條件都不滿足,那就會認為這次YGC做起來會沒什么效果,或者比較危險,***不做,于是就會直接返回,但是這種情況下,YGC的日志還是照常會打的,你看到的現象就是YGC前后內存大小不變。
總結
還是總結下吧,針對動態數組擴容的問題,可以有兩種情況
- 如果新擴容的數組是在老生代的,如果該數組不可達了,那經過CMS GC是會回收數組里的內容的。
- 如果新擴容的數組是在新生代的,如果該數組不可達了,CMSScavengeBeforeRemark無法完全保證YGC能順利進行,如果真的做了YGC,那肯定可以回收掉數組里的不可達的那些byte數組,如果因為各種限制導致YGC并沒有做,那還是無法回收掉數組里面的內容。
【本文是51CTO專欄作者李嘉鵬的原創文章,轉載請通過微信公眾號(你假笨,id:lovestblog)聯系作者本人獲取授權】