java中的分代垃圾回收策略

什麼是分代垃圾回收策略?

根據對象的生命週期的長短把對象分成不同的種類(年輕代,年老代和持久代)並分別進行內存回收,就是分代垃圾回收!值得注意的是,這種劃分對象的手段並不是自動進行的,而是伴隨着回收過濾進行的,也就是說年輕代與年老代之間的轉換是伴隨着對象回收的,只有經過了回收的洗禮後一些對象纔會被選中成爲年老代,而另外一些不幸的對象則早早地就被系統取走了小命。

爲什麼要運用分代垃圾回收策略?在java程序運行的過程中,會產生大量的對象,因每個對象所能承擔的職責不同所具有的功能不同所以也有着不一樣的生命週期,有的對象生命週期較長,比如Http請求中的Session對象,線程,Socket連接等;有的對象生命週期較短,比如String對象,由於其不變類的特性,有的在使用一次後即可回收。試想,在不進行對象存活時間區分的情況下,每次垃圾回收都是對整個堆空間進行回收,那麼消耗的時間相對會很長,而且對於存活時間較長的對象進行的掃描工作等都是徒勞。因此就需要引入分治的思想,所謂分治的思想就是因地制宜,將對象進行代的劃分,把不同生命週期的對象放在不同的代上使用不同的垃圾回收方式。

如何劃分?將對象按其生命週期的不同劃分成:年輕代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是類信息,所以與java對象的回收關係不大,與回收息息相關的是年輕代和年老代。

年輕代:年輕代又進一步可以劃分爲一個伊甸園(Eden)和兩個存活區(Survivor space)伊甸園是進行內存分配的地方,是一塊連續的空閒內存區域,在裏面進行內存分配速度非常快,因爲不需要進行可用內存塊的查找。新對象是總是在伊甸園中生成,只有經受住了一定的考驗後才能後順利地進入到存活區中,這種考驗是什麼在後面會講到。把存活區劃分爲兩塊,其實也是爲了滿足垃圾回收的需要,因爲在年輕代中經歷了“回收大劫”未必就能夠進入到年老代中。系統總是把對象放在伊甸園和一個存活區(任意的一個),在垃圾回收時,根據其存活時間被複制到另一個存活區或者年老代中,則之前的存活區和伊甸園中剩下的都是需要被回收的對象,只要對這兩個區域進行清除即可,兩個存活區是交替使用,循環往復,在下一次垃圾回收時,之前被清除的存活區又用來放置存活下來的對象了。一般來說,年輕代區域較小,而且大部分對象是需要進行清除的。

年老代:在年輕代中經歷了N次回收後仍然沒有被清除的對象,就會被放到年老代中,可以說他們都是久經沙場而不亡的一代,都是生命週期較長的對象。對於年老代和永久代,就不能再採用像年輕代中那樣搬移騰挪的回收算法,因爲那些對於這些回收戰場上的老兵來說是小兒科,而是採用一種稱爲“標記-清除-壓縮(Mark-Sweep-Compact)”的算法。標記的過程是找出當前還存活的對象,並進行標記;清除則是遍歷整個年老區,找到已標記的對象並進行清除;而壓縮則是把存活的對象移動到整個內存區的一端,使得另一端是一塊連續的空間,方便進行內存分配和複製。

持久代:用於存放靜態文件,比如java類、方法等。持久代對垃圾回收沒有顯著的影響。

什麼時候進行回收?由於對象進行了了分代處理,所以垃圾回收區域和時間也不一樣。回收(Gabage Colection )的類型有兩種:Scavenge GC和Full GC。


  1. Scavenge GC:當新對象生成,但在Eden申請空間失敗時就會觸發Scavenge GC,對Enden去進行GC,清除掉非存活的對象,並且把存活的對象移動到Survivor區中的其中一個區中。前面的提到考驗就是Scavenge GC,也就是說對象經過了Scavenge GC才能夠進入到存活區中。這種形式的GC只會在年輕代中進行,因爲大部分對象都是從Eden區開始的,同時Eden區不會分配得太大,所以對Eden區的GC會非常地頻繁。

  2. Full GC:對整個對進行整理,包括了年輕代、年老代和持久代。Full GC要對整個塊進行回收,所以要比Scavenge GC慢得多,因此應該儘可能減少Full GC的次數。在對JVM調優的過程中,很大一部分工作就是對Full GC的調節,如下原因可能觸發它的執行:

  • 年老代(Tenured)被寫滿

  • 持久代(Permanent)被寫滿

  • System.gc()被顯式調用

  • 自上一次GC之後Heap的各域分配策略動態變化



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章