什麼是垃圾?爲何要回收?java
在java中,當一個對象沒有被任何指針指向的時候,他就成爲了一個垃圾,也就應該對其進行回收,若是不對其進行回收就會形成內存泄露,甚至是內存溢出。程序員
垃圾回收分爲兩個階段垃圾標記和垃圾回收。算法
引用計算算法(java並未使用,不存在循環引用形成內存泄露)數據庫
對每個對象保存一個整型的引用計數器屬性,用於記錄對象被引用的狀況。當對象被任何一個對象引用就會對計數器加以,引用失效就減一,直到計數器爲0,就把該對象視爲垃圾,對其進行回收。網絡
該算法具備效率高,沒有回收延遲的特性,可是他卻增大了開銷,每次進行加減法也增大了時間複雜度,同時,他不能處理循環引用的問題。因此實際上java並未採用這樣的算法。多線程
可達性分析算法併發
以根對象集合爲起始點,按照從上到下的方式搜索根對象集合所鏈接的目標對象是否可達。內存中存活的對象都直接或者間接同根對象鏈接在一塊兒。不然,就認爲該對象應該被回收。jvm
那麼根元素集合包括哪些?ide
虛擬機棧中引用的對象。性能
本地方法棧內引用的對象。
方法區中靜態屬性引用的對象。
方法區中常量引用的對象。
全部被同步鎖持有的對象。
java虛擬機內部的引用。
對象的finalization機制
一個對象在被回收以前會執行finalization()方法,來進行資源釋放等操做。
雖然對象的finalization機制聽起來很高大上,可是咱們平時好像歷來沒有用過,沒有用過就對了,由於這是給垃圾回收調用的,並不須要程序員調用,並且也不容許程序員調用。由於finalization可能會致使對象復活,其次該機制發生的時間是由GC線程決定的,若是不發生GC就永遠不會發生這個機制,最後,若是這個機制使用不當,就會嚴重影響GC性能。
對象的三種狀態
可觸及狀態
可復活狀態
不可觸及狀態
虛擬機在斷定一個對象是否被回收的時候,須要進行兩次標記,第一次,判斷該對象從根對象集合開始有沒有引用鏈,,而後判斷對象是否須要執行finalization()方法,若是該對象沒有重寫finalization()方法,活着以前已經調用過該方法(這個方法只能被調用一次),那麼就認爲不須要執行,那麼該對象也就達到了第三種狀態。
若是該對象重寫了finalization()方法,且未執行過,那麼該對象就會被插入一個隊列中,由虛擬機自動建立一個低優先級的finalizer線程觸發其finalization()方法,進行第二次標記,若是方法中,對象忽然復活,就會將其移除即將回收的集合,達到第二個復活狀態。
標記清除算法
將全部具備引用執行的對象進行標記,對未標記的的對象進行清除。
因爲清除階段須要對全部對象進行遍歷,因此效率不高,並且清除的空間是碎片化的,須要維護一個空閒列表。
複製清除算法
對全部存活的對象不進行標記,而是直接將其複製到另一塊如出一轍大小的空間中去,而後對原來空間進行總體回收。這樣作的好處是沒有標記和清除過程,使得清除十分高效,複製過去的內存空間是連續的,不須要維護空閒表。可是須要兩倍的內存空間,並且對象的複製會到值棧中引用地址會發生變化。
標記壓縮算法
複製算法創建在存活對象少,垃圾比較多的狀況下。該算法就是在標記清除算法的基礎上進行了碎片整理,這樣就不須要維護空閒列表。可是效率比標記清除算法更低。
分代收集算法
按照不一樣的分區選擇不一樣的算法。在新生區,因爲對象具備朝生夕死,回收頻率高的特性,因此能夠採用回收效率較高的複製算法。而對於老年區,因爲對象的生命週期較長,並且空間比較大,回收頻率不高,咱們就能夠採用標記清除,標記壓縮算法或者混合使用。
增量收集算法
還是傳統的標記-清除算法和複製算法,只是經過經過分階段的方式處理線程衝突問題,容許垃圾回收線程分階段完成清理和複製。可是頻繁的進行線程切換會形成垃圾回收的總成本升高,系統的吞吐量降低。
分區算法
將堆空間劃分爲不少個小空間,根據目標的停頓時間,每次合理的回收若干個小區間,從而減小一次GC所產生的停頓。
當一個對象不被使用的時候,jvm會自動對其進行回收,可是若是出現一些意外狀況致使垃圾回收器沒有對其回收,這樣的現象,咱們就叫他內存泄露,簡單來講,就是對象不能被使用了,可是還不能回收掉,長期佔用必定的內存空間。那麼在哪些狀況下會產生內存泄露?
當存在一些複雜的指針指向,若是存在對象不使用了,因而不少指針都放開,可是某一個指針沒有放開,致使這些對象中。仍然有部分指針沒有斷開,而形成對象不能被回收。
另外,在單例模式中,單例的生命週期和應用程序的週期是同樣長的,因此單例程序中,若是持有對不對象的引用的狀況,那麼這個外部對象就不能被回收,就會致使內存泄露。
還有一些就是資源未關閉鏈接的行爲,數據庫鏈接,網絡鏈接,io操做等,就會致使對象不會被回收(主要是一些和外部資源有鏈接的對象)。
強引用
相似new出來的對象,只要引用還在就永遠不會進行回收。
Object o = new Object();
軟引用
若是出現內存不足就會對軟引用對象進行回收。
SoftReferencre<User> usersoftRef = new SoftReference<User>(new User("zero",18));
弱引用
生命週期僅存在於下一次垃圾回收以前,也就是隻要發生GC就必定會回收弱引用。
WeakReference<User> userweakreference = new WeakReference<User>(new User("zero",18));
虛引用(對象回收跟蹤)
虛引用顧名思義,就是形同虛設。與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。
應用場景是在單核CPU的狀況下使用一條收集線程去完成清理工做,在垃圾回收期間,其餘全部的工做線程都要停下來,直到垃圾回收線程完畢。在其特殊的工做場景下,他的回收效率是最高的,由於他不須要反覆進行多線程的切換。一般和Serial Old GC搭配工做。
採用並行回收的方式,其餘跟serial回收器十分相似。主要運用在多CPU的環境下。能夠和CMS GC以及 Serial Old GC搭配工做。
一個採用了複製算法,並行回收和stw機制的垃圾回收器。由此可知,該回收器的目標是注重吞吐量,
他和採用了標記-壓縮算法、並行回收、stw機制的Parallel Old 收集器搭配, 效果很是好。(是jdk8的默認垃圾回收器)
第一款能讓垃圾回收線程和用戶線程同時進行工做的回收器。他的關注點是儘量縮短垃圾回收時用戶線程的停頓時間,提升響應速度。
採用標記-清除算法,因此在垃圾回收以後會產生一些碎片空間,只能引用空閒列表的方式來執行後續的內存分配。
對CPU的資源也很敏感,在併發階段由於會佔用一部分線程,從而使程序變慢,總吞吐量也會下降。
沒法處理浮動垃圾。在第二階段的併發標記過程完成後,由於用戶線程的某些操做使得某些以前不是垃圾的對象變成了垃圾,可是第三階段的重寫標記只是進行對以前懷疑是垃圾的對象確認一下是不是真的垃圾,不會標記新產生的這部分浮動垃圾。也就不會回收這部分垃圾。
流程:
初始標記
併發標記
從新標記
併發清除
一個並行回收器,將堆內存劃分紅多個不想關的區域(物理上不連續),不一樣的區域表示不一樣的堆分區(Eden,s0,s1,old),這樣作的目的是避免在堆中作全區域的回收,經過跟蹤每一個區域的回收價值(回收得到的空間大小以及所需時間比例)維護一個優先級列表,根據容許回收的時間,選擇回收價值大的區域進行回收。
特色:
並行性
併發性
分代收集
空間整合
可預測的停頓時間模型
回收環節:
年輕代GC
老年代併發標記
混合回收