JVM堆內(nèi)存被分為兩部分年輕代(Young Generation)和老年代(Old Generation)。
新生代是所有新對象產(chǎn)生的地方。當(dāng)年輕代的內(nèi)存空間被用完時,就會觸發(fā)垃圾回收。這個垃圾回收叫做Minor GC。新生代分為3個部分Eden區(qū)和Survivor區(qū)(FromSpace和ToSpace,兩個區(qū)域大小相同),新生代的大小可以通過-Xmn來控制,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例。
新生代的特點
大多數(shù)新建的對象都在Eden區(qū)
當(dāng)Eden區(qū)域被填滿時,就會執(zhí)行Minor GC。并把所有存活下來的對象轉(zhuǎn)移到其中一個survivor區(qū)
Minor GC同樣會檢查存活下來的對象,并把他們轉(zhuǎn)移到另一個survivor區(qū)。這樣在一段時間內(nèi),總會有一個空的survivor區(qū)
經(jīng)過多次GC周期后,仍然存活下來的對象會被轉(zhuǎn)移到老年代內(nèi)存空間。通常這是在新生代有資格提升到老年代前通過設(shè)定年齡閾值來完成的。
老年代內(nèi)存里包含了長期存活的對象和經(jīng)過多次Minor GC后依然存活下來的對象。通常會在老年代內(nèi)存被占滿時進行垃圾回收。老年代的垃圾收集叫做Major GC。Major GC會花費更多的時間。舊生帶占用大小為-Xmx值減去-Xmn對應(yīng)的值
永久代包含了JVM需要的應(yīng)用元數(shù)據(jù),這些元數(shù)據(jù)描述了在應(yīng)用里使用的類和方法。注意,永久代不是Java堆內(nèi)存的一部分,有一些JVM沒有這一代,主要存放敞亮及類的一些信息,默認(rèn)最小值為16MB,最大值為64MB,可以通過-XX:PermSize及-XX:MaxPermSize來設(shè)置最小值和最大值。
永久代存放JVM運行時使用的類。永久代同樣包含了Java SE庫的類和方法。永久代的對象在full GC時進行垃圾收集。
方法區(qū)是永久代空間的一部分,并用來存儲類型信息(運行時常量和靜態(tài)變量)和方法代碼和構(gòu)造函數(shù)代碼。
如果JVM實現(xiàn)支持,JVM內(nèi)存管理會為創(chuàng)建內(nèi)存池,用來為不變對象創(chuàng)建對象池。字符串池就是內(nèi)存池類型的一個很好的例子。內(nèi)存池可以屬于堆或者永久代,這取決于JVM內(nèi)存管理的實現(xiàn)。
運行時常量池是每個類常量池的運行時代表。它包含了類的運行時常量和靜態(tài)方法。運行時常量池是方法區(qū)的一部分。
Java棧內(nèi)存用于運行線程。它們包含了方法里的臨時數(shù)據(jù)、堆里其它對象引用的特定數(shù)據(jù)。你可以閱讀棧內(nèi)存和堆內(nèi)存的區(qū)別。
VM設(shè)置描述
-Xms設(shè)置JVM啟動時堆的初始化大小。
-Xmx設(shè)置堆最大值。
-Xmn設(shè)置年輕代的空間大小,剩下的為老年代的空間大小。
-XX:PermGen設(shè)置永久代內(nèi)存的初始化大小。
-XX:MaxPermGen設(shè)置永久代的最大值。
-XX:SurvivorRatio提供Eden區(qū)和survivor區(qū)的空間比例。比如,如果年輕代的大小為10m并且VM開關(guān)是-XX:SurvivorRatio=2,那么將會保留5m內(nèi)存給Eden區(qū)和每個Survivor區(qū)分配2.5m內(nèi)存。默認(rèn)比例是8。
-XX:NewRatio提供年老代和年輕代的比例大小。默認(rèn)值是2。
大多數(shù)時候,上面的選項已經(jīng)足夠使用了。但是如果你還想了解其他的選項,那么請查看JVM選項官方網(wǎng)頁。
JVM堆內(nèi)存主要被分為三塊,新生代、老年代、持久代。三代的特點不同,造就了他們所用的GC算法不同,新生代適合那些生命周期較短,頻繁創(chuàng)建及銷毀的對象,老年代適合生命周期相對較長的對象,持久代在Sun HotSpot中就是指方法區(qū)(有些JVM中根本就沒有持久代這中說法)。
Java垃圾回收會找出沒用的對象,把它從內(nèi)存中移除并釋放出內(nèi)存給以后創(chuàng)建的對象使用。Java程序語言中的一個最大優(yōu)點是自動垃圾回收,不像其他的程序語言那樣需要手動分配和釋放內(nèi)存,比如C語言。
垃圾收集器是一個后臺運行程序。它管理著內(nèi)存中的所有對象并找出沒被引用的對象。所有的這些未引用的對象都會被刪除,回收它們的空間并分配給其他對象。
一個基本的垃圾回收過程涉及三個步驟:
標(biāo)記:這是第一步。在這一步,垃圾收集器會找出哪些對象正在使用和哪些對象不在使用。
正常清除:垃圾收集器清會除不在使用的對象,回收它們的空間分配給其他對象。
壓縮清除:為了提升性能,壓縮清除會在刪除沒用的對象后,把所有存活的對象移到一起。這樣可以提高分配新對象的效率。
簡單標(biāo)記和清除方法存在兩個問題:
效率很低。因為大多數(shù)新建對象都會成為“沒用對象”。
經(jīng)過多次垃圾回收周期的對象很有可能在以后的周期也會存活下來。
上面簡單清除方法的問題在于Java垃圾收集的分代回收的,而且在堆內(nèi)存里有年輕代和年老代兩個區(qū)域。我已經(jīng)在上面解釋了Minor GC和Major GC是怎樣掃描對象,以及如何把對象從一個分代空間移到另外一個分代空間。
這里有五種可以在應(yīng)用里使用的垃圾回收類型。僅需要使用JVM開關(guān)就可以在我們的應(yīng)用里啟用垃圾回收策略。讓我們一起來逐一了解:
Serial GC(-XX:+UseSerialGC):Serial GC使用簡單的標(biāo)記、清除、壓縮方法對年輕代和年老代進行垃圾回收,即Minor GC和Major GC。Serial GC在client模式(客戶端模式)很有用,比如在簡單的獨立應(yīng)用和CPU配置較低的機器。這個模式對占有內(nèi)存較少的應(yīng)用很管用。
Parallel GC(-XX:+UseParallelGC):除了會產(chǎn)生N個線程來進行年輕代的垃圾收集外,Parallel GC和Serial GC幾乎一樣。這里的N是系統(tǒng)CPU的核數(shù)。我們可以使用 -XX:ParallelGCThreads=n 這個JVM選項來控制線程數(shù)量。并行垃圾收集器也叫throughput收集器。因為它使用了多CPU加快垃圾回收性能。Parallel GC在進行年老代垃圾收集時使用單線程。
Parallel Old GC(-XX:+UseParallelOldGC):和Parallel GC一樣。不同之處,Parallel Old GC在年輕代垃圾收集和年老代垃圾回收時都使用多線程收集。
并發(fā)標(biāo)記清除(CMS)收集器(-XX:+UseConcMarkSweepGC):CMS收集器也被稱為短暫停頓并發(fā)收集器。它是對年老代進行垃圾收集的。CMS收集器通過多線程并發(fā)進行垃圾回收,盡量減少垃圾收集造成的停頓。CMS收集器對年輕代進行垃圾回收使用的算法和Parallel收集器一樣。這個垃圾收集器適用于不能忍受長時間停頓要求快速響應(yīng)的應(yīng)用??墒褂?-XX:ParallelCMSThreads=n JVM選項來限制CMS收集器的線程數(shù)量。
G1垃圾收集器(-XX:+UseG1GC) G1(Garbage First):垃圾收集器是在Java 7后才可以使用的特性,它的長遠目標(biāo)時代替CMS收集器。G1收集器是一個并行的、并發(fā)的和增量式壓縮短暫停頓的垃圾收集器。G1收集器和其他的收集器運行方式不一樣,不區(qū)分年輕代和年老代空間。它把堆空間劃分為多個大小相等的區(qū)域。當(dāng)進行垃圾收集時,它會優(yōu)先收集存活對象較少的區(qū)域,因此叫“Garbage First”。你可以在Oracle Garbage-FIrst收集器文檔找到更多詳細信息。