1.Java中finalize()的做用一主要是清理那些對象(並不是使用new)得到了一塊「特殊」的內存區域。程序員能夠用finalize()來操做。 程序員都瞭解初始化的重要性,但經常會忘記一樣也重要的清理工做。畢竟,誰須要清理一個int呢?但在使用程序庫時,把一個對象用完後就「棄之不顧」的作法並不是老是安全的。固然,Java有垃圾回收器負責回收無用對象佔據的內存資源。但也有特殊狀況:假定你的對象(並不是使用new)得到了一塊「特殊」的內存區域,因爲垃圾回收器只知道釋放那些經由new分配的內存,因此它不知道該如何釋放該對象的這塊「特殊」內存區域,爲了應對這種狀況,java容許在類中定義一個名爲finalize()的方法。它的工做原理「假定」是這樣的:一旦垃圾回收器準備好釋放對象佔用的存儲孔家,將首先調用其finalize()的方法。而且在下一次垃圾回收動做發生時,纔會真正回收對象佔用的內存。因此要是你打算用finalize(),就能在垃圾回收時刻作一些重要的清理工做。注意這裏的finalize()並非C++裏的析構.在C++中,對象必定會被銷燬,而在Java裏的對象卻並不是老是被垃圾回收(1.對象可能不被垃圾回收;2.垃圾回收並並不等於「析構」)。 java
2.垃圾回收只與內存有關。也就是說,使用垃圾回收器的惟一緣由是爲了回收程序再也不使用的內存。因此對於與垃圾回收有關的任何行爲來講(尤爲是finalize()方法),它們也必須同內存及其回收有關。但這是否意味着要是對象中含有其餘對象,finalize()就應該明確釋放那些對象呢?不,不管對象是如何建立的,垃圾回收器都會負責釋放對象佔據的全部內存。這就將對finalize()的需求限制到一種特殊狀況,即經過某種建立對象方式之外的方式爲對象分配了存儲空間。不過,java中一切皆爲對象,那這種特殊狀況是怎麼回事呢?因爲在分配內存時可能採用了相似C語言中的作法,而非java中的一般作法。這種狀況主要發生在使用「本地方法」的狀況下,本地方法是一種在Java中調用非Java代碼的方式。在非java代碼中,也許會調用C的malloc()函數系列來分配存儲空間,並且除非了free()函數 程序員
3.垃圾回收如何工做 編程
「引用記數(reference counting)」是一種簡單但速度很慢的垃圾回收技術。每一個對象都含有一個引用記數器,當有引用鏈接至對象時,引用計數加1。當引用離開做用域或被置爲null時,引用計數減1。雖然管理引用記數的開銷不大,但須要在整個程序生命週期中持續地開銷。垃圾回收器會在含有所有對象的列表上遍歷,當發現某個對象的引用計數爲0時,就釋放其佔用的空間。這種方法有個缺陷,若是對象之間存在循環引用,可能會出現「對象應該被回收,但引用計數卻不爲零」的狀況。對垃圾回收器而言,定位這樣存在交互引用的對象組所需的工做量極大。引用記數經常使用來講明垃圾收集的工做方式,彷佛從未被應用於任何一種Java虛擬機實現中。 安全
在一些更快的模式中,垃圾回收器並不是基於引用記數技術。它們依據的思想是:對任何「活」的對象,必定能最終追溯到其存活在堆棧或靜態存儲區之中的引用。這個引用鏈條可能會穿過數個對象層次。由此,若是你從堆棧和靜態存儲區開始,遍歷全部的引用,就能找到全部「活」的對象。對於發現的每一個引用,你必須追蹤它所引用的對象,而後是此對象包含的全部引用,如此反覆進行,直到「根源於堆棧和靜態存儲區的引用」所造成的網絡所有被訪問爲止。你所訪問過的對象必須都是「活」的。注意,這就解決了「存在交互引用的總體對象」的問題,這些對象根本不會被發現,所以也就被自動回收了。 網絡
在這種方式下,Java虛擬機將採用一種「自適應」的垃圾回收技術。至於如何處理找到的存活對象,取決於不一樣的Java虛擬機實現。有一種做法名爲「中止——複製」(stop-and-copy)。這意味着,先暫停程序的運行,(因此它不屬於後臺回收模式),而後將全部存活的對象從當前堆複製到另外一個堆,沒有被複制的所有都是垃圾。當對象被複制到新堆時,它們是一個挨着一個的,因此新堆保持緊湊排列,而後就能夠按前述方法簡單、直接地分配新空間了。 函數
「標記——清掃」所依據的思路一樣是從堆棧和靜態存儲區出發,遍歷全部的引用,進而找出全部存活的對象。每當它找到一個存活對象,就會給對象設一個標記,這個過程當中不會回收任何對象。只有所有標記工做完成的時候,清除動做纔會開始。在清處過程當中,沒有標記的對象將被釋放,不會發生任何複製動做。因此剩下的堆空間是不連續的,垃圾回收器要是但願獲得連續空間的話,就得從新整理剩下的對象。 優化
「中止——複製」的意思是這種垃圾回收方式不是在後臺進行的;相反,垃圾回收動做發生的同時,程序將會被暫停。在Sun 公司的文檔中你會發現,許多參考文獻將垃圾回收視爲低優先級的後臺進程,但事實上垃圾回收器並不是以這種方式實現——至少Sun公司早期版本的Java虛擬機中並不是如此。當可用內存數量較低時,Sun版中的垃圾回收器纔會被激活,一樣,「標記——清掃」工做也必須在程序暫停的狀況下才能進行。 翻譯
如前文所述,這裏討論的Java虛擬機,內存分配單位是較大的「塊」。若是對象較大,它會佔用單獨的塊。嚴格來講,「中止——複製」要求你在釋放舊有對象以前,必須先把全部存活對象從舊堆複製到新堆,這將致使大量內存複製行爲。有了塊以後,垃圾回收器在回收的時候就能夠往廢棄的塊裏拷貝對象了。每一個塊都用相應的「代數(generation count)」記錄它是否還存活。一般,若是塊在某處被引用,其代數會增長;垃圾回收器將對上次回收動做以後新分配的塊進行整理。這對處理大量短命的臨時對象頗有幫助。垃圾回收器會按期進行完整的清除動做——大型對象仍然不會被複制(只是其代數會增長),內含小型對象的那些塊則被複制並整理。Java虛擬機會進行監視,若是全部對象都很穩定,垃圾回收器的效率下降的話,就切換到「標記——清掃」方式;一樣, Java虛擬機會注意「標記——清掃」的效果,要是堆空間出現不少碎片,就會切換回「中止——複製」方式。這就是「自適應」技術。你能夠給它個羅嗦的稱呼:「自適應的、分代的、中止——複製、標記——清掃」式垃圾回收器。 對象
Java虛擬機中有許多附加技術用以提高速度。尤爲是與加載器操做有關的,被稱爲「即時」(Just-In-Time,JIT)編譯的技術。這種技術能夠把程序所有或部分翻譯成本地機器碼(這原本是Java虛擬機的工做),程序運行速度所以得以提高。當須要裝載某個類(一般是在你爲該類建立第一個對象)時,編譯器會先找到其 .class 文件,而後將該類的字節碼裝入內存。此時,有兩種方案可供選擇。一種是就讓即時編譯器編譯全部代碼。但這種作法有兩個缺陷:這種加載動做散落在整個程序生命週期內,累加起來要花更多時間;而且會增長可執行代碼的長度(字節碼要比即時編譯器展開後的本地機器碼小不少),這將致使頁面調度,從而下降程序速度。另外一種作法稱爲「惰性編譯(lazy uation)」,意思是即時編譯器只在必要的時候才編譯代碼。這樣,從不會被執行的代碼也許就壓根不會被JIT所編譯。新版JDK中的Java HotSpot技術就採用了相似方法,代碼每次被執行的時候都會作一些優化,因此執行的次數越多,它的速度就越快。 生命週期
參考資料:《Java編程思想第四版》,《深刻Java虛擬機》