1、對象的狀態
對于jvm中的垃圾收集器中,判斷對象是否可以被回收,哪些對象是否需要存活是有以下的方法的。
1.1、引用計數法
引用計數的實現很簡單,判斷方式也很高效,在大多數情況下都不錯,但是如果有a對象和b對象這種相互循環引用的問題是無法有效的處理的
1.2、可達性分析算法
算法思路:通過一系列稱為"GC ROOT"的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑被稱為引用鏈,當一個對象到GC ROOT沒有任何引用鏈相連接的時候,就證明這個對象是不可用的(如下圖示例中的object5,6,7)。
對于java而言,可作為GC ROOT的對象包括以下幾種:
- 虛擬機棧中引用的對象
- 方法區中類靜態屬性引用的對象
- 方法區中常量引用的對象
- 本地方法棧中
JNI
引用的對象
1.3、引用的類別
對于jdk2之后,java對于引用的概念進行擴充,分為以下的幾種:
- 強引用:類似于new出來的對象,只要強引用在,gc永遠不會回收
- 軟引用:是一種有用但是非必須的對象,系統在將要內存溢出異常之前,把這些對象列進回收范圍里進行第二次回收,如果回收后還沒有足夠的內存才會拋出內存溢出,jdk2以后用softReference來實現。
- 弱引用:也是非必須對象的一種,強度要低于軟引用,被弱引用關聯的對象只能生存到下一次gc發生之前。當gc時,無論內存是否充足,都會回收掉只被弱引用關聯的對象,用weakReference來實現。
- 虛引用:是最弱的一種引用關系。無法通過虛引用來獲得對象實例。虛引用的目的就是讓這個對象在gc被回收時收到一個系統通知,使用phantomReference來實現。
1.4、回收的機制
即使是可達性算法中,不可達的對象也不是一下就會被回收的。
一般他們是要經歷兩次標記過程:如果對象在進行可達性分析后發現沒有在GC ROOT的引用鏈上,他會被第一次標記并且進行一次篩選,篩選的條件是此對象是否需要執行finalizer()
方法。當對象沒有覆蓋finalizer()
方法,或者finalizer()
方法已經被jvm調用過,虛擬機講這兩種情況都視為”沒必要執行“。
注意:我們在日常的開發中最好不要去操作finalizer()
方法。
1.5、回收方法區
永久代的gc包含兩個部分的內容:廢棄常量和無用的類
沒有任何String對象引用常量池中的這個常量,也沒有其他地方引用了這個字面量,如果這時發生內存回收,而且必要的話,這個常量就會被系統清理出常量池。常量池中的其他類(接口)、方法、字段的符號引用也與此類似。
類需要同時滿足下面3個條件才能算是“無用的類”:
- 該類所有的實例都已經被回收,也就是Java堆中不存在該類的任何實例。
- 加載該類的ClassLoader已經被回收。
- 該類對應的java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
虛擬機可以對滿足上述3個條件的無用類進行回收,這里說的僅僅是“可以”,而并不是和對象一樣,不使用了就必然會回收。
在大量使用反射、動態代理、CGLib等ByteCode框架、動態生成JSP以及OSGi這類頻繁自定義ClassLoader的場景都需要虛擬機具備類卸載的功能,以保證永久代不會溢出。