JVM會(huì)動(dòng)態(tài)地調(diào)整實(shí)際的年齡閾值,不過(guò)通過(guò)指定-XX:+MaxTenuringThreshold參數(shù)可以給該值設(shè)置一個(gè)上限。將-XX:+MaxTenuringThreshold設(shè)置為0則立即觸發(fā)對(duì)象提升,而不會(huì)復(fù)制到存活區(qū)中。在現(xiàn)代的JVM中,這個(gè)值默認(rèn)會(huì)被設(shè)置為15個(gè)GC周期。在HotSpot虛擬機(jī)中這也是該值的上限。
如果存活區(qū)的大小不足以存放所有的新生代存活對(duì)象,則會(huì)出現(xiàn)過(guò)早提升。
老生代
老生代的內(nèi)存空間的實(shí)現(xiàn)則更為復(fù)雜。老生代的空間通常都會(huì)非常大,里面存放的對(duì)象都是不太可能會(huì)被回收的。
老生代的GC比新生代的GC發(fā)生的頻率要少得多。由于老生代中的多數(shù)對(duì)象都被認(rèn)為是存活的,也就不會(huì)存在標(biāo)記-復(fù)制操作了。在GC中,這些對(duì)象會(huì)被挪到一起以減少碎片。老生代的回收算法通常都是根據(jù)不同的理論來(lái)構(gòu)建的。不過(guò)大體上都會(huì)分成如下幾步:
- 標(biāo)記可達(dá)對(duì)象,設(shè)置GC根對(duì)象可達(dá)的所有對(duì)象后的標(biāo)記位
- 刪除不可達(dá)對(duì)象
- 整理老生代空間的對(duì)象,將存活對(duì)象復(fù)制到老生代開(kāi)始的連續(xù)空間內(nèi)。
從以上描述中可知,為了避免過(guò)度碎片化,老生代的GC是明確需要進(jìn)行整理操作的。
持久代
在Java 8以前還有一個(gè)特殊的空間叫做持久代(Permanent Generation)。這是元數(shù)據(jù)比如類相關(guān)數(shù)據(jù)存放的地方。除此之外,像駐留的字符串(internalized string)也會(huì)被存放在持久代中。這的確給Java開(kāi)發(fā)人員帶來(lái)了不少麻煩事,因?yàn)楹茈y評(píng)估這究竟會(huì)使用到多少空間。評(píng)估不到位偏會(huì)拋出 java.lang.OutOfMemoryError: Permgen space 的異常。只要不是真的因?yàn)閮?nèi)存泄漏而引起的OutOfMemoryError異常,可以通過(guò)增加持久代空間的大小來(lái)解決這一問(wèn)題,比如下例中的把持久代最大空間設(shè)置為256MB:
java -XX:MaxPermSize=256m com.mycompany.MyApplication