首先需要了解JVM的內存管理,分爲兩大塊:
- 共享區內存:堆,方法區
- 獨佔區內存:虛擬機棧、本地方法棧、程序計數器
具體的可以參考我的另外一篇文章**JVM內存管理 。**其中程序計數器是負責程序的執行操作,比如一個程序執行到第10行代碼,另外一個程序開始執行。當另外一個程序完成後,就需要回到之前的第10行繼續執行。這就需要程序計數器進行分配調度了。
一、垃圾標記算法
1、引用計數法
引用計數法就是如果一個對象沒有被任何引用指向,則可視之爲垃圾。比如:
但是另外一種情況就會導致引用計數法失效:
2、根搜索法
這個算法的基本思想是通過一系列稱爲“GC Roots”的對象作爲起始點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈(即GC Roots到對象不可達)時,則證明此對象是不可用的。如下圖:
其中對象1、2、3、4、5和GC root都有引用關係,所以不會被回收,對象6、7、8沒有引用關係,將會被回收。
二、垃圾清除算法
1、標記清除法
算法分爲“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收掉所有被標記的對象。之所以說它是最基礎的收集算法,是因爲後續的收集算法都是基於這種思路並對其缺點進行改進而得到的。如下圖:
缺點:
- 需要遍歷所有的內存區域,進行標記,效率低。
- 容易產生內存碎片(被清除的對象分佈在不同的區域,然後申請一塊比較大的內存時,有些內存區域就得不到利用,內存碎片越多,容易產生OOM)
2、複製清除法
它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用過的內存空間一次清理掉。
缺點:
- 存活的對象較多時,複製效率低。(適用於新生代)
- 浪費內存,可使用的內存爲原來的一半。
優點:
- 能夠解決內存碎片
3、標記壓縮法
在標記可回收的對象後將所有存活的對象壓縮到內存的一端,使他們緊湊的排列在一起,然後對端邊界以外的內存進行回收。回收後,已用和未用的內存都各自一邊。如下圖:
優點:
- 解決了標記-清除算法效率低和容易產生大量內存碎片的問題,它被廣泛的應用於老年代中。
4、分代收集算法
在Java虛擬機中,各種對象的生命週期會有着較大的差別,大部分對象生命週期很短暫,少部分對象生命週期很長,有的甚至和應用程序以及Java虛擬機的運行週期一樣長。因此,應該對不同生命週期的對象採取不同的收集策略,根據生命週期長短將它們分別放到不同的區域,並在不同的區域採用不同的收集算法,這就是分代的概念。 如下圖:
(1)第一次,當我們創建對象時會在新生代的Eden區申請一塊內存。當gc觸發,發現Eden區已滿(比如Eden區內存大小爲10M,存活對象佔用5M),那麼會將存活對象存到from區域。
(2)第二次,繼續創建對象,在Eden區同樣申請內存,當gc到來時,發現Eden區又滿了,就會把Eden區和From區的存活對象,都放到To區。並把Eden區和From區清除。
(3)第三次,此時Eden和From區都是空的。繼續創建對象,Eden區又滿了,就會把Eden區和To區存活的對象,都放到From區。並把Eden區和To區清空。
(4)也就是說Eden區是負責爲對象開闢空間的。存活的對象會在From,To區域之間來回存放。當某個對象這樣來來回回,達到一個閾值的時候,虛擬機就認定這個對象時長期存活的,就會把它放到老年代中。
(5)如果創建的對象需要的內存超過Eden大小,也會將這個對象放到老年代中。老年代內存不夠時,也會進行gc。
三、對象引用類型
1、強引用
無論內存夠不夠,對象都不會被回收,實在不夠就拋OOM異常。
2、軟引用
只有內存不夠的時候纔會被回收。回收時間不定
3、弱引用
內存夠不夠都會被回收。回收時間不定
4、虛引用
如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收,虛引用主要用來跟蹤對象被垃圾回收的活動。回收時間不定。