引言 Java的堆是一個運行時數據區
類的實例(對象)從中分配空間
Java虛擬機(JVM)的堆中儲存著正在運行的應用程序所建立的所有對象
這些對象通過new
newarray
anewarray和multianewarray等指令建立
但是它們不需要程序代碼來顯式地釋放
一般來說
堆的是由垃圾回收 來負責的
盡管JVM規范並不要求特殊的垃圾回收技術
甚至根本就不需要垃圾回收
但是由於內存的有限性
JVM在實現的時候都有一個由垃圾回收所管理的堆
垃圾回收是一種動態存儲管理技術
它自動地釋放不再被程序引用的對象
按照特定的垃圾收集算法來實現資源自動回收的功能
垃圾收集的意義 在C++中
對象所占的內存在程序結束運行之前一直被占用
在明確釋放之前不能分配給其它對象
而在Java中
當沒有對象引用指向原先分配給某個對象的內存時
該內存便成為垃圾
JVM的一個系統級線程會自動釋放該內存塊
垃圾收集意味著程序不再需要的對象是
無用信息
這些信息將被丟棄
當一個對象不再被引用的時候
內存回收它占領的空間
以便空間被後來的新對象使用
事實上
除了釋放沒用的對象
垃圾收集也可以清除內存記錄碎片
由於創建對象和垃圾收集器釋放丟棄對象所占的內存空間
內存會出現碎片
碎片是分配給對象的內存塊之間的空閒內存洞
碎片整理將所占用的堆內存移到堆的一端
JVM將整理出的內存分配給新的對象
垃圾收集能自動釋放內存空間
減輕編程的負擔
這使Java 虛擬機具有一些優點
首先
它能使編程效率提高
在沒有垃圾收集機制的時候
可能要花許多時間來解決一個難懂的存儲器問題
在用Java語言編程的時候
靠垃圾收集機制可大大縮短時間
其次是它保護程序的完整性
垃圾收集是Java語言安全性策略的一個重要部份
垃圾收集的一個潛在的缺點是它的開銷影響程序性能
Java虛擬機必須追蹤運行程序中有用的對象
而且最終釋放沒用的對象
這一個過程需要花費處理器的時間
其次垃圾收集算法的不完備性
早先采用的某些垃圾收集算法就不能保證
%收集到所有的廢棄內存
當然隨著垃圾收集算法的不斷改進以及軟硬件運行效率的不斷提升
這些問題都可以迎刃而解
垃圾收集的算法分析 Java語言規范沒有明確地說明JVM使用哪種垃圾回收算法
但是任何一種垃圾收集算法一般要做
件基本的事情
(
)發現無用信息對象
(
)回收被無用對象占用的內存空間
使該空間可被程序再次使用
大多數垃圾回收算法使用了根集(root set)這個概念
所謂根集就量正在執行的Java程序可以訪問的引用變量的集合(包括局部變量
參數
類變量)
程序可以使用引用變量訪問對象的屬性和調用對象的方法
垃圾收集首選需要確定從根開始哪些是可達的和哪些是不可達的
從根集可達的對象都是活動對象
它們不能作為垃圾被回收
這也包括從根集間接可達的對象
而根集通過任意路徑不可達的對象符合垃圾收集的條件
應該被回收
下面介紹幾個常用的算法
引用計數法(Reference Counting Collector)
引用計數法是唯一沒有使用根集的垃圾回收得法
該算法使用引用計數器來區分存活對象和不再使用的對象
一般來說
堆中的每個對象對應一個引用計數器
當每一次創建一個對象並賦給一個變量時
引用計數器置為
當對象被賦給任意變量時
引用計數器每次加
當對象出了作用域後(該對象丟棄不再使用)
引用計數器減
一旦引用計數器為
對象就滿足了垃圾收集的條件
基於引用計數器的垃圾收集器運行較快
不會長時間中斷程序執行
適宜地必須 實時運行的程序
但引用計數器增加了程序執行的開銷
因為每次對象賦給新的變量
計數器加
而每次現有對象出了作用域生
計數器減
tracing算法(Tracing Collector)
tracing算法是為了解決引用計數法的問題而提出
它使用了根集的概念
基於tracing算法的垃圾收集器從根集開始掃描
識別出哪些對象可達
哪些對象不可達
並用某種方式標記可達對象
例如對每個可達對象設置一個或多個位
在掃描識別過程中
基於tracing算法的垃圾收集也稱為標記和清除(mark
and
sweep)垃圾收集器
compacting算法(Compacting Collector)
為了解決堆碎片問題
基於tracing的垃圾回收吸收了Compacting算法的思想
在清除的過程中
算法將所有的對象移到堆的一端
堆的另一端就變成了一個相鄰的空閒內存區
收集器會對它移動的所有對象的所有引用進行更新
使得這些引用 在新的位置能識別原來 的對象
在基於Compacting算法的收集器的實現中
一般增加句柄和句柄表
coping算法(Coping Collector)
該算法的提出是為了克服句柄的開銷和解決堆碎片的垃圾回收
它開始時把堆分成 一個對象 面和多個空閒面
程序從對象面為對象分配空間
當對象滿了
基於coping算法的垃圾 收集就從根集中掃描活動對象
並將每個 活動對象復制到空閒面(使得活動對象所占的內存之間沒有空閒洞)
這樣空閒面變成了對象面
原來的對象面變成了空閒面
程序會在新的對象面中分配內存
一種典型的基於coping算法的垃圾回收是stop
and
copy算法
它將堆分成對象面和空閒區域面
在對象面與空閒區域面的切換過程中
程序暫停執行
generation算法(Generational Collector)
stop
and
copy垃圾收集器的一個缺陷是收集器必須復制所有的活動對象
這增加了程序等待時間
這是coping算法低效的原因
在程序設計中有這樣的規律
多數對象存在的時間比較短
少數的存在時間比較長
因此
generation算法將堆分成兩個或多個
每個子堆作為對象的一代(generation)
由於多數對象存在的時間比較短
隨著程序丟棄不使用的對象
垃圾收集器將從最年輕的子堆中收集這些對象
在分代式的垃圾收集器運行後
上次運行存活下來的對象移到下一最高代的子堆中
由於老一代的子堆不會經常被回收
因而節省了時間
adaptive算法(Adaptive Collector)
在特定的情況下
一些垃圾收集算法會優於其它算法
基於Adaptive算法的垃圾收集器就是監控當前堆的使用情況
並將選擇適當算法的垃圾收集器
透視Java垃圾回收 命令行參數透視垃圾收集器的運行
使用System
gc()可以不管JVM使用的是哪一種垃圾回收的算法
都可以請求Java的垃圾回收
在命令行中有一個參數
verbosegc可以查看Java使用的堆內存的情況
它的格式如下
java
verbosegc classfile
可以看個例子
class TestGC
{
public static void main(String[] args)
{
new TestGC();
System
gc();
System
runFinalization();
}
}
在這個例子中
一個新的對象被創建
由於它沒有使用
所以該對象迅速地變為可達
程序編譯後
執行命令
java
verbosegc TestGC 後結果為
[Full GC
K
>
K(
K)
secs]
機器的環境為
Windows
+ JDK
箭頭前後的數據
K和
K分別表示垃圾收集GC前後所有存活對象使用的內存容量
說明有
K
K=
K的對象容量被回收
括號內的數據
K為堆內存的總容量
收集所需要的時間是
秒(這個時間在每次執行的時候會有所不同)
finalize方法透視垃圾收集器的運行
在JVM垃圾收集器收集一個對象之前
一般要求程序調用適當的方法釋放資源
但在沒有明確釋放資源的情況下
Java提供了缺省機制來終止化該對象心釋放資源
這個方法就是finalize()
它的原型為
protected void finalize() throws Throwable
在finalize()方法返回之後
對象消失
垃圾收集開始執行
原型中的throws Throwable表示它可以拋出任何類型的異常
之所以要使用finalize()
是由於有時需要采取與Java的普通方法不同的一種方法
通過分配內存來做一些具有C風格的事情
這主要可以通過
固有方法
來進行
它是從Java裡調用非Java方法的一種方式
C和C++是目前唯一獲得固有方法支持的語言
但由於它們能調用通過其他語言編寫的子程序
所以能夠有效地調用任何東西
在非Java代碼內部
也許能調用C的malloc()系列函數
用它分配存儲空間
而且除非調用了free()
否則存儲空間不會得到釋放
從而造成內存
漏洞
的出現
當然
free()是一個C和C++函數
所以我們需要在finalize()內部的一個固有方法中調用它
也就是說我們不能過多地使用finalize()
它並不是進行普通清除工作的理想場所
在普通的清除工作中
為清除一個對象
那個對象的用戶必須在希望進行清除的地點調用一個清除方法
這與C++
破壞器
的概念稍有抵觸
在C++中
所有對象都會破壞(清除)
或者換句話說
所有對象都
應該
破壞
若將C++對象創建成一個本地對象
比如在堆棧中創建(在Java中是不可能的)
那麼清除或破壞工作就會在
結束花括號
所代表的
創建這個對象的作用域的末尾進行
若對象是用new創建的(類似於Java)
那麼當程序員調用C++的delete命令時(Java沒有這個命令)
就會調用相應的破壞器
若程序員忘記了
那麼永遠不會調用破壞器
我們最終得到的將是一個內存
漏洞
另外還包括對象的其他部分永遠不會得到清除
相反
Java不允許我們創建本地(局部)對象
無論如何都要使用new
但在Java中
沒有
delete
命令來釋放對象
因為垃圾收集器會幫助我們自動釋放存儲空間
所以如果站在比較簡化的立場
我們可以說正是由於存在垃圾收集機制
所以Java
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19351.html