首先,第一個問題:什麼是垃圾?
我們說當一個對象,沒有任何引用指向它的時候,那這個對象就是垃圾。
那麼JVM是怎麼知道這個對象已經沒有引用指向它了呢?也就是說JVM是怎麼找到垃圾的?
目前來說有兩種算法:reference count(引用計數)、Root Searching(根可達)
reference count(引用計數)
在對象的頭信息中記了一個數,每當有一個引用指向了這個對象,就加1。引用失效時,就減1。當計數值爲0的時候,說明這個對象變成了垃圾,需要被回收。
但是引用計數這種方式不能解決循環引用的問題
這裏有A、B、C三個對象循環引用,導致他們的引用計數無法爲0,所以不能被回收。
Root Searching(根可達)
從gcroot(根對象)不斷往下搜索,凡是能找到的都是有用的對象,找不到的就是垃圾。
哪些對象可作爲GCroot?JVM stacks,native method stack,run-time constant pool,static references in method area,Clazz
我們可以簡單理解爲main方法中可以訪問到的對象可作爲根對象。
垃圾回收算法
找到垃圾後,有3種算法可以用於清理垃圾:Mark-Sweep(標記清除)、copying(拷貝)、Mark-Compact(標記壓縮)
- Mark-Sweep(標記清除)
先標記出需要回收的對象,標記完成後再回收所有被標記的對象。這種算法的缺陷在於容易產生大量不連續的內存碎片。在分配較大對象的時候,無法找到足夠大的連續內存。同時標記和清除的效率也不高。
- copying(拷貝)
copying就是把內存一分爲二,每次只使用其中一塊,垃圾回收時,只需找到還存活的對象,將它們複製到另一塊內存中,再將已使用的這一整塊內存直接清理掉。這種算法效率比較高,但缺點是浪費空間。
- Mark-Compact(標記壓縮)
類似於硬盤整理碎片,將存活對象都壓縮到最前端,清理掉邊界外碎片。這種算法效率很低。
內存分區分代模型
整個內存分爲年輕代和老年代兩部分,年輕代中按照 8:1:1分成三部分:eden、survivor1,survivor2