之前看過不少次關於垃圾回收相關的文章,都只是看過就忘記了,沒有好好的整理一下,發現寫文章能夠強化本身的記憶。html
java與C,c++有很大的不一樣就是java語言開發者不須要關注內存信息,不會顯式的直接操做內存,而是經過jvm虛擬機來實現。java
java虛擬機運行的時候內存分配圖以下圖:c++
jvm虛擬機棧:一個是線程獨有的,每次啓動一個線程,就建立一個jvm虛擬機棧,線程退出的時候就銷燬。這裏面主要保存線程本地變量名和局部變量值。算法
本地方法棧: 調用本地jni方法的時候而建立的。這裏分配的jvm以外的內存空間。方法調用結束以後銷燬。bootstrap
pc寄存器 : 這個保存線程當前執行的字節碼指令jvm
堆:主要保存建立的對象。post
方法區:保存class相關的信息。主要是class的一個內存結構信息測試
常量池:方法區的一部分,主要保存class內存結構中常量值 例如String值,public static final 類型的值this
咱們這裏說的垃圾回收,主要是java虛擬機對堆內存區域的回收。url
目前有兩種算法
每一個對象上都有一個引用計數,對象每被引用一次,引用計數器就+1,對象引用被釋放,引用計數器-1,直到對象的引用計數爲0,對象就標識能夠回收
這個能夠用數據算法中的圖形表示,對象A-對象B-對象C 都有引用,因此不會被回收,對象B因爲沒有被引用,沒有路徑能夠達到對象B,對象B的引用計數就就是0,對象B就會被回收。
可是這個算法有明顯的缺陷,對於循環引用的狀況下,循環引用的對象就不會被回收。例以下圖:對象A,對象B 循環引用,沒有其餘的對象引用A和B,則A和B 都不會被回收。
這種算法目前定義了幾個root,也就是這幾個對象是jvm虛擬機不會被回收的對象,因此這些對象引用的對象都是在使用中的對象,這些對象未使用的對象就是即將要被回收的對象。簡單就是說:若是對象可以達到root,就不會被回收,若是對象不可以達到root,就會被回收。
以下圖:對象D訪問不到根對象,因此就會被回收
如下對象會被認爲是root對象:
- 被啓動類(bootstrap加載器)加載的類和建立的對象
- jvm運行時方法區類靜態變量(static)引用的對象
- jvm運行時方法去常量池引用的對象
- jvm當前運行線程中的虛擬機棧變量表引用的對象
- 本地方法棧中(jni)引用的對象
因爲這種算法即便存在互相引用的對象,但若是這兩個對象沒法訪問到根對象,仍是會被回收。以下圖:對象C和對象D互相引用,可是因爲沒法訪問根,因此會被回收。
jvm在肯定是否回收的對象的時候採用的是root搜索算法來實現。
在root搜索算法的裏面,咱們說的引用這裏都指定的是強引用關係。所謂強引用關係,就是經過用new 方式建立的對象,而且顯示關聯的對象
1
|
Object obj =
new
Object();
|
以上就是表明的是強引用關係,變量obj 強引用了 Object的一個對象。
java裏面有四種應用關係,從強到弱分別爲:
Strong Reference(強引用) –>Weak Reference (弱引用) -> Soft Reference(軟引用) – > Phantom Reference(引用)
Strong Reference : 只有在引用對象root不可達的狀況下才會標識爲可回收,垃圾回收纔可能進行回收
Soft Reference : 不管其引用的對象是否root可達,在響應內存須要時,由垃圾回收判斷是否須要回收。
Weak Reference :用來描述非必需對象。即便在root算法中 其引用的對象root可達到,只能生存到下一次垃圾回收以前。
Phantom Reference :沒法經過虛引用得到一個對象的實例,設置虛引用的目的就是能在這個對象被收集器回收時收到一個系統通知。
下面能夠看一個測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public
class
ReferenceTest {
public
static
final
Map<Integer, Reference> map =
new
HashMap<Integer, Reference>();
public
static
void
main(String[] args) {
for
(
int
i =
0
; i <
1000
; i++) {
map.put(i,
new
WeakReference(
new
ReferenceObject(i)));
}
int
i =
0
;
for
(Reference r : map.values()) {
if
(r.get() ==
null
) {
i++;
}
}
System.out.println(
"被回收的對象數:"
+ i);
}
static
class
ReferenceObject {
private
int
i;
private
byte
[] b;
public
ReferenceObject(
int
i) {
this
.i = i;
b =
new
byte
[
1024
*
10
];
}
}
}
|
這裏建立大約1000個 10K的 Weak Reference 對象,最後打印的結果是:被回收的對象數:767,這裏ReferenceObject若是設置爲1K的話,最後的打印結果是0
這個例子並不嚴謹,可是卻說明了被Weak Reference的對象在必定的時候會被jvm回收,可是強引用就不會出現這種狀態。