最終目的是將內存中無用的對象回收掉。具體的斷定方法有:算法
注:一般狀況下,Xms和Xmx,-XX:PermSize和-XX:MaxPerSize都會設置爲同樣。sql
上面看不懂的參數不要深究,等下提到回過頭再來看,這裏只是將全部參數羅列出來方便查找。優化
JVM中,將對象在內存中分爲了三代:spa
爲何要分代勒,由於針對每一個年齡代,都有不一樣的垃圾回收算法,以及內存分配機制。若是將全部對象放在一塊兒,第一是會形成頻繁遍歷判斷回收的開銷,第二是會形成複製、移動的開銷,爲何會有複製、移動,由於回收內存必然會形成內存碎片,而內存碎片會致使空間浪費,因此必須經過複製、移動來清理隨便,使得空閒內存連續。線程
如上圖,至於年輕代爲何要如此分配,與特定的回收算法有關。3d
大部分對象剛建立的時候都會分配在年輕代的Eden區,只要年輕代空間不夠就會觸發MinorGC(只回收年輕代內存),minorGC採起復制算法進行回收,當JVM運行觸發第一輪minorGC時,會將eden區存活的對象先複製到一個suprivor區。而後刪除eden區對象,當觸發下一輪minorGC時,又把suprivor區和eden區的存活的對象轉移到另外一個suprivor區,而後刪除這兩個區的全部對象。依次類推。至於爲何要用複製算法,包括老年代的標記整理算法,這是考慮到了避免內存碎片。若是對象內存不連續,會形成不少的空間浪費。中間件
老年代的對象都是從年輕代根據必定的規則流轉過來的。 具體有幾類流轉方式:對象
超過指定年齡(參數-XX:MaxTenuringThreshold 配置,默認15),這裏年齡指的是沒有被垃圾回收,存活下來一次理解爲增長一歲。流轉到老年代。blog
大對象直接進入,超過參數指定字節數(-XX:PretenureSizeThreshold)設置的字節數的大對象會直接進入老年代,這是由於對象越大,複製開銷就越大。內存
動態年齡判斷規則進入,意思是不必定要到指定年齡再流轉到15,若是某一年齡以上的對象到達必定大小,也會提早進入老年代。當躲過一輪GC的對象加起來超過surrvivor區50%,如年齡1+年齡2+年齡n一直累加,直到年齡n的時候發現加起來超過了surrvivor空間的50%,則年齡n以上的對象直接進入老年代
minorGC發生時,suprivor區放不下,則全部存活對象轉移到老年代。這裏涉及一個老年代分配擔保規則,指的是每次MinorGC發生時,都會判斷老年代可用內存大不大於,年輕代存活對象內存之和,若是大於則直接進行minorGC,若是小於則要看參數XX:HandlePromotionFailure是否啓用(默認啓用),若是啓用則對老年代此次須要承載的轉移對象內存進行預估(取前面minorGC被轉移的平均內存大小),若大於則也進行MinorGC,若意料情況外轉移內存超出了老年代可用空間,則進行FullGC,若fullGC仍是不夠,則拋出OOM錯誤。FullGC是採起的標記整理算法,指的是移動存活對象,讓內存連續,而後刪除須要回收的對象,爲何使用標記整理?由於認爲老年代對象存活概率高,複製算法不划算。
永久代存放的是元數據信息,當類加載時,類元數據信息寫入永久代,fullGC時永久代數據被回收,回收條件是:該類全部實例對象全部已經從堆內存被回收,該類classLoader已經被回收,該類Class對象沒有任何引用。
附圖:
現一個日處理量上億數據的計算系統,不斷從Mysql和其餘數據中間件中提取數據進行計算處理。每分鐘執行500次數據提取和計算任務,每次任務處理耗時10秒,每次處理1萬條數據(每條數據20個字段),可是集羣部署,共5臺機器,1臺機器每分鐘處理100次任務,每臺機器是4核8G內配置,JVM分了4G,3G堆內存,1.5G年輕代,1.5G老年代。
咱們先來估算一下內存佔用:
實際生產環境是怎麼樣的勒?
每一個任務處理10S,意味着還有大概六分之一的數據應該存活,算200M,可是200M放不進Survivor區,因此會嘗試往老年代放,老年代如今大於1.2G因此直接放就是了。
此時老年代可用容量小於年輕代對象總內存,默認判斷老年代剩餘空間是否大於平均每次MinorGC轉移過來的老年代對象容量,這裏是大於,因此仍是繼續MInorGC。
此時會觸發FullGC清理老年代,因而老年代的對象被所有清理掉了:
此時一個Survivor區有200MB,每次MinorGC後都能存放的下存活對象,不用往老年代轉移(固然仍是有轉移,這只是避免了suprivor區太小被迫轉移的對象)。JVM優化的策略最核心的就是減小FullGC次數,由於掃描對象多了一個老年代和永久代、永久代標記算法略微複雜、老年代整理時因爲對象較多比較慢的緣由,FullGC效率是遠遠低於MinorGC的,通常時間是minorGC的10倍以上。