怎麼判斷對象是否可以回收?
一:引用計數算法
在對象中添加一個引用計數器,每當有一個地方引用他時,計數器加1,當引用失效時,計數器減1,當計數器爲0時,就表明該對象已經不能被使用了
缺點是不能解決對象之間相互循環引用的問題
二:可達性分析法
通過一系列稱爲“GC roots”的對象作爲起始點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GCroots沒有任何引用鏈時,表明該對象是不可用的。
可作爲GCroots的對象:
1虛擬機棧(棧幀的本地變量表)引用的對象
2方法區中靜態屬性引用的對象
3方法區中常亮引用的對象
4Native方法引用的對象
判斷對象是否存活都與“引用”有關,對於一些對象,當內存空間足夠時,可保留在內存中,如果內存在進行垃圾回收以後還是非常緊張,則可以拋棄這些對象,
引用分爲四種:強引用,軟引用,弱引用,虛引用
強引用:普遍存在的new 一個object,只要強引用還在,垃圾收集器永遠不會收集被引用的對象
軟引用:描述一些還有用但不是必須的對象,當系統將要發生內存溢出異常之前,會將這些對象列入回收對象中進行第二次回收,如果這次回收還沒有足夠的內存,纔會報出內存溢出異常
弱引用:也是用來描述一些不是必須的對象,比軟引用要弱一些,只能存活到下一次垃圾收集發生之前,無論當前內存是否足夠,都會被回收掉。
虛引用:其目的只是在於在被虛引用標記的對象在被垃圾回收時能夠收到一條系統通知。
當對象已經沒有與GCroots相連的引用鏈時,會檢查該對象是否有必要執行finalize()方法,當這對象有必要執行執行finalize方法時,該對象會被放置在一個F-Quene隊列中,如果對象想要逃脫被回收的命運,只要重新與引用鏈上的任何一個對象關聯即可。並不提倡使用finalize()方法。
回收方法區
方法區的回收效率非常低,其回收主要是兩部分內容:廢棄常量和無用的類
廢棄常量:當一個字符已經進入常量池中,但是當前系統沒有任何一個對象引用該字符,那麼該字符就會被回收
無用的類需滿足以下條件
1該類的所有實例都已經被回收,也就是java堆中不存在任何該類的實例
2加載該類的classLoader已經被回收
3該類對應的java.lang.class對象沒有在任何地方被引用
垃圾收集算法
一 標記清除算法
首先標記出需要回收的算法,在標記完成後統一回收所有被標記的對象
缺點:標記和清除的過程效率都不高。
會產生大量的內存碎片空間,導致程序運行過程中需要分配較大對象時,因爲找不到足夠的連續內存而不得不提前出發另一次垃圾收集動作。
(二)複製算法
將內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊,當這一塊內存使用完以後,就將還存活着對象複製到另外一塊上面,然後在把已經使用內存空間一次清理掉。
現在虛擬機都採用複製算法,因爲新生代中98%的對象都是朝生夕死的,可將內存分爲一塊較大的Eden區和兩塊較小Survivor區,每次只是用Eden區和其中一塊Survivor區。
當回收時,將Eden區和Survivor區中還存活着的對象一次性複製到另外一塊Survivor上面,最後在清除掉Eden和Survivor空間,Eden和Survivor的大比例爲 8:1:1,我們沒有辦法保證每次回收都有不多於10%的對象存活,當Survivor空間不夠用時,需要依賴其他內存(老年代)進行分配擔保。
優點:不會產生內存碎片空間
缺點:代價較高,每次都要浪費一定大小的內存空間
標誌整理算法
複製收集算法在對象存活率較高的情況下,效率將會變得很低,標誌整理算法是將所有廢棄的對象坐上標記,然後將沒有標記的對象移到另一邊,在清空另一邊區域即可,老年代中一般使用標記整理算法。
分代收集算法
就是根據對象存活週期的不同將內存劃分爲幾塊,這樣就可根據各個年代的特點採用最適合的收集算法
新生代採用複製算法,老年代採用標記清除算法或標記整理算法。