1、內存管理簡介
內存管理的職責為分配內存,回收內存。
沒有自動內存管理的語言/平台容易發生錯誤。
典型的問題包括懸掛指針問題,一個指針引用了一個已經被回收的內存地址,導致程序的運行完全不可知。
另一個典型問題為內存洩露,內存已經分配,但是已經沒有了指向該內存的指針,導致內存洩露。
程序員要花費大量時間在調試該類問題上。
2、GC簡介
因此引入了Garbage Collector機制,由運行時環境來自動管理內存。
Garbage Collector解決了懸掛指針和內存洩露大部分的問題(不是全部)。
注意Garbage Collector(簡稱Collector)和Garbage Collection(簡稱GC)的區別。
3、Collector的職責:
分配內存。
保證有引用的內存不被釋放。
回收沒有指針引用的內存。
對象被引用稱為活對象,對象沒有被引用稱為垃圾對象/垃圾/垃圾內存,找到垃圾對象並回收是Collector的一個主要工作,該過程稱為GC。
Collector一般使用一個稱為堆的內存池來進行內存的分配和回收。
一般的,當堆內存滿或者達到一個閥值時,堆內存或者部分堆內存被GC。
4、好的Collector的特性
保證有引用的對象不被GC。
快速的回收內存垃圾。
在程序運行期間GC要高效,盡量少的影響程序運行。和大部分的計算機問題一樣,這是一個關於空間,時間,效率平衡的問題。
避免內存碎片,內存碎片導致占用大量內存的大對象內存申請難以滿足。可以采用Compaction技術避免內存碎片。Compaction技術:把活對象移向連續內存區的一端,回收其余的內存以便以後的分配。
良好的擴展性,內存分配和GC在多核機器上不應該成為性能瓶頸。
5、GC性能指標
Throughput: 程序時間(不包含GC時間)/總時間。
GC overhead: GC時間/總時間。
Pause time: GC運行時程序掛起時間。
Frequency of GC: GC頻率。
Footprint: a measure of size, such as heap size。
Promptness:對象變為垃圾到該垃圾被回收後內存可用的時間。
依賴於不同的場景,對於GC的性能指標的關注點也不一樣。
6、分代GC
分代GC把內存劃分為多個代(內存區域),每個代存儲不同年齡的對象。 常見的分為2代,young和old。
分配內存時,先從young代分配,如果young代已滿,可以執行GC(可能導致對象提升),如果有空間,則分配,如果young代還是沒有空間,可以對整個內存堆GC。
young代GC後還存活的對象可以提升到old代。
該機制基於以下觀察事實:
1 大部分新分配的對象很快就沒有引用了,變成垃圾。
2 很少有old代對象引用young代對象。
基於代內存存儲對象的特性,對不同代的內存可以使用不同的GC算法。
Young代GC需要高效,快速,頻繁的執行,關注點主要在速度上。
Old代由於增長緩慢,因此GC不頻繁,但是其內存空間比較大,因此,需要更長時間才能執行完GC。關注點在內存空間利用率上。
7、Java Collector
Jvm的內存分為3代。Young, Old, Permanent。
大部分對象存儲在Young代。
在Young代中經歷數次GC存活的對象可以提升到Old代,大對象也可以直接分配到Old代。
Permanent代保存虛擬機自己的靜態(refective)數據,例如類(class)和方法(method)對象。
Young代由一個Eden和2個survivor組成。大部分的對象的內存分配和回收在這裡完成。
Survivor存儲至少經過一次GC存活下來的對象,以增大該對象在提升至old代前被回收的機會。2個survivor中有一個為空。分別為From和to survivor。
當young代內存滿,執行young代GC(minor GC)。
當old或permanent代內存滿,執行full GC(major GC),所有代都被GC。一般先執行young GC,再執行old, permanent GC。
有時old代太滿,以至於如果young GC先運行,則無法存儲提升的對象。這時,Young GC不運行,old GC算法在整個堆上運行(CMS collector是個例外,該collector不能運行在young 代上)。