java技術體系中所提倡的自動內存管理最後能夠歸結爲自動化解決兩個問題:給對象分配內存以及回收分配給對象的內存,關於回收內存,上一篇已經說了不少JVM內部的垃圾收集器體系以及他運行的垃圾收集算法,如今咱們在探討一下給對象分配內存的時候須要注意的事項。java
對象的內存分配,大的講,就是在java heap上分配,對象主要分配在新生代的Eden空間上,若是啓用了本地線程緩衝分配,那麼將按照線程優先在TLAB上分配,少數狀況也可能直接分配在老年代。可是分配的原則不是百分之百固定的,還要取決你採用的那種垃圾收集器的二組合,以及虛擬機中內存設置的相關參數。
算法
1:對象優先在Eden上分配
數組
大多數狀況下,對象在新生代Eden區域中進行分配,檔Eden區域中沒有足夠的空間進行分配的時候,發起一次Minor GC
spa
注意提到的Minor GC和Full GC
線程
Minor GC:這就是新生代的GC,指的是新生的垃圾收集動做,因爲java對象大多具備朝生夕死的特性,因此Minor GC很是頻繁,通常回收速度也比較快。
對象
Full GC:指的是發生在老年代的GC,又被稱做Major GC,出現了Major GC,常常會伴隨這至少一次的Minor GC,但這也不是絕對的,通常來講Major GC的速度會比Minor GC慢十倍以上。內存
2:大對象直接進入老年代資源
所謂的大對象是指大量須要連續java內存空間的java對象,最典型的大對象就是那種很長的字符串或者數組。大對象對於虛擬機分配來講是個很壞的消息,可是也有更壞的,就是那種朝生夕死的大對象,常常會出現大對象容易致使內存還有很多空間的時候就提早出發垃圾收集機制以獲取足夠的連續空間來「安置」他們。字符串
虛擬機提供了一個參數,上一篇也講到過,就是-XX:PretenureSizeThreshold參數,讓大於這個參數的對象直接進入老年代分配,這樣的目的就是避免在Eden區以及兩個Survivor區之間發生大量的內存複製,這是由於新生代採用的複製算法,複製大量的生存對象會耗費不少資源。
虛擬機
3:長期存活的對象將進入老年代
虛擬機既然採用分代收集的思想來管理內存,那內存回收的時候就必須能識別那些對象放在新生代,那些對象放在老年代,爲了作到這點,虛擬機給每一個對象定義了一個對象年齡計數器,若是對象在Eden出生而且通過一次Minor GC後仍然存活,而且能被Survivor接納,年齡就加一歲,當他的年齡增長到必定程度的時候,默認是15,就會晉升到老年代,這個值能夠經過虛擬機的參數-XX:MaxTenuringThreshold來進行設置。
4:動態對象年齡斷定
爲了能更好的適應不一樣程序的內存狀況,虛擬機並不老是要求對象的年齡必須達到MaxTenuringThreshold才能晉升老年代,若是在Survivor空間中相同年齡全部對象大小綜合大於Survivor空間的一半,年齡大於等於該年齡的對象就能夠直接進入老年代,無語等到MaxTenuringThreshold中要求的年齡。
5:空間分配擔保:
這點就給借錢時候的保人相似,若是到時間你換不上,那麼保人要先把錢換上。
言歸正傳,在發生Minor GC的時候,虛擬機會檢測以前每次晉升到老年代的平均大小是否大於老年代的剩餘空間大小,若是大於,則改成直接進行一次Full GC,若是小於,則查看HandlePromotionFailure設置是否容許擔保失敗;若是容許,那隻會進行Minor GC,若是不容許,則也要進行一次Full GC.
因爲新生代採用複製收集算法,可是爲了保證內存的利用率,只是用其中一塊Survivor空間做爲輪換備份,所以當出現大量對象在Minor GC後任然存活的狀況下,就須要老年代進行分配擔保,讓Survivor沒法容納的對象直接進入老年代,與生活中貸款很相似,老年代要作這樣的擔保,前提是老年代自己還能夠容納這些對象,可是實際中通過Minor GC以後新生代到底會有多少對象存活下來,這個是不曉得的,因此只好去以前每一次回收晉升到老年代對象大小的一個平均值做爲經驗值,與老年代剩餘的空間大小作比較,決定是否進行Full GC來讓老年代騰出更多的空間。