JVM垃圾回收算法

1 概述

前面已經介紹過了,jvm運行時的數據區域包括程序計數器、虛擬機棧、本地方法棧、堆和方法區,其中程序計數器、虛擬機棧和本地方法棧是線程私有的,當方法結束或者線程銷燬時,這部分的內存就會被回收了,不用過多的考慮。所以,本章所講的主要是堆和方法區的內存分配與回收問題。

2 對象“死”了嗎

我們知道,堆裏面幾乎存放了java中所有的對象實例,垃圾收集器(GC)在對這部分內存進行回收時,首先需要判斷對象是否還存活着,然後將“死”的對象或者是沒有必要存活下去的對象進行回收。

2.1 引用計數算法

引用計數算法就是給每個對象添加一個計數器,當有地方引用它的時候,計數器就加1,當引用消失時,計數器就減1,計數器爲0的對象可以認爲該對象沒有被引用,可以被垃圾收集器回收。不過,目前主流的java虛擬機並沒有採用這種算法,主要原因是難以解決對象間相互引用的問題,即:Object A = B, Object B = A.

2.2 可達性分析法

可達性分析法就是通過從一系列稱之爲“GC Root”的點開始出發,向下面所有的對象開始搜索,如果“GC Root”無法到達一個對象,則這個對象就會被認爲是可以被回收的對象,就會被垃圾收集器回收。
可以作爲GC Root對象的主要有以下幾種:

  • 虛擬機棧中引用的對象
  • 方法區中類靜態屬性引用的對象
  • 方法區中常量引用的對象
  • 本地方法棧中JNI引用的對象
Java中的引用主要分爲強引用、軟引用、弱引用和虛引用。強引用的對象是不會被垃圾收集器回收的,軟引用對象會被放到二次回收的範圍內進行第二次回收,如果第二次回收後內存還是不夠,就會報內存溢出異常,弱引用和虛引用都會被垃圾收集器回收掉。
2.3 To be or not to be

當一個對象被認爲是可回收的時候,也不一定就會被回收,它們首先會被進行一次回收標記,然後判斷這個對象是否有必要執行finalize()方法,如果沒有必要執行或者是finalize()方法已經被jvm執行過了,那麼這個對象就幸運的活了下來。當然,就算是認爲有必要執行finalize()方法或者是jvm還沒有執行finalize()方法,這個對象還是有機會活下來的。當一個對象在執行finalize()方法的時,只要重將自己與GC Root連接到,即GC Root與該對象是可到達的,那麼這個對象就會被標記會“即將回收”,如果這個時候還沒有拯救出自己的話,那麼就會被垃圾收集器回收掉。另外需要注意一點的是,一個對象的finalize()方法只會執行一次,也就是說一個對象只要一次自救的機會。

3 垃圾回收算法思想

3.1 標記-清除算法

這種算法的思想就是將要回收的對象標記起來,然後統一的將這些對象清除掉,這是最基本的一種回收思想,不過這種算法有兩個不足點:一個是效率問題,因爲標記和清除兩個操作效率都不高,二是空間問題,因爲這種回收算法會導致大量的內存碎片,當需要一個很大內存空間來存儲對象時,有時會難處理。

.3.2 複製算法

這種算法的思想就是將內存分成兩部分,分配內存時只使用一部分,當垃圾回收時,將未回收的複製到另一部分內存中,這樣雖然不會產出內存碎片,但是會浪費一半的內存使用空間。

3.3 標記-整理算法

這種算法思想首先是標記要回收的對象,然後將要清理的對象向一邊移動,不需要回收的對象向另一邊移動,最後直接將需要清理的對象回收掉即可。

3.4 分代收集算法

這種算法是根據對象不同的存活週期劃分爲幾塊,一般分爲新生代和老年代,根據不同年代的特點採取不同的回收算法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章