程序計數器,虛擬機棧,本地方法3個區域隨線程而生,隨線程而滅,棧中的棧幀隨着方法的進入和退出有條不紊地執行着出棧和入棧操做,每個棧幀中分配多少內存,基本上是在類結構肯定下來就已知。所以這幾個區域的內存分配和回收都具有肯定性。在這幾個區域就不須要考慮太多回收問題。垃圾收集器主要關注於Java堆和方法區。算法
首先說爲何要判斷是否存活,當垃圾收集器在對堆進行回收前,第一就是要肯定對象哪些是還在被引用的或者後面還須要被引用的,即存活,哪些是已經「死去」(即不可能再被任何途徑使用)性能
一、引用計數算法操作系統
在對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1,引用失效時就減1.任什麼時候刻計數器爲0的對象就是不可能再被使用的。這個方法效率挺高,大部分狀況下也是很不錯的算法。線程
可是在JVM中會很難解決對象之間相互循環引用的問題,就若是兩個對象之間相互調用,這時候就會發生相似死鎖的狀況,即這個地方相互調用會使得引用計數法始終認爲有對象在引用當前對象,就一直計數值大於或等於1,也就沒法通知GC收集器回收它們。可是實際的狀況是這兩個對象後面已經再也不調用,因此這個方法雖然簡單高效,但不是咱們的首選。虛擬機也不是經過這個算法來判斷對象是否存活的。cdn
二、可達性分析算法對象
使用一系列的GC Roots的對象(包括:虛擬機棧中引用的對象,方法區中類靜態屬性引用的對象,方法區中常量引用的對象,本地方法棧中JNI引用的對象)做爲起點,從節點開始向下搜索,當沒有被GCRoots連接到的對象就能夠回收,以下圖的對象4和5就判斷爲可回收對象。blog
在JDK1.2以後,Java對引用這個概念進行了擴充,也就是對象不只僅只有引用和沒有引用兩個概念,而是擴展到了4個:進程
強引用:相似於「Object obj=new Object()」只要強引用在,垃圾收集器永遠不會回收掉被引用的對象。內存
軟引用:是用來描述一些還有用可是並不是必需的對象,對於軟引用對象,在內存溢出異常以前,會把這些對象列進回收範圍之中進行第二次回收。虛擬機
弱引用,比軟引用更弱一點,被弱引用關聯的對象只能生存到下一次垃圾收集發生以前。當垃圾收集發生時不管內存是否足夠,都會只回收弱引用的對象。
虛引用,最弱的引用關係,對象是否有虛引用對其生存時間是沒有影響的。惟一目的就是能在這個對象被收集器回收時收到一個系統通知。
對象要想真正宣告「死亡」須要至少兩次的標記過程,當對象在可達性分析時候發現沒有被GC Roots鏈到那麼對象就會進行第一次標記而且進行第一次篩選,篩選的條件就是判斷該對象有沒有必要執行finalize()方法,須要執行的話就會把對象放入F-Queue的對列中去執行該對象中的finalize()方法。若是finalize()方法讓對象從新被GC Roots鏈到那麼對象就從新活下來,不然就會進行第二次標記,等待垃圾回收的到來
目前來講Java堆中的對象是分爲新生代和老年代,對於新生代中的對象採用的是複製算法清理
一、複製算法
它將可用內存空間劃分爲一塊較大的Eden空間和兩塊較小的From Survivor(S0)和To Survivor(S1)空間。每次使用時只使用Eden和其中一塊S區。好比此次使用的是S0區。回收時將Eden和S0區中的中還存活的對象一次性複製到S1中最後再清理Eden和S0中的對象,HotSpot虛擬機默認Eden:S0:S1之間大小比例是8:1:1,這是由於新生代中對象大多數甚至98%的都是「朝生夕死」。若是S區的大小不夠那麼就會依賴老年代的內存進行分配擔保。
二、對象重新生代變成老年代的斷定方法
每經歷一次Minor GC(複製算法回收對象)就會讓對象的年齡加一,當對象年齡爲15時就會把新生代的對象放入老年代中。
若是Survivor區中的存放不下的對象就會放入老年代中:對象會優先在Eden區中分配,然後經過一次Minor GC就讓對象進入Survivor區中,當Survivor區中存放不下該對象時就會將該對象放入老年代。
新生成的大對象也會直接放入老年代中(能夠經過-XX:+PretenuerSizeThreshold設置)超過這個size的對象一輩子成就會放入老年代。
三、標記—清理與標記—整理算法
在老年代中由於對象存活率高,沒有額外的空間對它進行分配擔保,因此會採用標記—清理或標記—整理算法來進行回收對象
標記—清理算法:首先標記出全部須要回收的對象,在標記完成以後統一回收全部標記的對象
標記—整理算法:先標記全部可回收對象,讓存活的對象向一端移動,而後直接清理掉端邊界之外的內存
上面的標記過程都是根據可達性分析算法中對象標記斷定來實現的。
上面介紹了這麼多,那咱們到底怎麼操做裏面的一些參數呢?
-Xms:初始堆大小,JVM 啓動的時候,給定堆空間大小。
-Xmx:最大堆大小,JVM 運行過程當中,若是初始堆空間不足的時候,最大能夠擴展到多 少。
-Xmn:設置年輕代大小。整個堆大小=年輕代大小+年老代大小+持久代大小。持久代一 般固定大小爲 64m,因此增大年輕代後,將會減少年老代大小。此值對系統性能影響較大, Sun 官方推薦配置爲整個堆的 3/8。
-Xss:設置每一個線程的 Java 棧大小。JDK5.0 之後每一個線程 Java 棧大小爲 1M,之前每 個線程堆棧大小爲 256K。根據應用的線程所需內存大小進行調整。在相同物理內存下,減 小這個值能生成更多的線程。可是操做系統對一個進程內的線程數仍是有限制的,不能無限生成。
-XX:NewSize=n:設置年輕代大小
-XX:NewRatio=n:設置年輕代和年老代的比值。如:爲 3,表示年輕代與年老代比值爲 1: 3,年輕代佔整個年輕代+年老代和的 1/4
-XX:SurvivorRatio=n:年輕代中 Eden 區與兩個 Survivor 區的比值。注意 Survivor 區有兩個。 如:3,表示 Eden:Survivor=3:2,一個 Survivor 區佔整個年輕代的 1/5
-XX:MaxPermSize=n:設置持久代大小
-XX:MaxTenuringThreshold:設置垃圾最大年齡。若是設置爲 0 的話,則年輕代對象不經 過 Survivor 區,直接進入年老代。對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在 Survivor 區進行屢次複製,這樣能夠增長對象再年輕代 的存活時間,增長在年輕代即被回收的機率。