全文轉載: http://topic.csdn.net/u/20091211/12/c79a9293-fdd5-4f81-b18f-a4a29c8d9be2.html
?
作者: yang677888 (from CSDN)
?
?
?
1、
?
使用 System.gc() 可以不管JVM使用的是哪一種垃圾回收的算法,都可以請求 Java的垃圾回收。
?
在命令行中有一個參數-verbosegc可以查看Java使用的堆內存的情況,它的格式:java -verbosegc classfile
- class ?TestGC?{??????
- ????? ? public ? static ? void ?main(String[]?args)?{??????
- ????? ?? new ?TestGC();??????
- ????? ??System.gc();??????
- ????? ??System.runFinalization();??????
- ???????}??????
- ????}???????
- ???
- ??
- ???? class ?TestGC?{????
- ???? ? public ? static ? void ?main(String[]?args)?{????
- ???? new ?TestGC();????
- ???? ?System.gc();????
- ???? System.runFinalization();????
- ???? }????
- }??????
?在這個例子 中,一個新的對象被創建,由于它沒有使用,所以該對象迅速地變為可達,程序編譯后,執行命令: java -verbosegc TestGC 后結果為:
[Full GC 168K->97K(1984K), 0.0253873 secs]
機器的環 境為,Windows 2000 + JDK1.3.1,箭頭前后的數據168K和97K分別表示垃圾收集GC前后所有存活對象使用的內存容量,說明有168K-97K=71K的對象容量被回 收,括號內的數據1984K為堆內存的總容量,收集所需要的時間是0.0253873秒(這個時間在每次執行的時候會有所不同)。
?
?
?2、 finalize方法 透視垃圾收集器的運行
?
在JVM垃圾收集器收集一個對象之前 ,一般要求程序調用適當的方法釋放資源,但在沒有明確釋放資源的情況下,Java提供了缺省機制來終止化該對象心釋放資源,這個方法就是 finalize()。它的原型為:
Java代碼
?
1.?? ?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沒有破壞器。然而, 隨著以后學習的深入,就會知道垃圾收集器的存在并不能完全消除對破壞器的需要,或者說不能消除對破壞器代表的那種機制的需要(而且絕對不能直接調用 finalize(),所以應盡量避免用它)。若希望執行除釋放存儲空間之外的其他某種形式的清除工作,仍然必須調用Java中的一個方法。它等價于 C++的破壞器,只是沒后者方便。
下面這個例子向大家展示了垃圾收集所經歷的過程,并對前面的陳述進行了總結。
- package ?hr.test;????
- ????????
- import ?java.util.Calendar;????
- ????????
- ????? class ?Chair?{????
- ???????? static ? int ?created?=? 0 ;? //對象創建計數?? ??
- ???????? static ? int ?finalized?=? 0 ;? //對象回收計數?? ??
- ???????? //構造器?? ??
- ????????Chair(){????
- ????????????created++;????
- ????????????System.err.println( "created?" +created+ "【" +Calendar.getInstance().get(Calendar.MINUTE)+ "m?" +Calendar.getInstance().get(Calendar.SECOND)+ "s】" );????
- ????????}????
- ???????? //垃圾回收析構函數?? ??
- ???????? protected ? void ?finalize()?{????
- ????????????finalized++;????
- ????????????System.out.println( "finalize?" +finalized+ "【" +Calendar.getInstance().get(Calendar.MINUTE)+ "m?" +Calendar.getInstance().get(Calendar.SECOND)+ "s】" );????
- ????????}????
- ????}????
- ????????
- ???? public ? class ?Garbage?{????
- ???????? public ? static ? void ?main(String[]?args)?{????
- ???????????? while (Chair.created!= 10000 )?{????
- ???????????????? new ?Chair(); //創建對象?? ??
- ????????????}????
- ???????????? while ( true ){????
- ???????????????? if (Chair.created== 10000 ){????
- ???????????????????? break ;????
- ????????????????}????
- ????????????}????
- ????????}????
- ????}??????
? 上面這個程序創建了10000個Chair對象。但是在運行結果中發現,在累計循環創建到x個對象的時候,我們發現主線程被掛起,JVM開始運行垃圾 回收程序。證據就是屏幕上開始打印出"final ..."。而且主線程被掛起的時間是不定的,有的時候我們甚至能在輸出中看到半條"create.. "就開始打印"final ..."了。
經過上述的說明,可以發現垃圾回收有以下的幾個特點: ?
(1) 垃圾收集發生的不可預 知性:
?
由于實現了不同的垃圾收集算法和采用了不同的收集機制,所以它有可能是定時發生,有可能是當出現系統空閑CPU資源時發生,也有可能是和原始的垃圾 收集一樣,等到內存消耗出現極限時發生,這與垃圾收集器的選擇和具體的設置都有關系。 ?
(2)垃圾收集的精確性:
?
主要包括2 個方面:(a)垃圾收集器能夠精確標記活著的對象; (b)垃圾收集器能夠精確地定位對象之間的引用關系。 前者是完全地回收所有廢棄對象的前提,否則就可能 造成內存泄漏。而后者則是實現歸并和復制等算法的必要條件。所有不可達對象都能夠可靠地得到回收,所有對象都能夠重新分配,允許對象的復制和對象內存的縮 并,這樣就有效地防止內存的支離破碎。 ?
(3)現在有許多種不同的垃圾收集器,每種有其算法且其表現各異,既有當垃圾收集開始時就 停止應用程序的運行,又有當垃圾收集開始時也允許應用程序的線程運行,還有在同一時間垃圾收集多線程運行。 ?
?
(4)垃圾收集的實現 和具體的JVM 以及JVM的內存模型有非常緊密的關系。
?
不同的JVM 可能采用不同的垃圾收集,而JVM 的內存模型決定著該JVM可以采用哪些類型垃圾收集?,F在,HotSpot 系列JVM中的內存系統都采用先進的面向對象的框架設計,這使得該系列JVM都可以采用最先進的垃圾收集。 ?
(5)隨著技術的發 展,現代垃圾收集技術提供許多可選的垃圾收集器,而且在配置每種收集器的時候又可以設置不同的參數,這就使得根據不同的應用環境獲得最優的應用性能成為可能。 ?
?
針對以上特點,我們在使用的時候要注意: ?
?。?)不要試圖去假定垃圾收集發生的時間,這一切都是未知 的。
?
比如,方法中的一個臨時對象在方法調用完畢后就變成了無用對象,這個時候它的內存就可以被釋放。 ?
?。?)Java中提供了一些 和垃圾收集打交道的類,而且提供了一種強行執行垃圾收集的方法--調用System.gc(),但這同樣是個不確定的方法。 Java 中并不保證每次調用該方法就一定能夠啟動垃圾收集,它只不過會向JVM發出這樣一個申請,到底是否真正執行垃圾收集,一切都是個未知數。 ?
?
(3)挑選適合自己的垃圾收集器。 一般來說,如果系統沒有特殊和苛刻的性能要求,可以采用JVM的缺省選項。
?
否則可以考慮使用有針對性的垃圾收集器,比 如增量收集器就比較適合實時性要求較高的系統之中。系統具有較高的配置,有比較多的閑置資源,可以考慮使用并行標記/清除收集器。 ?
(4)關鍵的也是難把握的問題是內存泄漏。
?
良好的編程習慣和嚴謹的編程態度永遠是最重要的,不要讓自己的一個小錯誤導致內存出現大漏洞。 ?
(5)盡早釋放無用對象的引用。
?
大多數程序員在使用臨時變量的時候,都是讓引用變量在退出活動域(scope)后,自動設置為null,暗示垃圾收集器 來收集該對象,還必須注意該引用的對象是否被監聽,如果有,則要去掉監聽器,然后再賦空值。 ?
結束語 ?
一般 來說,Java開發人員可以不重視JVM中堆內存的分配和垃圾處理收集,但是,充分理解Java的這一特性可以讓我們更有效地利用資源。同時要注意 finalize()方法是Java的缺省機制,有時為確保對象資源的明確釋放,可以編寫自己的finalize方法。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
