通過關鍵字new創建java對象,即可視為java對象申請內存空間,JVM會在內存堆中為每個對象分配空間,當一個java對象失去引用時,JVM的垃圾回收機制會自動清除他們,并回收它們所占的內存空間。
是否回收一個對象的標準:是否還有引用變量引用該對象
JVM的垃圾回收機制采用有向圖方式來管理內存中的對象。對象在堆內存中的三種狀態: 1. 可達狀態:有一個以上的引用變量引用它, 在有向圖中可以從起始頂點導航到該對象, 程序可通過引用變量來調用該對象的屬性和方法 2. 可恢復狀態:某個對象不再被任何對象引用,從起始頂點不能導航到該對象,在回收該對象之前,系統會調用finalize方法進行資源清理,如果系統在調用finalizde后重新讓一個以上的引用變量引用該對象,則再次變為可達狀態,反之為不可達狀態 3.不可達狀態:對象的所有關聯被切斷,且調用finalize后仍為不可達,即對象永久失去引用
當某個對象被其他類的類變量引用時,只有該類被銷毀后該對象才會進入可恢復狀態;當某個對象被其他對象的實例變量引用時,只有當引用該對象的對象被銷毀或變為不可達之后才會能進入不可達狀態。
強引用:程序創建一個對象,并把這個對象賦給一個引用變量 軟引用:用SoftReference類加以實現,當系統空間足夠時不會被系統回收,當系統空間不足時會被回收 弱引用:通過WeakReference類實現,和軟引用很像,但弱引用的引用級別更低。當系統回收機制運行時,不管系統內存是否足夠,總會回收該對象所占用的內存。 虛引用:跟蹤對象被垃圾回收的狀態,程序可以通過檢查與虛引用關聯的引用隊列中是否已經包含指定的虛引用,從而了解虛引用所引用對象是否即將被回收。虛引用必須和引用隊列聯合使用。
內存泄漏:存在無用的額內存沒有被回收回來 java內存泄漏的原因: JVM肯定不會回收強引用所引用的java對象,即使系統內存非常緊張,即使該對象以后永遠不會被用到 產生的原因:所有不可達的對象都由垃圾回收機負責回收,因此程序猿不需考慮這部分的內存泄漏。但如果程序中的一些java對象處于可達狀態,但程序以后永遠都不會訪問他們,則占據的內存空間不會被回收,因此發生內存泄漏。
垃圾回收機制完成的兩件事: 1. 跟蹤并監控每個java對象,當某個對象處于不可達狀態時,回收該對象所占用的內存; 2. 清理內存分配、回收過程中產生的內存碎片
垃圾回收的設計算法: 1. 串行回收:始終使用一個CPU來執行垃圾回收操作 2. 并行回收:每個CPU負責回收工作的一部分,效率高,同時復雜度也在提升,內存碎片會增加 3. 應用程序停止:執行垃圾回收的同時會導致應用程序的暫停 4. 并發執行:不會導致應用程序停止,需解決和應用程序的執行沖突,開銷比應用程序停止的方法大,需更多的堆內存 5. 復制式:將所有可達對象復制到另一塊相同的內存中,然后一次性回收整個空間,不產生內存碎片,但需要額外的內存 6. 不壓縮式: 從根訪問所有可達對象并標記,再次遍歷內存區域,回收處理未標記的對象,內存利用率高,但需遍歷兩次堆內存,易產生碎片 7. 壓縮式:從根訪問所有可達對象并標記,將所有可達對象搬遷到一起,之前占用的內存全部回收
現行的垃圾回收器采用分代的方式來進行回收,根據對象生存時間的長短分為young、old、permanent,基于以下兩點事實: 1. 絕大多數的對象在young期間就會被回收; 2. 很老的對象和很新的對象很少存在相互引用
young代采用復制算法,因為大部分對象很快進入不可達狀態。只需遍歷那些處于可達狀態的對象,而且這些對象的數量較少,復制成本不大,因此可充分發揮復制算法的優點。 young代由1個Eden區和兩個Survivor區構成。絕大多數的對象先分配到Eden區,Survivor區中的對象至少在young代中經歷了一次垃圾回收,同一時間1個survivor用來保存對象,另一個是空的。復制就是將Eden和非空的survivor復制到另一個survivor
old代垃圾回收具有如下兩個特征: 1. old代垃圾回收的執行頻率無需太高,因此很少有對象會死掉 2. 每次對old代執行垃圾回收需要更長的時間來完成 基于以上考慮,會采用標記壓縮算法,可避免復制old代的大量對象,而且由于old代的對象不會很快死亡,回收過程在并不會產生大量的內存碎片
permanent代中的對象垃圾回收機制通常不會回收
總結: young代回收的系統開銷比較小,也被稱為次要回收。old代回收的成本較大,稱為主要回收。young代的內存會先被回收,對于old代的回收頻率則要低的多。
常見的垃圾回收器: 1. 串行回收器:young代和old代的回收都是串行的 2. 并行回收器:比串行回收器增加了多CPU并行的能力,即同時啟動多線程并行來執行垃圾回收,并行只針對young代的復制算法 3.并行壓縮回收器:會old代采用不同的算法(先劃分幾個固定大小的區域,mark階段多線程標記可達對象,summary階段從最左邊的區域檢查每個區域的密度,當檢測到可達密度較小時會對其和右側區域進行回收,comact階段利用多線程并行復制數據到密集區域) 4.并發標示-清理回收器(CMS):young代回收方式保持不變,CMS對多數old代回收為并發操作,可分為初始標識、并發標識和再標識。只需兩次短暫的暫停,適用于對實時性要求較高的程序,但需要的堆內存也是最大的
內存管理小技巧: 1. 直接采用直接量創建,不用new的方式來創建 2. 盡早釋放無用對象的引用:方法局部變量引用所引用的對象會隨著方法的結束變為垃圾,并很快被JVM回收。 3. 盡量少用靜態變量:類變量會存放到permanent代中,只要類對象存在就不會被垃圾回收 4. 避免在經常調用的方法、循環中創建java對象 5.緩存經常使用的對象:使用HashMap進行緩存,直接使用某些開源的緩存項目 6. 盡量不要使用finalize的方法 7. 考慮使用軟引用:當系統內存緊張時,系統會自動釋放軟引用所引用的對象
|
新聞熱點
疑難解答