YGC前后新生代变大?
問題描述
我們都知道gc是為了釋放內存,但是你是否碰到過ygc前后新生代反增不減的情況呢?gc日志效果類似下面的:
2016-05-18T15:06:13.011+0800: [GC [ParNew (promotion failed): 636088K->690555K(707840K), 0.2958900 secs][CMS: 1019739K->1019733K(1310720K), 2.6208600 secs] 1655820K->1655820K(2018560K), [CMS Perm : 205486K->205486K(262144K)], 2.9174390 secs] [Times: user=3.74 sys=0.01, real=2.91 secs]從上面的gc日志來看,我們新生代使用的是ParNew,而老生代用的是CMS GC,我們注意到ParNew的效果是新生代從636088K新增到了690555K,這是什么情況?
?
原理分析
要解釋這個問題,我們先要弄清楚YGC的過程,parNew是新生代的gc算法,簡單來說從gc roots開始掃描對象,當掃到一個只要是屬于新生代的對象就將其挪到to space,但是老的對象還不會做釋放,直到gc完成之后再看是否釋放老的對象(比如說上面我們看到了promotion failed的關鍵字,意味著晉升失敗了,也就是說to和old都裝不下新生代晉升來的對象,那么在這種情況下其實是不會對eden和from里的老對象做釋放的,盡管to space里已經可能存在一份副本了),但是在gc前后不管是否晉升成功,都會對from space和to space做一個對換,也就是原來的from變成to,原來的to變成from,再來看看打印gc前后內存變化的代碼
void GenCollectedHeap::print_heap_change(size_t prev_used) const {if (PrintGCDetails && Verbose) {gclog_or_tty->print(" " ?SIZE_FORMAT"->" SIZE_FORMAT"(" ?SIZE_FORMAT ")",prev_used, used(), capacity());} else {gclog_or_tty->print(" " ?SIZE_FORMAT "K""->" SIZE_FORMAT "K""(" ?SIZE_FORMAT "K)",prev_used / K, used() / K, capacity() / K);} }size_t GenCollectedHeap::used() const {size_t res = 0;for (int i = 0; i < _n_gens; i++) {res += _gens[i]->used();}return res; }size_t DefNewGeneration::used() const {return eden()->used()+ from()->used(); ? ? ?// to() is only used during scavenge }從上面代碼我們知道,gc之后的內存情況是used()方法返回的,其中新生代的used方法返回的是eden+from的內存,同樣的上面的prev_used也是這么計算的,只是發生在gc之前,這樣一來,根據我上面提到的情況,在gc之后不管是否成功都會做一次from和to的swap,那么gc之前新生代的使用大小,其實是gc之前eden+from的使用大小,而gc之后的新生代的使用大小,其實是eden+原來的to現在使用的大小,原來的to現在使用的大小其實就是在gc過程中將eden和from拷貝過來的對象所占的大小。
綜上分析你應該知道為什么會出現這種情況了,其實是一種特殊情況,只有在出現promotion failed的情況下才會發生這樣的情況,因為在這個情況下存在to里新增對象,而from和eden不會變化的情況。
總結
以上是生活随笔為你收集整理的YGC前后新生代变大?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何定位消耗CPU最多的线程
- 下一篇: 不可逆的类初始化过程