一、垃圾回收的目標是Java堆
1、程序計數器與虛擬機棧
程序計數器與虛擬機棧不必考慮內存的分配與回收的問題。
- JVM中的程序計數器和虛擬機棧都是隨着線程的建立而建立,隨着線程的消亡而消亡的。
- 另外其中存儲的一些基本類型變量以及引用對象和返回結果使用內存也基本是固定的。
2、Java堆
Java堆中的內存的分配與回收是動態的,垃圾收集器所主要關注的就是這部分內存。
- 一個接口中的多個實現類需要的內存可能不一樣。
- 一個方法中的多個分支需要的內存也可能不一樣。
- 只有在程序運行期間時,才能知道會創建哪些對象。
3、Java方法區
Java中的方法區的主要回收兩部分內容 = 廢棄常量 + 無用的類
- 例如常量池中的”abc“字面量,如果沒有引用指向了它,那麼這個常量就會被系統清理出常量池,常量池中的其他類(接口)、方法、字段的符號引用也類似。
- 判定一個類是否是無用的類的條件比較苛刻,需要滿足以下三個條件:
- 該類的所有實例都已經被回收,也就是Java堆中不存在該類的任何實例。
- 加載該類的ClassLoader已經被回收。
- 該類對應的java.lang.class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
在大量使用反射、動態代理、CGLib等ByteCode框架、動態生成JSP以及OSGI這類頻繁自定義ClassLoader的場景都需要虛擬機具備類卸載的功能,以保證永久代不會溢出。
二、回收Java堆中哪些對象呢?
回收對象,肯定是回收那些無用的對象,否則會影響程序的運行,那麼如何判斷對象是否是有用的存活的呢?
1、引用計數算法
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1,當引用失效時,計數器值就減1,任何時刻計數器爲0的對象,就是不可能再被使用的。
但是主流的Java虛擬機裏面沒有選用引用計數算法來管理內存,最主要的原因是它很難解決對象之間的相互循環引用的問題。
2、可達性分析算法
主流的實現中都是使用的可達性分析算法來判定對象是否存活的。
- 通過一系列的稱爲GC Roots的對象作爲起始點。
- 從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈。
- 當一個對象到GC Roots沒有任何引用鏈相連,也就是從GC Roots到這個對象不可達時,證明此對象是不可用的。
在Java中,可作爲GC Roots的對象包括下面幾種:
- 虛擬機棧(棧幀中的本地變量表)中引用的對象。
- 方法區中類靜態屬性引用的對象。
- 方法區中常量引用的對象。
- 本地方法棧中JNI(一般所說的Native方法)引用的對象。